import { expect, test } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import App from './App.js';
import React from 'react';

describe('App', () => {
  beforeEach(() => {
    Object.defineProperty(window, 'drupalSettings', {
      writable: true,
      configurable: true,
      value: {
        conductor: {
          accountId: '12345',
          draftId: '',
          maxDrafts: 5,
          usedDrafts: 2,
        },
        canvas: {
          entityId: '1',
          entityType: 'canvas_page',
        },
      },
    });
  });

  describe('Draft usage alert', () => {
    test('displays alert and hides tabs when no drafts remain', () => {
      if (window.drupalSettings?.conductor) {
        window.drupalSettings.conductor.usedDrafts = 5;
        window.drupalSettings.conductor.maxDrafts = 5;
      }

      render(<App />);

      // Verify the alert is displayed
      expect(screen.getByRole('alert')).toBeInTheDocument();
      expect(screen.getByText(/No drafts remaining\./i)).toBeInTheDocument();

      // Verify tabs are not displayed
      expect(screen.queryByRole('tab', { name: /keywords/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('tab', { name: /competitors/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('tab', { name: /content outline/i })).not.toBeInTheDocument();

      // Verify the no-drafts message is displayed
      expect(screen.getByText(/You have reached your draft limit/i)).toBeInTheDocument();
    });

    test('displays tabs when no drafts remain, but editing existing draft', () => {
      if (window.drupalSettings?.conductor) {
        window.drupalSettings.conductor.draftId = '12345';
        window.drupalSettings.conductor.usedDrafts = 5;
        window.drupalSettings.conductor.maxDrafts = 5;
      }

      render(<App />);

      // Verify the alert is displayed
      expect(screen.getByRole('alert')).toBeInTheDocument();
      expect(screen.getByText(/No drafts remaining\./i)).toBeInTheDocument();

      // Verify tabs ARE displayed because we have an existing draft ID
      expect(screen.getByRole('tab', { name: /keywords/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /competitors/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /content outline/i })).toBeInTheDocument();

      // Verify the no-drafts message is NOT displayed
      expect(screen.queryByText(/You have reached your draft limit/i)).not.toBeInTheDocument();
    });

    test('displays error message when draft usage information is not available', () => {
      if (window.drupalSettings?.conductor) {
        delete window.drupalSettings.conductor.usedDrafts;
        delete window.drupalSettings.conductor.maxDrafts;
      }

      render(<App />);

      // Verify the error message is displayed
      expect(
        screen.getByText(/Draft usage information is not available. Please reload the app./i),
      ).toBeInTheDocument();

      // Verify tabs are not displayed
      expect(screen.queryByRole('tab', { name: /keywords/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('tab', { name: /competitors/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('tab', { name: /content outline/i })).not.toBeInTheDocument();
    });

    test('shows the alert when more than two-thirds of drafts are used', () => {
      if (window.drupalSettings?.conductor) {
        window.drupalSettings.conductor.usedDrafts = 7;
        window.drupalSettings.conductor.maxDrafts = 9;
      }

      render(<App />);

      expect(screen.getByRole('alert')).toBeInTheDocument();
      expect(
        screen.getByText(/You have used 7 out of 9 of your writing drafts/i),
      ).toBeInTheDocument();
    });

    test("doesn't show the alert when two-thirds or less of drafts are used", () => {
      if (window.drupalSettings?.conductor) {
        window.drupalSettings.conductor.usedDrafts = 3;
        window.drupalSettings.conductor.maxDrafts = 6;
      }

      expect(screen.queryByRole('alert')).not.toBeInTheDocument();
    });

    test('shows contact support message when draft limit reached', () => {
      if (window.drupalSettings?.conductor) {
        window.drupalSettings.conductor.usedDrafts = 10;
        window.drupalSettings.conductor.maxDrafts = 10;
      }

      render(<App />);

      expect(screen.getByRole('alert')).toBeInTheDocument();
      expect(
        screen.getByText(
          /You have reached your draft limit. Please contact support to increase your limit/i,
        ),
      ).toBeInTheDocument();

      // Verify tabs are not displayed
      expect(screen.queryByRole('tab', { name: /keywords/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('tab', { name: /competitors/i })).not.toBeInTheDocument();
      expect(screen.queryByRole('tab', { name: /content outline/i })).not.toBeInTheDocument();
    });
  });

  test('4 tabs are present and first tab content is visible', () => {
    render(<App />);

    // Verify all 4 tabs are present
    const keywordsTab = screen.getByRole('tab', { name: /keywords/i });
    const competitorsTab = screen.getByRole('tab', { name: /competitors/i });
    // const guidanceTab = screen.getByRole('tab', { name: /guidance/i });
    const contentOutlineTab = screen.getByRole('tab', { name: /content outline/i });

    expect(keywordsTab).toBeInTheDocument();
    expect(competitorsTab).toBeInTheDocument();
    // expect(guidanceTab).toBeInTheDocument();
    expect(contentOutlineTab).toBeInTheDocument();

    // Verify the first tab (Keywords) is selected/active by default
    expect(keywordsTab).toHaveAttribute('aria-selected', 'true');
  });

  describe('Keywords', () => {
    test('Keywords are saved to state', async () => {
      const user = userEvent.setup();

      render(<App />);

      const input = screen.getByRole('textbox');

      await user.type(input, 'jawn{Enter}');
      await user.type(input, 'tastykake{Enter}');
      expect(screen.queryByText(/^jawn$/)).toBeInTheDocument();
      expect(screen.queryByText(/^tastykake$/)).toBeInTheDocument();

      const competitorsTab = screen.getByRole('tab', { name: /competitors/i });
      await user.click(competitorsTab);

      expect(screen.queryByText(/^jawn$/)).not.toBeInTheDocument();
      expect(screen.queryByText(/^tastykake$/)).not.toBeInTheDocument();

      const keywordsTab = screen.getByRole('tab', { name: /keywords/i });
      await user.click(keywordsTab);

      expect(screen.queryByText(/^jawn$/)).toBeInTheDocument();
      expect(screen.queryByText(/^tastykake$/)).toBeInTheDocument();
    });

    test('Keywords persist when switching tabs', async () => {
      const user = userEvent.setup();

      render(<App />);

      // Add a keyword in the Keywords tab and 'Evaluate'
      await user.type(screen.getByRole('textbox'), 'phanatic{Enter}');
      const evaluateButton = screen.getByRole('button', { name: /evaluate/i });
      expect(evaluateButton).not.toBeDisabled();
      await user.click(evaluateButton);
      const loadingSpinner = screen.getByRole('status');
      expect(loadingSpinner).toBeInTheDocument();
      await waitFor(
        () => {
          const loadingSpinner = screen.queryByRole('status');
          expect(loadingSpinner).not.toBeInTheDocument();
        },
        {
          timeout: 3000,
        },
      );
      await waitFor(() => {
        expect(screen.getByRole('table')).toBeInTheDocument();
      });

      // Switch to the Competitors tab
      await user.click(screen.getByRole('tab', { name: /competitors/i }));
      expect(screen.queryByRole('table')).not.toBeInTheDocument();

      // Switch back to the Keywords tab
      await user.click(screen.getByRole('tab', { name: /keywords/i }));
      expect(screen.getByText('phanatic')).toBeInTheDocument();

      // Wait for results table to be restored
      await waitFor(() => {
        expect(screen.getByRole('table')).toBeInTheDocument();
      });
    });
  });

  test('Rank source is preserved', async () => {
    const user = userEvent.setup();
    const { container } = render(<App />);

    let singleValueElement = container.querySelector('.rank-source-selector__single-value');
    expect(singleValueElement).toHaveTextContent('Google (US / English)');

    fireEvent.mouseDown(container.querySelector('.rank-source-selector__dropdown-indicator')!, {
      button: 0,
    });

    await user.keyboard('United Kingdom');
    await user.keyboard('{Enter}');

    singleValueElement = container.querySelector('.rank-source-selector__single-value');
    expect(singleValueElement).toHaveTextContent('Google (United Kingdom / English)');

    const competitorsTab = screen.getByRole('tab', { name: /competitors/i });
    await user.click(competitorsTab);
    const keywordsTab = screen.getByRole('tab', { name: /keywords/i });
    await user.click(keywordsTab);

    singleValueElement = container.querySelector('.rank-source-selector__single-value');
    expect(singleValueElement).toHaveTextContent('Google (United Kingdom / English)');
  });

  test('Competitor URLs fetched when clicking the Generate Content Guidance button', async () => {
    const user = userEvent.setup();

    render(<App />);

    const input = screen.getByRole('textbox');

    await user.type(input, 'jawn{Enter}');
    await user.type(input, 'tastykake{Enter}');
    expect(screen.queryByText(/^jawn$/)).toBeInTheDocument();
    expect(screen.queryByText(/^tastykake$/)).toBeInTheDocument();

    const generateButton = screen.getByRole('button', { name: /generate content guidance/i });
    await user.click(generateButton);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    const listItems = screen.getAllByRole('listitem');
    expect(listItems).toHaveLength(30);
  });

  test('Competitor URLs fetched when changing tabs', async () => {
    const user = userEvent.setup();

    render(<App />);

    const input = screen.getByRole('textbox');

    await user.type(input, 'jawn{Enter}');
    await user.type(input, 'tastykake{Enter}');

    const competitorsTab = screen.getByRole('tab', { name: /competitors/i });
    await user.click(competitorsTab);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    const listItems = screen.getAllByRole('listitem');
    expect(listItems).toHaveLength(30);

    const label = screen.getByLabelText('https://en.wikipedia.org/wiki/Jawn');
    expect(label).toBeInTheDocument();
  });

  test('Competitor URLs updated when keywords are updated', async () => {
    const user = userEvent.setup();

    const { container } = render(<App />);

    let input = screen.getByRole('textbox');

    await user.type(input, 'jawn{Enter}');
    await user.type(input, 'tastykake{Enter}');

    const competitorsTab = screen.getByRole('tab', { name: /competitors/i });
    await user.click(competitorsTab);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    let firstLabel = container.querySelector('label:first-of-type');
    expect(firstLabel).toHaveTextContent('https://en.wikipedia.org/wiki/Jawn');

    const keywordsTab = screen.getByRole('tab', { name: /keywords/i });
    await user.click(keywordsTab);
    const removeJawnButton = screen.getByLabelText('Remove jawn');
    await user.click(removeJawnButton);
    const removeTastykakeButton = screen.getByLabelText('Remove tastykake');
    await user.click(removeTastykakeButton);
    expect(screen.queryByText('jawn')).not.toBeInTheDocument();
    expect(screen.queryByText('tastykake')).not.toBeInTheDocument();
    input = screen.getByRole('textbox');
    await user.type(input, 'gritty{Enter}');
    await user.type(input, 'phanatic{Enter}');
    await user.type(input, 'swoop{Enter}');

    const generateButton = screen.getByRole('button', { name: /generate content guidance/i });
    await user.click(generateButton);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });
    firstLabel = container.querySelector('label:first-of-type');
    expect(firstLabel).toHaveTextContent('https://www.instagram.com/grittynhl/');
  });

  test('Excluded competitor URLs are preserved', async () => {
    const user = userEvent.setup();

    render(<App />);

    const input = screen.getByRole('textbox');

    await user.type(input, 'jawn{Enter}');
    await user.type(input, 'tastykake{Enter}');

    const generateButton = screen.getByRole('button', { name: /generate content guidance/i });
    await user.click(generateButton);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    const listItems = screen.getAllByRole('listitem');
    expect(listItems).toHaveLength(30);

    const checkboxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
    await user.click(checkboxes[0]!);
    await user.click(checkboxes[1]!);
    await user.click(checkboxes[2]!);
    const checkedCheckboxes = checkboxes.filter((checkbox) => checkbox.checked);
    expect(checkedCheckboxes).toHaveLength(27);

    const keywordsTab = screen.getByRole('tab', { name: /keywords/i });
    await user.click(keywordsTab);
    // const guidanceTab = screen.getByRole('tab', { name: /guidance/i });
    // await user.click(guidanceTab);
    const competitorsTab = screen.getByRole('tab', { name: /competitors/i });
    await user.click(competitorsTab);

    const checkboxesAgain = screen.getAllByRole('checkbox') as HTMLInputElement[];
    const checkedCheckboxesAgain = checkboxesAgain.filter((checkbox) => checkbox.checked);
    expect(checkedCheckboxesAgain).toHaveLength(27);
  });

  test('Competitor URLs are preserved', async () => {
    Object.defineProperty(window, 'drupalSettings', {
      writable: true,
      configurable: true,
      value: {
        conductor: {
          accountId: '12345',
          draftId: 'bead88f1-bd37-48a7-9861-46f04058141d',
          maxDrafts: 5,
          usedDrafts: 2,
        },
        canvas: {
          entityId: '1',
          entityType: 'canvas_page',
        },
      },
    });

    const user = userEvent.setup();

    render(<App />);

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

    const generateButton = screen.getByRole('button', { name: /generate content guidance/i });
    await user.click(generateButton);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    const checkboxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
    expect(checkboxes).toHaveLength(45);
    await user.click(checkboxes[0]!);

    const keywordsTab = screen.getByRole('tab', { name: /keywords/i });
    await user.click(keywordsTab);
    await user.click(generateButton);
    const checkedCheckboxes = checkboxes.filter((checkbox) => checkbox.checked);
    expect(checkedCheckboxes).toHaveLength(44);
  });

  test('Competitor URLs are reset when keywords are changed', async () => {
    const user = userEvent.setup();

    render(<App />);

    let input = screen.getByRole('textbox');

    await user.type(input, 'jawn{Enter}');
    await user.type(input, 'tastykake{Enter}');

    let generateButton = screen.getByRole('button', { name: /generate content guidance/i });
    await user.click(generateButton);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });

    let checkboxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
    expect(checkboxes).toHaveLength(30);
    await user.click(checkboxes[0]!);
    let checkedCheckboxes = checkboxes.filter((checkbox) => checkbox.checked);
    expect(checkedCheckboxes).toHaveLength(29);

    const keywordsTab = screen.getByRole('tab', { name: /keywords/i });
    await user.click(keywordsTab);
    await user.click(generateButton);
    expect(checkedCheckboxes).toHaveLength(29);

    await user.click(keywordsTab);
    input = screen.getByRole('textbox');
    await user.type(input, 'gritty{Enter}');
    generateButton = screen.getByRole('button', { name: /generate content guidance/i });
    await user.click(generateButton);
    await waitFor(() => {
      expect(screen.queryByText('Loading')).not.toBeInTheDocument();
    });
    checkboxes = screen.getAllByRole('checkbox') as HTMLInputElement[];
    checkedCheckboxes = checkboxes.filter((checkbox) => checkbox.checked);
    expect(checkedCheckboxes).toHaveLength(30);
  });
});
