import { expect, test } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { AppStateProvider } from '../Context.js';
import getBatchUntrackedSerp200 from '../../../tests/fixtures/api/get-batch-untracked-serp-200.json' with { type: 'json' };
import getBatchUntrackedSerp200_2 from '../../../tests/fixtures/api/get-batch-untracked-serp-200_2.json' with { type: 'json' };
import Competitors, { type Serp } from './Competitors.js';

describe('Competitors', () => {
  test('Nothing loaded when no keywords provided', async () => {
    const initialState = {
      accountId: '12345',
      keywords: [],
      competitorUrls: [],
      excludedCompetitorUrls: [],
    };

    render(
      <AppStateProvider initialState={initialState}>
        <Competitors />
      </AppStateProvider>,
    );

    await waitFor(() => {
      expect(
        screen.getByText(
          'Keywords are required, please go back to the previous screen to select some.',
        ),
      ).toBeInTheDocument();
    });
  });

  test('Generate content guidance button disabled if not enough competitor URLs selected', async () => {
    const user = userEvent.setup();
    const initialState = {
      accountId: '12345',
      keywords: ['gritty', 'phanatic', 'swoop'],
      competitorUrls: getBatchUntrackedSerp200_2.map((serp: Serp) => serp.url),
      excludedCompetitorUrls: [],
    };

    render(
      <AppStateProvider initialState={initialState}>
        <Competitors />
      </AppStateProvider>,
    );

    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    const generateButton = screen.getByRole('button', { name: /generate content guidance/i });
    expect(generateButton).toBeEnabled();

    const checkboxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
    expect(checkboxes).toHaveLength(45);
    for (const checkbox of checkboxes) {
      if (checkbox.checked) {
        await user.click(checkbox);
      }
    }
    expect(checkboxes.filter((checkbox) => checkbox.checked)).toHaveLength(0);
    expect(generateButton).toBeDisabled();

    for (let i = 0; i < 9; i++) {
      await user.click(checkboxes[i]!);
    }
    expect(generateButton).toBeDisabled();
    await user.click(checkboxes[9]!);
    expect(generateButton).toBeEnabled();
  });

  describe('Accessibility', () => {
    test('checkboxes have proper labels and are keyboard accessible', async () => {
      const user = userEvent.setup();
      const initialState = {
        accountId: '12345',
        keywords: ['gritty', 'phanatic', 'swoop'],
        competitorUrls: getBatchUntrackedSerp200_2.map((serp: Serp) => serp.url),
        excludedCompetitorUrls: [],
      };

      render(
        <AppStateProvider initialState={initialState}>
          <Competitors />
        </AppStateProvider>,
      );

      await waitFor(() => {
        expect(screen.queryByText('Loading')).not.toBeInTheDocument();
      });

      const checkboxes = screen.getAllByRole('checkbox');

      // Test that each checkbox is properly labeled
      checkboxes.forEach((checkbox) => {
        // eslint-disable-next-line testing-library/no-node-access -- verifying label wraps checkbox
        const label = checkbox.closest('label');
        expect(label).toBeInTheDocument();
        expect(label).toHaveTextContent(/https?:\/\//); // Should contain URL text
      });

      // Test keyboard navigation
      const firstCheckbox = checkboxes[0];
      firstCheckbox?.focus();
      // eslint-disable-next-line testing-library/no-node-access -- checking focus state
      expect(document.activeElement).toBe(firstCheckbox);

      // Test space key interaction
      await user.keyboard(' ');
      expect(firstCheckbox).not.toBeChecked();
    });

    test('external links have proper accessibility attributes', async () => {
      const initialState = {
        accountId: '12345',
        keywords: ['gritty', 'phanatic', 'swoop'],
        competitorUrls: getBatchUntrackedSerp200.map((serp: Serp) => serp.url),
        excludedCompetitorUrls: [],
      };

      render(
        <AppStateProvider initialState={initialState}>
          <Competitors />
        </AppStateProvider>,
      );

      await waitFor(() => {
        expect(screen.queryByText('Loading')).not.toBeInTheDocument();
      });

      const externalLinks = screen.getAllByRole('link');

      externalLinks.forEach((link) => {
        // Check for proper security attributes.
        expect(link).toHaveAttribute('rel', 'noopener noreferrer');
        expect(link).toHaveAttribute('target', '_blank');
        expect(link).toHaveAttribute('title');
        expect(link).toHaveAttribute('href');
      });
    });
  });

  test('Loading spinner shows when Competitor URLs are loading', async () => {
    const initialState = {
      accountId: '12345',
      keywords: ['jawn'],
      competitorUrls: [],
      excludedCompetitorUrls: [],
    };

    render(
      <AppStateProvider initialState={initialState}>
        <Competitors />
      </AppStateProvider>,
    );

    const loadingSpinner = screen.getByRole('status');
    expect(loadingSpinner).toBeInTheDocument();
  });

  test('Error message shows on bad response', async () => {
    const user = userEvent.setup();
    const initialState = {
      accountId: '12345',
      keywords: ['schuylkill'],
      competitorUrls: [],
      excludedCompetitorUrls: [],
    };

    render(
      <AppStateProvider initialState={initialState}>
        <Competitors />
      </AppStateProvider>,
    );

    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    const error = screen.getByRole('alert');
    expect(error).toBeInTheDocument();

    const tryAgainButton = screen.getByRole('button', { name: /please try again/i });
    await user.click(tryAgainButton);
    await waitFor(() => {
      const spinner = screen.queryByText(/loading/i);
      expect(spinner).not.toBeInTheDocument();
    });
    const checkboxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
    // 75 because we've loaded a whole different set of competitor URLs and the previous
    // ones were saved to the draft.
    expect(checkboxes).toHaveLength(75);
  });
});
