import React from 'react';
import '@testing-library/jest-dom';
import { fireEvent, render, screen } from '@testing-library/react';
import { ConfirmProvider } from './confirm';
import { PromptProvider } from './prompt';
import { ToastProvider } from './toast';
import { I18nProvider } from './i18n/I18nProvider';
import JobDetailsDialog from './components/JobDetailsDialog';
import { api } from './api';

jest.setTimeout(15000);

jest.mock('./api', () => ({
  api: {
    get: jest.fn(),
    post: jest.fn(() => Promise.resolve({ data: {} })),
    put: jest.fn(() => Promise.resolve({ data: {} })),
    patch: jest.fn(() => Promise.resolve({ data: {} })),
    delete: jest.fn(() => Promise.resolve({ data: {} })),
    interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } },
  },
}));

const mockedApi = api as jest.Mocked<typeof api>;

const matchScore = {
  score: 82,
  band: 'Strong',
  matchedCount: 4,
  totalKeywords: 6,
  matchedKeywords: ['C#', '.NET', 'SQL', 'Docker'],
  missingKeywords: ['Kubernetes', 'GraphQL'],
  sectionCoverage: [
    { section: 'Skills', matched: 4, total: 6 },
    { section: 'Experience', matched: 3, total: 6 },
  ],
  hasEnoughSignal: true,
  learningRecommendations: [
    { id: 71, keyword: 'Kubernetes', status: 'pending' },
    { id: 72, keyword: 'GraphQL', status: 'pending' },
  ],
};

function renderDialog() {
  return render(
    <ToastProvider>
      <I18nProvider>
        <ConfirmProvider>
          <PromptProvider>
            <JobDetailsDialog open jobId={42} onClose={() => {}} initialTab={5} />
          </PromptProvider>
        </ConfirmProvider>
      </I18nProvider>
    </ToastProvider>,
  );
}

beforeEach(() => {
  mockedApi.get.mockImplementation((url: string) => {
    if (url === '/jobapplications/42') {
      return Promise.resolve({ data: { id: 42, jobTitle: 'Backend Developer', status: 'Applied', dateApplied: new Date().toISOString(), daysSince: 3, company: { name: 'Acme' } } } as any);
    }
    if (url === '/jobapplications/42/match-score') {
      return Promise.resolve({ data: matchScore } as any);
    }
    if (url === '/auth/me') return Promise.resolve({ data: { roles: [] } } as any);
    if (url === '/jobapplications/42/history') return Promise.resolve({ data: [] } as any);
    if (url === '/attachments/42') return Promise.resolve({ data: [] } as any);
    // Candidate-fit AI narrative: leave pending-ish/empty so we only assert on the fast panel.
    if (url === '/jobapplications/42/candidate-fit') return Promise.resolve({ data: null } as any);
    return Promise.resolve({ data: {} } as any);
  });
});

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

test('match score panel shows the score and honest important-term labels', async () => {
  renderDialog();

  expect(await screen.findByText('82%')).toBeInTheDocument();
  expect(await screen.findByText(/strong match/i)).toBeInTheDocument();
  expect(await screen.findByText('4/6 important terms')).toBeInTheDocument();
  expect(await screen.findByText('Important terms already in your CV')).toBeInTheDocument();
  expect(await screen.findByText('Important terms from the job to review')).toBeInTheDocument();
  expect(screen.queryByText(/matched keywords/i)).not.toBeInTheDocument();

  // Matched keyword chips
  expect(await screen.findByText('C#')).toBeInTheDocument();
  expect(await screen.findByText('Docker')).toBeInTheDocument();

  // Missing keyword chips
  expect((await screen.findAllByText('Kubernetes')).length).toBeGreaterThan(0);
  expect((await screen.findAllByText('GraphQL')).length).toBeGreaterThan(0);

  // Section coverage
  expect(await screen.findByText('Skills: 4/6')).toBeInTheDocument();
});

test('learning recommendations can be completed or dismissed', async () => {
  renderDialog();

  fireEvent.click((await screen.findAllByRole('button', { name: /mark learned/i }))[0]);
  expect(mockedApi.patch).toHaveBeenCalledWith('/jobapplications/42/checklist/71', { status: 'done' });
  expect(await screen.findByText('Learned')).toBeInTheDocument();

  fireEvent.click(screen.getAllByRole('button', { name: /dismiss/i })[0]);
  expect(mockedApi.patch).toHaveBeenCalledWith('/jobapplications/42/checklist/72', { status: 'dismissed' });
  expect(await screen.findByText('1 dismissed')).toBeInTheDocument();
});

test('match score panel degrades gracefully when there is not enough signal', async () => {
  mockedApi.get.mockImplementation((url: string) => {
    if (url === '/jobapplications/42') {
      return Promise.resolve({ data: { id: 42, jobTitle: 'Backend Developer', status: 'Applied', dateApplied: new Date().toISOString(), daysSince: 3, company: { name: 'Acme' } } } as any);
    }
    if (url === '/jobapplications/42/match-score') {
      return Promise.resolve({ data: { ...matchScore, score: 0, band: 'Unknown', matchedCount: 0, matchedKeywords: [], missingKeywords: [], sectionCoverage: [], hasEnoughSignal: false } } as any);
    }
    if (url === '/auth/me') return Promise.resolve({ data: { roles: [] } } as any);
    if (url === '/jobapplications/42/history') return Promise.resolve({ data: [] } as any);
    if (url === '/attachments/42') return Promise.resolve({ data: [] } as any);
    if (url === '/jobapplications/42/candidate-fit') return Promise.resolve({ data: null } as any);
    return Promise.resolve({ data: {} } as any);
  });

  renderDialog();

  expect(await screen.findByText('—')).toBeInTheDocument();
  expect(await screen.findByText(/not enough signal/i)).toBeInTheDocument();
});
