import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import BookingSection from './BookingSection.svelte';
import { homepageContent } from '$lib/content/homepage';

async function fillOwnerStep() {
  await fireEvent.input(screen.getByLabelText(/Full Name/i), {
    target: { value: 'Alex Walker' }
  });
  await fireEvent.input(screen.getByLabelText(/^Email/i), {
    target: { value: 'alex@example.com' }
  });
  await fireEvent.input(screen.getByLabelText(/Contact #/i), {
    target: { value: '021 123 4567' }
  });
}

async function fillDogStep() {
  await fireEvent.click(screen.getByLabelText('Pack Walks'));
  await fireEvent.click(screen.getByLabelText('Other Services'));
  await fireEvent.input(screen.getByLabelText(/Dog's Name/i), {
    target: { value: 'Maya' }
  });
  await fireEvent.input(screen.getByLabelText(/Location/i), {
    target: { value: 'Kingsland' }
  });
  await fireEvent.input(screen.getByLabelText(/Pack Walks fit/i), {
    target: { value: 'Loves small group walks.' }
  });
}

async function moveToOwnerStep(container: HTMLElement) {
  await fillDogStep();
  await fireEvent.click(container.querySelector('.booking-next-button')!);
}

describe('BookingSection', () => {
  beforeEach(() => {
    window.sessionStorage.clear();
    Object.defineProperty(document, 'referrer', {
      configurable: true,
      value: 'https://www.google.com/'
    });
  });

  it('validates the dog details step before progressing', async () => {
    const { container } = render(BookingSection, {
      booking: homepageContent.booking
    });

    expect(screen.queryByLabelText(/General enquiry/i)).not.toBeInTheDocument();

    await fireEvent.click(container.querySelector('.booking-next-button')!);

    expect(screen.getByText("Please enter your dog's name")).toBeInTheDocument();
    expect(screen.getByText('Please enter your location')).toBeInTheDocument();
  });

  it('validates the owner details step before submitting', async () => {
    const { container } = render(BookingSection, {
      booking: homepageContent.booking
    });

    await moveToOwnerStep(container);
    await fireEvent.click(container.querySelector('.booking-submit-button')!);

    expect(screen.getByText('Please enter your full name')).toBeInTheDocument();
    expect(screen.getByText('Please enter your email address')).toBeInTheDocument();
    expect(screen.getByText('Please enter your contact number')).toBeInTheDocument();
  });

  it('submits the completed booking flow and shows the success modal', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: vi.fn().mockResolvedValue({})
    });
    vi.stubGlobal('fetch', fetchMock);

    const { container } = render(BookingSection, {
      booking: homepageContent.booking
    });

    await moveToOwnerStep(container);
    await fillOwnerStep();

    await fireEvent.click(container.querySelector('.booking-submit-button')!);

    await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
    expect(fetchMock).toHaveBeenCalledWith(
      '/api/submit',
      expect.objectContaining({
        method: 'POST',
        headers: { 'Content-Type': 'application/json' }
      })
    );

    const payload = JSON.parse(fetchMock.mock.calls[0][1].body as string);
    expect(payload).toMatchObject({
      enquiryType: 'booking',
      fullName: 'Alex Walker',
      email: 'alex@example.com',
      phone: '021 123 4567',
      petName: 'Maya',
      location: 'Kingsland',
      message: 'Loves small group walks.',
      services: ['Pack Walks', 'Other Services'],
      website: '',
      referrer: 'https://www.google.com/',
      stepChanges: 1,
      journey: [window.location.pathname]
    });
    expect(payload.formStartedAt).toEqual(expect.any(Number));
    expect(payload.visitStartedAt).toEqual(expect.any(Number));
    expect(payload.pageEnteredAt).toEqual(expect.any(Number));
    expect(payload.firstInteractionAt).toEqual(expect.any(Number));
    expect(payload.sendClickedAt).toEqual(expect.any(Number));

    expect(screen.getByRole('dialog', { name: /Booking confirmed/i })).toBeInTheDocument();
    expect(screen.getByRole('heading', { name: /on our radar/i })).toBeInTheDocument();

    await fireEvent.click(screen.getByRole('button', { name: /Sounds great!/i }));

    await waitFor(() =>
      expect(screen.queryByRole('dialog', { name: /Booking confirmed/i })).not.toBeInTheDocument()
    );
  });

  it('allows general enquiries without dog or service details', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: vi.fn().mockResolvedValue({})
    });
    vi.stubGlobal('fetch', fetchMock);

    const { container } = render(BookingSection, {
      booking: homepageContent.booking,
      allowGeneralEnquiry: true
    });

    await fireEvent.click(screen.getByLabelText(/General enquiry/i));
    expect(screen.queryByLabelText(/Dog's Name/i)).not.toBeInTheDocument();
    expect(screen.queryByText('Pack Walks')).not.toBeInTheDocument();

    await fireEvent.click(container.querySelector('.booking-next-button')!);
    expect(screen.getByText('Please tell us how we can help')).toBeInTheDocument();

    await fireEvent.input(screen.getByLabelText(/Your Message/i), {
      target: { value: 'I would like to discuss a business partnership.' }
    });

    await fireEvent.click(container.querySelector('.booking-next-button')!);
    await fillOwnerStep();
    await fireEvent.click(container.querySelector('.booking-submit-button')!);

    await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));

    const payload = JSON.parse(fetchMock.mock.calls[0][1].body as string);
    expect(payload).toMatchObject({
      enquiryType: 'general',
      fullName: 'Alex Walker',
      email: 'alex@example.com',
      phone: '021 123 4567',
      petName: '',
      location: '',
      message: 'I would like to discuss a business partnership.',
      services: [],
      stepChanges: 1,
      journey: [window.location.pathname]
    });

    expect(screen.getByRole('dialog', { name: /Enquiry confirmed/i })).toBeInTheDocument();
    expect(screen.getByText(/Your message is with us!/i)).toBeInTheDocument();
  });

  it('shows the API error message when submission fails', async () => {
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: false,
        json: vi.fn().mockResolvedValue({ detail: 'Mail API unavailable' })
      })
    );

    const { container } = render(BookingSection, {
      booking: homepageContent.booking
    });

    await moveToOwnerStep(container);
    await fillOwnerStep();

    await fireEvent.click(container.querySelector('.booking-submit-button')!);

    expect(await screen.findByText('Mail API unavailable')).toBeInTheDocument();
  });
});
