import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ResourceCalendar from '../ResourceCalendar';
import { Appointment } from '../../types';

// Mock react-i18next
vi.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: (key: string, defaultValue?: string) => defaultValue || key,
  }),
}));

// Mock Portal component
vi.mock('../Portal', () => ({
  default: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));

// Mock date-fns to control time-based tests
vi.mock('date-fns', async () => {
  const actual = await vi.importActual('date-fns');
  return {
    ...actual,
  };
});

// Use today's date for appointments so they show up in the calendar
const today = new Date();
today.setHours(10, 0, 0, 0);

const mockAppointments: Appointment[] = [
  {
    id: '1',
    resourceId: 'resource-1',
    customerId: 'customer-1',
    customerName: 'John Doe',
    serviceId: 'service-1',
    startTime: new Date(today.getTime()),
    durationMinutes: 60,
    status: 'SCHEDULED',
    notes: 'First appointment',
    depositAmount: null,
    depositTransactionId: '',
    finalPrice: null,
    finalChargeTransactionId: '',
    isVariablePricing: false,
    remainingBalance: null,
    overpaidAmount: null,
  },
  {
    id: '2',
    resourceId: 'resource-1',
    customerId: 'customer-2',
    customerName: 'Jane Smith',
    serviceId: 'service-2',
    startTime: new Date(today.getTime() + 4.5 * 60 * 60 * 1000), // 14:30
    durationMinutes: 90,
    status: 'SCHEDULED',
    notes: 'Second appointment',
    depositAmount: null,
    depositTransactionId: '',
    finalPrice: null,
    finalChargeTransactionId: '',
    isVariablePricing: false,
    remainingBalance: null,
    overpaidAmount: null,
  },
  {
    id: '3',
    resourceId: 'resource-2',
    customerId: 'customer-3',
    customerName: 'Bob Johnson',
    serviceId: 'service-1',
    startTime: new Date(today.getTime() + 1 * 60 * 60 * 1000), // 11:00
    durationMinutes: 45,
    status: 'SCHEDULED',
    notes: 'Different resource',
    depositAmount: null,
    depositTransactionId: '',
    finalPrice: null,
    finalChargeTransactionId: '',
    isVariablePricing: false,
    remainingBalance: null,
    overpaidAmount: null,
  },
];

vi.mock('../../hooks/useAppointments', () => ({
  useAppointments: vi.fn(),
  useUpdateAppointment: vi.fn(),
}));

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

const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('ResourceCalendar', () => {
  const mockOnClose = vi.fn();
  const mockUpdateMutate = vi.fn();

  const defaultProps = {
    resourceId: 'resource-1',
    resourceName: 'Dr. Smith',
    onClose: mockOnClose,
  };

  beforeEach(() => {
    vi.clearAllMocks();

    vi.mocked(useAppointments).mockReturnValue({
      data: mockAppointments,
      isLoading: false,
    } as any);

    vi.mocked(useUpdateAppointment).mockReturnValue({
      mutate: mockUpdateMutate,
      mutateAsync: vi.fn(),
    } as any);
  });

  describe('Rendering', () => {
    it('renders calendar modal', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText('Dr. Smith Calendar')).toBeInTheDocument();
    });

    it('displays close button', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const closeButtons = screen.getAllByRole('button');
      const closeButton = closeButtons.find(btn => btn.querySelector('svg'));
      expect(closeButton).toBeInTheDocument();
    });

    it('calls onClose when close button is clicked', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const closeButtons = screen.getAllByRole('button');
      const closeButton = closeButtons.find(btn => btn.querySelector('svg'));
      fireEvent.click(closeButton!);
      expect(mockOnClose).toHaveBeenCalled();
    });

    it('displays resource name in title', () => {
      render(<ResourceCalendar {...defaultProps} resourceName="Conference Room A" />, {
        wrapper: createWrapper(),
      });
      expect(screen.getByText('Conference Room A Calendar')).toBeInTheDocument();
    });
  });

  describe('View modes', () => {
    it('renders day view by default', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const dayButton = screen.getByRole('button', { name: /^day$/i });
      expect(dayButton).toHaveClass('bg-white');
    });

    it('switches to week view when week button is clicked', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const weekButton = screen.getByRole('button', { name: /^week$/i });
      fireEvent.click(weekButton);
      expect(weekButton).toHaveClass('bg-white');
    });

    it('switches to month view when month button is clicked', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const monthButton = screen.getByRole('button', { name: /^month$/i });
      fireEvent.click(monthButton);
      expect(monthButton).toHaveClass('bg-white');
    });
  });

  describe('Navigation', () => {
    it('displays Today button', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByRole('button', { name: /today/i })).toBeInTheDocument();
    });

    it('displays previous and next navigation buttons', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const buttons = screen.getAllByRole('button');
      const navButtons = buttons.filter(btn => btn.querySelector('svg'));
      expect(navButtons.length).toBeGreaterThan(2);
    });

    it('navigates to previous day in day view', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const buttons = screen.getAllByRole('button');
      const prevButton = buttons.find(btn => {
        const svg = btn.querySelector('svg');
        return svg && btn.querySelector('[class*="ChevronLeft"]');
      });

      // Initial date rendering
      const initialText = screen.getByText(/\w+, \w+ \d+, \d{4}/);
      const initialDate = initialText.textContent;

      if (prevButton) {
        fireEvent.click(prevButton);
        const newText = screen.getByText(/\w+, \w+ \d+, \d{4}/);
        expect(newText.textContent).not.toBe(initialDate);
      }
    });

    it('clicks Today button to reset to current date', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const todayButton = screen.getByRole('button', { name: /today/i });
      fireEvent.click(todayButton);
      // Should display current date
      expect(screen.getByText(/\w+, \w+ \d+, \d{4}/)).toBeInTheDocument();
    });
  });

  describe('Appointments display', () => {
    it('displays appointments for the selected resource', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('Jane Smith')).toBeInTheDocument();
    });

    it('filters out appointments for other resources', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.queryByText('Bob Johnson')).not.toBeInTheDocument();
    });

    it('displays appointment customer names', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('Jane Smith')).toBeInTheDocument();
    });

    it('displays appointment time and duration', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      // Check for time format (e.g., "10:00 AM • 60 min")
      // Use getAllByText since there might be multiple appointments with same duration
      const timeElements = screen.getAllByText(/10:00 AM/);
      expect(timeElements.length).toBeGreaterThan(0);
      const durationElements = screen.getAllByText(/1h/);
      expect(durationElements.length).toBeGreaterThan(0);
    });
  });

  describe('Loading states', () => {
    it('displays loading message when loading', () => {
      vi.mocked(useAppointments).mockReturnValue({
        data: [],
        isLoading: true,
      } as any);

      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText('scheduler.loadingAppointments')).toBeInTheDocument();
    });

    it('displays empty state when no appointments', () => {
      vi.mocked(useAppointments).mockReturnValue({
        data: [],
        isLoading: false,
      } as any);

      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText('scheduler.noAppointmentsScheduled')).toBeInTheDocument();
    });
  });

  describe('Week view', () => {
    it('renders week view when week button is clicked', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      const weekButton = screen.getByRole('button', { name: /^week$/i });
      fireEvent.click(weekButton);

      // Verify week button is active (has bg-white class)
      expect(weekButton).toHaveClass('bg-white');
    });

    it('week view shows different content than day view', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });

      // Get content in day view
      const dayViewContent = document.body.textContent || '';

      // Switch to week view
      fireEvent.click(screen.getByRole('button', { name: /^week$/i }));

      // Get content in week view
      const weekViewContent = document.body.textContent || '';

      // Week view and day view should have different content
      // (Week view shows multiple days, day view shows single day timeline)
      expect(weekViewContent).not.toBe(dayViewContent);

      // Week view should show hint text for clicking days
      expect(screen.getByText(/click a day to view details/i)).toBeInTheDocument();
    });
  });

  describe('Month view', () => {
    it('displays calendar grid in month view', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      fireEvent.click(screen.getByRole('button', { name: /month/i }));

      // Should show weekday headers
      expect(screen.getByText('Mon')).toBeInTheDocument();
      expect(screen.getByText('Tue')).toBeInTheDocument();
      expect(screen.getByText('Wed')).toBeInTheDocument();
    });

    it('shows appointment count in month view cells', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      fireEvent.click(screen.getByRole('button', { name: /month/i }));

      // Should show "2 appts" for the day with 2 appointments
      expect(screen.getByText(/2 appt/)).toBeInTheDocument();
    });

    it('clicking a day in month view switches to week view', async () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      fireEvent.click(screen.getByRole('button', { name: /month/i }));

      // Find day cells and click one
      const dayCells = screen.getAllByText(/^\d+$/);
      if (dayCells.length > 0) {
        fireEvent.click(dayCells[0].closest('div')!);

        await waitFor(() => {
          const weekButton = screen.getByRole('button', { name: /week/i });
          expect(weekButton).toHaveClass('bg-white');
        });
      }
    });
  });

  describe('Drag and drop (day view)', () => {
    it('displays drag hint in day view', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText(/drag to move/i)).toBeInTheDocument();
    });

    it('displays click hint in week/month view', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      fireEvent.click(screen.getByRole('button', { name: /week/i }));
      expect(screen.getByText(/click a day to view details/i)).toBeInTheDocument();
    });
  });

  describe('Appointment interactions', () => {
    it('renders appointments with appropriate styling in day view', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });

      // Verify appointments are rendered
      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('Jane Smith')).toBeInTheDocument();

      // Verify they have parent elements (appointment containers)
      const appointment1 = screen.getByText('John Doe').parentElement;
      const appointment2 = screen.getByText('Jane Smith').parentElement;

      expect(appointment1).toBeInTheDocument();
      expect(appointment2).toBeInTheDocument();
    });
  });

  describe('Duration formatting', () => {
    it('formats duration less than 60 minutes as minutes', () => {
      const shortAppointment: Appointment = {
        ...mockAppointments[0],
        durationMinutes: 45,
      };

      vi.mocked(useAppointments).mockReturnValue({
        data: [shortAppointment],
        isLoading: false,
      } as any);

      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText(/45 min/)).toBeInTheDocument();
    });

    it('formats duration 60+ minutes as hours', () => {
      const longAppointment: Appointment = {
        ...mockAppointments[0],
        durationMinutes: 120,
      };

      vi.mocked(useAppointments).mockReturnValue({
        data: [longAppointment],
        isLoading: false,
      } as any);

      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText(/2h/)).toBeInTheDocument();
    });

    it('formats duration with hours and minutes', () => {
      const mixedAppointment: Appointment = {
        ...mockAppointments[0],
        durationMinutes: 90,
      };

      vi.mocked(useAppointments).mockReturnValue({
        data: [mixedAppointment],
        isLoading: false,
      } as any);

      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText(/1h 30m/)).toBeInTheDocument();
    });
  });

  describe('Accessibility', () => {
    it('has accessible button labels', () => {
      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByRole('button', { name: /^day$/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /^week$/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /^month$/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /^today$/i })).toBeInTheDocument();
    });
  });

  describe('Overlapping appointments', () => {
    it('handles overlapping appointments with lane layout', () => {
      const todayAt10 = new Date();
      todayAt10.setHours(10, 0, 0, 0);
      const todayAt1030 = new Date();
      todayAt1030.setHours(10, 30, 0, 0);

      const overlappingAppointments: Appointment[] = [
        {
          ...mockAppointments[0],
          startTime: todayAt10,
          durationMinutes: 120,
        },
        {
          ...mockAppointments[1],
          id: '2',
          startTime: todayAt1030,
          durationMinutes: 60,
        },
      ];

      vi.mocked(useAppointments).mockReturnValue({
        data: overlappingAppointments,
        isLoading: false,
      } as any);

      render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('Jane Smith')).toBeInTheDocument();
    });
  });

  describe('Props variations', () => {
    it('works with different resource IDs', () => {
      render(<ResourceCalendar {...defaultProps} resourceId="resource-2" />, {
        wrapper: createWrapper(),
      });
      expect(screen.getByText('Bob Johnson')).toBeInTheDocument();
    });

    it('updates when resource name changes', () => {
      const { rerender } = render(<ResourceCalendar {...defaultProps} />, { wrapper: createWrapper() });
      expect(screen.getByText('Dr. Smith Calendar')).toBeInTheDocument();

      rerender(
        <QueryClientProvider client={new QueryClient()}>
          <ResourceCalendar {...defaultProps} resourceName="Dr. Jones" />
        </QueryClientProvider>
      );
      expect(screen.getByText('Dr. Jones Calendar')).toBeInTheDocument();
    });
  });
});
