import { describe, test, expect, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { type UserEvent, userEvent } from '@testing-library/user-event';
import ContentGeneratorTextArea from './ContentGeneratorTextArea.js';

// Mock ResizeObserver
global.ResizeObserver = vi.fn().mockImplementation(() => ({
  observe: vi.fn(),
  unobserve: vi.fn(),
  disconnect: vi.fn(),
}));

vi.mock('../../assets/lightbulb.svg?react', () => ({
  default: () => <div data-testid="lightbulb-icon">Lightbulb</div>,
}));

const clickLightbulb = async (user: UserEvent, container: HTMLElement) => {
  // Wait for CKEditor toolbar
  await waitFor(() => {
    expect(container.querySelector('.ck-editor__top')).toBeInTheDocument();
  });

  // Give MutationObserver time to run
  await new Promise((resolve) => setTimeout(resolve, 500));

  const lightbulb = await screen.findByLabelText('Generate content with AI');

  await user.click(lightbulb);
};

describe('ContentGeneratorTextArea', () => {
  test('renders lightbulb button after editor toolbar mounts', async () => {
    render(<ContentGeneratorTextArea draftId="12345" />);

    await waitFor(() => {
      expect(screen.getByLabelText('Generate content with AI')).toBeInTheDocument();
    });
  });

  test('opens menu when lightbulb is clicked', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    expect(screen.getByTestId('content-generator-text-area__context-menu')).toBeInTheDocument();
    expect(screen.getByText('Replace with generated outline')).toBeInTheDocument();
    expect(screen.getByText('Replace with generated draft')).toBeInTheDocument();
  });

  test('closes menu when lightbulb is clicked again', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    expect(screen.getByText('Replace with generated outline')).toBeInTheDocument();
    const lightbulb = await screen.findByLabelText('Generate content with AI');
    await user.click(lightbulb);
    expect(screen.queryByText('Replace with generated outline')).not.toBeInTheDocument();
  });

  test('displays streaming overlay when loading', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    const generateOutline = screen.getByText('Replace with generated outline');
    await user.click(generateOutline);

    const messageElement = screen.getByText(/Generating content/i);
    expect(messageElement).toBeInTheDocument();
    await waitFor(
      () => {
        expect(screen.queryByText(/Generating content/i)).not.toBeInTheDocument();
      },
      { timeout: 3000 },
    );
  });

  test('generates text when "Generate outline" is clicked', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    const generateOutline = screen.getByText('Replace with generated outline');
    await user.click(generateOutline);

    const messageElement = screen.getByText(/Generating content/i);
    expect(messageElement).toBeInTheDocument();

    await waitFor(() => {
      const editableArea = screen.getByRole('textbox');
      expect(editableArea.textContent).toContain(
        'Strategic overview: This content focuses specifically on the unique cultural significance',
      );
    });
  });

  test('generates text when "Generate draft" is clicked', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    const generateDraft = screen.getByText('Replace with generated draft');
    await user.click(generateDraft);
    const generateContentButton = screen.getByRole('button', { name: 'Generate content' });
    await user.click(generateContentButton);

    const messageElement = screen.getByText(/Generating content/i);
    expect(messageElement).toBeInTheDocument();

    await waitFor(() => {
      const editableArea = screen.getByRole('textbox');
      expect(editableArea.textContent).toContain(
        'Philadelphia sports culture is legendary for its passionate fans and compelling traditions',
      );
    });
  });

  test('generates text with user options when "Generate draft" is clicked', async () => {
    const user = userEvent.setup();
    const fetchSpy = vi.spyOn(global, 'fetch');
    const { container } = render(
      <ContentGeneratorTextArea draftId="0ef76729-8512-432b-8b72-8a1bc47e4d60" />,
    );
    await clickLightbulb(user, container);

    const generateDraft = screen.getByText('Replace with generated draft');
    await user.click(generateDraft);

    // Fill in tone and style
    const toneAndStyleInput = screen.getByPlaceholderText('Describe the voice of your content');
    await user.type(toneAndStyleInput, 'over excited');

    // Fill in terms to exclude (tag input)
    const tagInput = screen.getByRole('textbox', { name: '' });
    await user.type(tagInput, 'orange{Enter}');

    // Fill in additional instructions
    const additionalInstructionsInput = screen.getByPlaceholderText(
      'What else should we know about your content?',
    );
    await user.type(additionalInstructionsInput, 'the audience is non-US');

    const generateContentButton = screen.getByRole('button', { name: 'Generate content' });
    await user.click(generateContentButton);

    // Verify the POST request was made with correct URL and payload
    await waitFor(() => {
      expect(fetchSpy).toHaveBeenCalledWith(
        '/conductor/proxy/v3/draft-generation/content-generation?draft_id=0ef76729-8512-432b-8b72-8a1bc47e4d60&cache=false',
        expect.objectContaining({
          method: 'POST',
          body: JSON.stringify({
            voice_and_tone: 'over excited',
            terms_to_exclude: ['orange'],
            additional_instructions: 'the audience is non-US',
          }),
          headers: expect.objectContaining({
            'Content-Type': 'application/json',
          }),
        }),
      );
    });
  });

  test('closes menu after "Generate outline" is clicked', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    await user.click(screen.getByText('Replace with generated outline'));

    expect(screen.queryByText('Replace with generated outline')).not.toBeInTheDocument();
  });

  test('closes menu after "Generate draft" is clicked', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    await user.click(screen.getByText('Replace with generated draft'));

    expect(screen.queryByText('Replace with generated draft')).not.toBeInTheDocument();
  });

  test('calls cancel when Cancel button is clicked in streaming overlay', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    await user.click(screen.getByText('Replace with generated outline'));

    const cancelButton = screen.getByRole('button', { name: 'Cancel' });
    await user.click(cancelButton);

    const editableArea = screen.getByRole('textbox');
    expect(editableArea.textContent).not.toContain(
      'enduring role in Philadelphia sports and fandom',
    );
  });

  test('displays error overlay when there is an error', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345-bad" />);
    await clickLightbulb(user, container);

    const generateOutline = screen.getByText('Replace with generated outline');
    await user.click(generateOutline);

    expect(screen.getByText('Error')).toBeInTheDocument();
    expect(screen.getByText(/There was an error generating content/)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument();
  });

  test('calls cancel when Close button is clicked in error overlay', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345-bad" />);
    await clickLightbulb(user, container);

    const generateOutline = screen.getByText('Replace with generated outline');
    await user.click(generateOutline);

    expect(screen.getByText('Error')).toBeInTheDocument();
    expect(screen.getByText(/There was an error generating content/)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument();

    const cancelButton = screen.getByRole('button', { name: 'Close' });
    await user.click(cancelButton);
    expect(screen.queryByText('Error')).not.toBeInTheDocument();
  });

  test('applies streaming class when loading', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345" />);
    await clickLightbulb(user, container);

    const generateOutline = screen.getByText('Replace with generated outline');
    await user.click(generateOutline);

    const mainDiv = container.querySelector('.content-generator-text-area');
    expect(mainDiv).toHaveClass('content-generator-text-area--is-streaming');
  });

  test('does not apply streaming class when error occurs', async () => {
    const user = userEvent.setup();
    const { container } = render(<ContentGeneratorTextArea draftId="12345-bad" />);
    await clickLightbulb(user, container);

    const generateOutline = screen.getByText('Replace with generated outline');
    await user.click(generateOutline);

    const mainDiv = container.querySelector('.content-generator-text-area');
    expect(mainDiv).not.toHaveClass('content-generator-text-area--is-streaming');
  });
});
