import { render, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LeadsPage from '../page';

// Mock next/link
jest.mock('next/link', () => {
  return function MockLink({ children, href }: { children: React.ReactNode; href: string }) {
    return <a href={href}>{children}</a>;
  };
});

// Mock LeadFormSettingsModal
jest.mock('@/components/LeadFormSettingsModal', () => {
  return function MockLeadFormSettingsModal({
    isOpen,
    onClose,
  }: {
    isOpen: boolean;
    onClose: () => void;
    onSave: () => void;
  }) {
    if (!isOpen) return null;
    return (
      <div data-testid="settings-modal">
        <button onClick={onClose}>Close Settings</button>
      </div>
    );
  };
});

// Mock TierGate
jest.mock('@/components/ui/TierGate', () => {
  return function MockTierGate({ title }: { title: string }) {
    return <div data-testid="tier-gate">{title}</div>;
  };
});

// Mock fetch
global.fetch = jest.fn();

const mockLeads = [
  {
    id: 'lead-1',
    name: 'John Doe',
    email: 'john@example.com',
    phone: '(555) 123-4567',
    message: 'I want to learn more about your products',
    timestamp: '2024-01-15T10:00:00.000Z',
  },
  {
    id: 'lead-2',
    name: 'Jane Smith',
    email: 'jane@example.com',
    phone: null,
    message: 'Please contact me',
    timestamp: '2024-01-14T09:00:00.000Z',
  },
  {
    id: 'lead-3',
    name: 'Bob Wilson',
    email: 'bob@example.com',
    phone: '(555) 987-6543',
    message: 'Interested in becoming a dealer',
    timestamp: '2024-01-13T08:00:00.000Z',
  },
];

describe('LeadsPage', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  const mockFetchSuccess = (leads = mockLeads) => {
    (global.fetch as jest.Mock).mockResolvedValue({
      ok: true,
      json: () =>
        Promise.resolve({
          leads,
          total: leads.length,
          page: 1,
          pageSize: 20,
        }),
    });
  };

  const mockFetchUpgradeRequired = () => {
    (global.fetch as jest.Mock).mockResolvedValue({
      ok: false,
      json: () => Promise.resolve({ error: 'upgrade_required' }),
    });
  };

  const mockFetchError = () => {
    (global.fetch as jest.Mock).mockResolvedValue({
      ok: false,
      json: () => Promise.resolve({ error: 'Something went wrong' }),
    });
  };

  describe('loading state', () => {
    it('shows loading spinner initially', () => {
      (global.fetch as jest.Mock).mockImplementation(() => new Promise(() => {}));
      render(<LeadsPage />);

      expect(screen.getByText('Loading leads...')).toBeInTheDocument();
    });
  });

  describe('tier gate', () => {
    it('shows tier gate when upgrade required', async () => {
      mockFetchUpgradeRequired();
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByTestId('tier-gate')).toBeInTheDocument();
      });
    });
  });

  describe('error state', () => {
    it('shows error message and retry button', async () => {
      mockFetchError();
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('Something went wrong')).toBeInTheDocument();
        expect(screen.getByRole('button', { name: /try again/i })).toBeInTheDocument();
      });
    });

    it('retries fetch when retry button clicked', async () => {
      mockFetchError();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByRole('button', { name: /try again/i })).toBeInTheDocument();
      });

      mockFetchSuccess();
      await user.click(screen.getByRole('button', { name: /try again/i }));

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });
    });
  });

  describe('empty state', () => {
    it('shows empty state when no leads', async () => {
      mockFetchSuccess([]);
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('No leads yet')).toBeInTheDocument();
      });
    });
  });

  describe('leads table', () => {
    it('renders leads in table', async () => {
      mockFetchSuccess();
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
        expect(screen.getByText('Jane Smith')).toBeInTheDocument();
        expect(screen.getByText('Bob Wilson')).toBeInTheDocument();
      });
    });

    it('renders email links', async () => {
      mockFetchSuccess();
      render(<LeadsPage />);

      await waitFor(() => {
        const emailLink = screen.getByRole('link', { name: 'john@example.com' });
        expect(emailLink).toHaveAttribute('href', 'mailto:john@example.com');
      });
    });

    it('renders phone links when available', async () => {
      mockFetchSuccess();
      render(<LeadsPage />);

      await waitFor(() => {
        const phoneLink = screen.getByRole('link', { name: '(555) 123-4567' });
        expect(phoneLink).toHaveAttribute('href', 'tel:(555) 123-4567');
      });
    });

    it('shows dash for missing phone', async () => {
      mockFetchSuccess();
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('Jane Smith')).toBeInTheDocument();
      });

      // Jane Smith's row should have a dash for phone
      const rows = screen.getAllByRole('row');
      const janeRow = rows.find((row) => within(row).queryByText('Jane Smith'));
      expect(janeRow).toBeInTheDocument();
      expect(within(janeRow!).getByText('-')).toBeInTheDocument();
    });
  });

  describe('selection', () => {
    it('renders checkboxes for each lead', async () => {
      mockFetchSuccess();
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const checkboxes = screen.getAllByRole('checkbox');
      // 1 header checkbox + 3 row checkboxes
      expect(checkboxes).toHaveLength(4);
    });

    it('selects individual lead when checkbox clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const checkboxes = screen.getAllByRole('checkbox');
      await user.click(checkboxes[1]); // First row checkbox

      expect(checkboxes[1]).toBeChecked();
      expect(screen.getByText('1 selected')).toBeInTheDocument();
    });

    it('selects all leads when header checkbox clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const headerCheckbox = screen.getAllByRole('checkbox')[0];
      await user.click(headerCheckbox);

      const checkboxes = screen.getAllByRole('checkbox');
      checkboxes.forEach((checkbox) => {
        expect(checkbox).toBeChecked();
      });
      expect(screen.getByText('3 selected')).toBeInTheDocument();
    });

    it('deselects all when header checkbox clicked again', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const headerCheckbox = screen.getAllByRole('checkbox')[0];
      await user.click(headerCheckbox); // Select all
      await user.click(headerCheckbox); // Deselect all

      const checkboxes = screen.getAllByRole('checkbox');
      checkboxes.forEach((checkbox) => {
        expect(checkbox).not.toBeChecked();
      });
      expect(screen.queryByText(/selected/)).not.toBeInTheDocument();
    });

    it('clears selection when clear selection button clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const checkboxes = screen.getAllByRole('checkbox');
      await user.click(checkboxes[1]);

      await user.click(screen.getByText('Clear selection'));

      expect(checkboxes[1]).not.toBeChecked();
      expect(screen.queryByText(/selected/)).not.toBeInTheDocument();
    });
  });

  describe('bulk action bar', () => {
    it('shows bulk action bar when leads selected', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const checkboxes = screen.getAllByRole('checkbox');
      await user.click(checkboxes[1]);

      expect(screen.getByText('1 selected')).toBeInTheDocument();
      expect(screen.getByText('Delete selected')).toBeInTheDocument();
      expect(screen.getByText('Clear selection')).toBeInTheDocument();
    });

    it('hides bulk action bar when no leads selected', async () => {
      mockFetchSuccess();
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      expect(screen.queryByText('Delete selected')).not.toBeInTheDocument();
    });
  });

  describe('single lead deletion', () => {
    it('removes lead from list when delete button clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
      expect(screen.getByText('Jane Smith')).toBeInTheDocument();
    });

    it('shows pending deletion banner after deletion', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      expect(screen.getByText('1 lead deleted')).toBeInTheDocument();
      expect(screen.getByText('Undo')).toBeInTheDocument();
    });

    it('updates total count after deletion', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('3 total leads')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      expect(screen.getByText('2 total leads')).toBeInTheDocument();
    });
  });

  describe('bulk deletion', () => {
    it('removes multiple leads when delete selected clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const checkboxes = screen.getAllByRole('checkbox');
      await user.click(checkboxes[1]); // John Doe
      await user.click(checkboxes[2]); // Jane Smith

      await user.click(screen.getByText('Delete selected'));

      expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
      expect(screen.queryByText('Jane Smith')).not.toBeInTheDocument();
      expect(screen.getByText('Bob Wilson')).toBeInTheDocument();
      expect(screen.getByText('2 leads deleted')).toBeInTheDocument();
    });

    it('clears selection after bulk delete', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const checkboxes = screen.getAllByRole('checkbox');
      await user.click(checkboxes[1]);
      await user.click(screen.getByText('Delete selected'));

      expect(screen.queryByText(/selected/)).not.toBeInTheDocument();
    });
  });

  describe('undo functionality', () => {
    it('restores leads when undo clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      expect(screen.queryByText('John Doe')).not.toBeInTheDocument();

      await user.click(screen.getByText('Undo'));

      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.queryByText(/deleted/)).not.toBeInTheDocument();
    });

    it('restores multiple leads to correct positions', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      // Select and delete first two leads
      const checkboxes = screen.getAllByRole('checkbox');
      await user.click(checkboxes[1]);
      await user.click(checkboxes[2]);
      await user.click(screen.getByText('Delete selected'));

      await user.click(screen.getByText('Undo'));

      // All leads should be back
      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('Jane Smith')).toBeInTheDocument();
      expect(screen.getByText('Bob Wilson')).toBeInTheDocument();
    });

    it('restores total count when undo clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('3 total leads')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      expect(screen.getByText('2 total leads')).toBeInTheDocument();

      await user.click(screen.getByText('Undo'));

      expect(screen.getByText('3 total leads')).toBeInTheDocument();
    });

    it('hides pending deletion banner when undo clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      expect(screen.getByText('1 lead deleted')).toBeInTheDocument();

      await user.click(screen.getByText('Undo'));

      expect(screen.queryByText(/deleted/)).not.toBeInTheDocument();
    });
  });

  describe('combining deletions', () => {
    it('combines multiple deletions into single undo', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]); // Delete John

      expect(screen.getByText('1 lead deleted')).toBeInTheDocument();

      // Delete another before undo expires
      const remainingDeleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(remainingDeleteButtons[0]); // Delete Jane

      expect(screen.getByText('2 leads deleted')).toBeInTheDocument();
    });

    it('restores all combined deletions on single undo', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);
      const remainingDeleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(remainingDeleteButtons[0]);

      await user.click(screen.getByText('Undo'));

      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('Jane Smith')).toBeInTheDocument();
    });
  });

  describe('timer expiration', () => {
    it('calls delete API after 10 seconds', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      // Reset fetch mock to track delete call
      (global.fetch as jest.Mock).mockClear();
      (global.fetch as jest.Mock).mockResolvedValue({
        ok: true,
        json: () => Promise.resolve({ deleted: 1 }),
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      // Advance timers by 10 seconds
      jest.advanceTimersByTime(10000);

      await waitFor(() => {
        expect(global.fetch).toHaveBeenCalledWith('/api/dealer/leads', {
          method: 'DELETE',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ leadIds: ['lead-1'] }),
        });
      });
    });

    it('does not call API if undo clicked before timer', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      // Reset fetch mock to track delete call
      (global.fetch as jest.Mock).mockClear();

      // Advance 5 seconds, then undo
      jest.advanceTimersByTime(5000);
      await user.click(screen.getByText('Undo'));

      // Advance past the original timeout
      jest.advanceTimersByTime(10000);

      // DELETE should not have been called
      expect(global.fetch).not.toHaveBeenCalledWith(
        '/api/dealer/leads',
        expect.objectContaining({ method: 'DELETE' })
      );
    });

    it('clears pending deletion banner after timer expires', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      (global.fetch as jest.Mock).mockResolvedValue({
        ok: true,
        json: () =>
          Promise.resolve({
            leads: mockLeads.slice(1),
            total: 2,
            page: 1,
            pageSize: 20,
          }),
      });

      const deleteButtons = screen.getAllByTitle('Delete lead');
      await user.click(deleteButtons[0]);

      expect(screen.getByText('1 lead deleted')).toBeInTheDocument();

      jest.advanceTimersByTime(10000);

      await waitFor(() => {
        expect(screen.queryByText(/deleted/)).not.toBeInTheDocument();
      });
    });
  });

  describe('settings modal', () => {
    it('opens settings modal when settings button clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      await user.click(screen.getByRole('button', { name: /settings/i }));

      expect(screen.getByTestId('settings-modal')).toBeInTheDocument();
    });

    it('closes settings modal when close button clicked', async () => {
      mockFetchSuccess();
      const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      await user.click(screen.getByRole('button', { name: /settings/i }));
      await user.click(screen.getByText('Close Settings'));

      expect(screen.queryByTestId('settings-modal')).not.toBeInTheDocument();
    });
  });

  describe('navigation', () => {
    it('renders back to dashboard link', async () => {
      mockFetchSuccess();
      render(<LeadsPage />);

      await waitFor(() => {
        expect(screen.getByText('John Doe')).toBeInTheDocument();
      });

      const backLink = screen.getByRole('link', { name: /back to dashboard/i });
      expect(backLink).toHaveAttribute('href', '/dashboard');
    });
  });
});
