import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import CustomerDashboard from '../CustomerDashboard';

// Mock react-router-dom hooks
const mockOutletContext = vi.fn();

vi.mock('react-router-dom', async () => {
  const actual = await vi.importActual('react-router-dom');
  return {
    ...actual,
    useOutletContext: () => mockOutletContext(),
    Link: ({ children, to }: { children: React.ReactNode; to: string }) => (
      <a href={to}>{children}</a>
    ),
  };
});

// Mock hooks
const mockMutateAsync = vi.fn();
vi.mock('../../../hooks/useAppointments', () => ({
  useAppointments: vi.fn(() => ({
    data: [],
    isLoading: false,
    error: null,
  })),
  useUpdateAppointment: vi.fn(() => ({
    mutateAsync: mockMutateAsync,
    isPending: false,
  })),
}));

vi.mock('../../../hooks/useServices', () => ({
  useServices: vi.fn(() => ({
    data: [],
  })),
}));

// Mock Portal component
vi.mock('../../../components/Portal', () => ({
  default: ({ children }: { children: React.ReactNode }) => <div data-testid="portal">{children}</div>,
}));

import { useAppointments, useUpdateAppointment } from '../../../hooks/useAppointments';
import { useServices } from '../../../hooks/useServices';

const mockUseAppointments = useAppointments as ReturnType<typeof vi.fn>;
const mockUseUpdateAppointment = useUpdateAppointment as ReturnType<typeof vi.fn>;
const mockUseServices = useServices as ReturnType<typeof vi.fn>;

describe('CustomerDashboard', () => {
  const defaultContext = {
    user: { id: '1', name: 'John Doe', email: 'john@test.com', role: 'customer' },
    business: {
      id: '1',
      name: 'Test Business',
      cancellationWindowHours: 24,
      lateCancellationFeePercent: 50,
    },
  };

  beforeEach(() => {
    vi.clearAllMocks();
    mockOutletContext.mockReturnValue(defaultContext);
    mockUseAppointments.mockReturnValue({ data: [], isLoading: false, error: null });
    mockUseUpdateAppointment.mockReturnValue({ mutateAsync: mockMutateAsync, isPending: false });
    mockUseServices.mockReturnValue({ data: [] });
    window.confirm = vi.fn(() => true);
  });

  it('renders welcome message with user first name', () => {
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('Welcome, John!')).toBeInTheDocument();
  });

  it('renders description text', () => {
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('View your upcoming appointments and manage your account.')).toBeInTheDocument();
  });

  it('renders Your Appointments heading', () => {
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('Your Appointments')).toBeInTheDocument();
  });

  it('renders loading state', () => {
    mockUseAppointments.mockReturnValue({ data: [], isLoading: true, error: null });
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    // The loading spinner should be shown
    const container = document.querySelector('.animate-spin');
    expect(container).toBeInTheDocument();
  });

  it('renders error state', () => {
    mockUseAppointments.mockReturnValue({ data: [], isLoading: false, error: new Error('Failed') });
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('Failed to load appointments. Please try again later.')).toBeInTheDocument();
  });

  it('renders tab buttons', () => {
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('Upcoming')).toBeInTheDocument();
    expect(screen.getByText('Past')).toBeInTheDocument();
  });

  it('shows empty state when no appointments', () => {
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('No upcoming appointments found.')).toBeInTheDocument();
  });

  it('switches to past tab', () => {
    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    fireEvent.click(screen.getByText('Past'));
    expect(screen.getByText('No past appointments found.')).toBeInTheDocument();
  });

  it('renders appointments when available', () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);

    mockUseAppointments.mockReturnValue({
      data: [
        {
          id: '1',
          serviceId: 's1',
          startTime: futureDate,
          durationMinutes: 60,
          status: 'SCHEDULED',
          customerName: 'Test Customer',
        },
      ],
      isLoading: false,
      error: null,
    });
    mockUseServices.mockReturnValue({
      data: [{ id: 's1', name: 'Test Service', price: '50.00' }],
    });

    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('Test Service')).toBeInTheDocument();
  });

  it('shows appointment status badge', () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);

    mockUseAppointments.mockReturnValue({
      data: [
        {
          id: '1',
          serviceId: 's1',
          startTime: futureDate,
          durationMinutes: 60,
          status: 'SCHEDULED',
        },
      ],
      isLoading: false,
      error: null,
    });
    mockUseServices.mockReturnValue({
      data: [{ id: 's1', name: 'Test Service', price: '50.00' }],
    });

    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );
    expect(screen.getByText('SCHEDULED')).toBeInTheDocument();
  });

  it('opens appointment detail modal on click', () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);

    mockUseAppointments.mockReturnValue({
      data: [
        {
          id: '1',
          serviceId: 's1',
          startTime: futureDate,
          durationMinutes: 60,
          status: 'SCHEDULED',
        },
      ],
      isLoading: false,
      error: null,
    });
    mockUseServices.mockReturnValue({
      data: [{ id: 's1', name: 'Test Service', price: '50.00' }],
    });

    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );

    fireEvent.click(screen.getByText('Test Service'));
    expect(screen.getByText('Appointment Details')).toBeInTheDocument();
  });

  it('shows cancel button for upcoming appointments', () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);

    mockUseAppointments.mockReturnValue({
      data: [
        {
          id: '1',
          serviceId: 's1',
          startTime: futureDate,
          durationMinutes: 60,
          status: 'SCHEDULED',
        },
      ],
      isLoading: false,
      error: null,
    });
    mockUseServices.mockReturnValue({
      data: [{ id: 's1', name: 'Test Service', price: '50.00' }],
    });

    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );

    fireEvent.click(screen.getByText('Test Service'));
    expect(screen.getByText('Cancel Appointment')).toBeInTheDocument();
  });

  it('shows close button in modal', () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);

    mockUseAppointments.mockReturnValue({
      data: [
        {
          id: '1',
          serviceId: 's1',
          startTime: futureDate,
          durationMinutes: 60,
          status: 'SCHEDULED',
        },
      ],
      isLoading: false,
      error: null,
    });
    mockUseServices.mockReturnValue({
      data: [{ id: 's1', name: 'Test Service', price: '50.00' }],
    });

    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );

    fireEvent.click(screen.getByText('Test Service'));
    expect(screen.getByText('Close')).toBeInTheDocument();
  });

  it('shows print receipt button in modal', () => {
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + 7);

    mockUseAppointments.mockReturnValue({
      data: [
        {
          id: '1',
          serviceId: 's1',
          startTime: futureDate,
          durationMinutes: 60,
          status: 'SCHEDULED',
        },
      ],
      isLoading: false,
      error: null,
    });
    mockUseServices.mockReturnValue({
      data: [{ id: 's1', name: 'Test Service', price: '50.00' }],
    });

    render(
      <MemoryRouter>
        <CustomerDashboard />
      </MemoryRouter>
    );

    fireEvent.click(screen.getByText('Test Service'));
    expect(screen.getByText('Print Receipt')).toBeInTheDocument();
  });
});
