import React from 'react';
import '@testing-library/jest-dom';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter, Route, Routes, useLocation } from 'react-router-dom';
import { ConfirmProvider } from './confirm';
import { PromptProvider } from './prompt';
import { ToastProvider } from './toast';
import { I18nProvider } from './i18n/I18nProvider';
import JobTable from './components/JobTable';
import { api } from './api';

jest.setTimeout(15000);

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

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

const jobRecord = {
  id: 42,
  jobTitle: 'Backend Developer',
  status: 'Waiting',
  dateApplied: new Date().toISOString(),
  daysSince: 10,
  company: { name: 'Acme', recruiterEmail: 'maria@acme.test', recruiterName: 'Maria Recruiter' },
  companyId: 1,
  needsFollowUp: true,
  followUpReason: 'Follow-up due soon',
  shortSummary: 'Strong backend match',
  tailoredCvText: 'Saved CV',
  coverLetterText: 'Saved cover letter',
  recruiterMessageDraft: 'Saved recruiter message',
  notes: 'Original notes\n\n<<<APPLICATION_ANSWER_DRAFT>>>\nSaved application answer\n<<<END_APPLICATION_ANSWER_DRAFT>>>',
  workflowSignal: {
    actionKey: 'follow-up',
    reason: 'Follow-up due soon',
    workspaceTab: 'follow-up',
    followMode: 'waiting-update',
    needsAttention: true,
    hasPackageGap: false,
    needsInterviewPrep: false,
    needsFollowUpAction: true,
    hasTailoredCv: true,
    hasSavedApplicationAnswerDraft: true,
    hasInterviewPrepNotes: false,
  },
};

function LocationIndicator() {
  const location = useLocation();
  return <div data-testid="location-indicator">{location.pathname}{location.search}</div>;
}

function renderLoop(initialPath = '/jobs') {
  return render(
    <ToastProvider>
      <I18nProvider>
        <ConfirmProvider>
          <PromptProvider>
            <MemoryRouter initialEntries={[initialPath]} future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
              <LocationIndicator />
              <Routes>
                <Route path="/jobs" element={<JobTable refreshToken={0} pageSize={15} onPageSizeChange={() => {}} columns={{ status: true, dateApplied: true, daysSince: true, jobUrl: false }} onColumnsChange={() => {}} mode="jobs" />} />
              </Routes>
            </MemoryRouter>
          </PromptProvider>
        </ConfirmProvider>
      </I18nProvider>
    </ToastProvider>,
  );
}

describe('end-to-end trust loop', () => {
  let correspondenceMessages: any[];

  beforeEach(() => {
    Object.assign(navigator, {
      clipboard: {
        writeText: jest.fn().mockResolvedValue(undefined),
      },
    });

    correspondenceMessages = [
      {
        id: 700,
        jobApplicationId: 42,
        from: 'Company',
        content: 'Acme wants to schedule a call.',
        subject: 'Backend Developer interview',
        channel: 'Email',
        date: new Date().toISOString(),
        externalMessageId: 'msg-1',
        externalThreadId: 'thread-1',
        externalFrom: 'Maria Recruiter <maria@acme.test>',
        externalTo: 'user@example.test',
      },
    ];

    mockedApi.get.mockImplementation((url: string) => {
      if (url === '/companies') return Promise.resolve({ data: [{ id: 1, name: 'Acme' }] } as any);
      if (url === '/jobapplications') return Promise.resolve({ data: { items: [jobRecord], total: 1, page: 1, pageSize: 15 } } as any);
      if (url === '/jobapplications/42') return Promise.resolve({ data: jobRecord } as any);
      if (url === '/auth/me') return Promise.resolve({ data: { roles: [], profileCvText: 'Master CV text' } } as any);
      if (url === '/jobapplications/42/history') return Promise.resolve({ data: [] } as any);
      if (url === '/jobapplications/42/tailored-cv-draft') {
        return Promise.resolve({
          data: {
            templateId: 'ats-minimal',
            headline: 'Backend Developer',
            summary: ['Tailored for the Acme backend role'],
            selectedSkills: [],
            experience: [],
            education: [],
            customSections: [],
            status: 'saved',
          },
        } as any);
      }
      if (url === '/attachments/42') return Promise.resolve({ data: [{ id: 9, fileName: 'resume.pdf', uploadDate: new Date().toISOString(), fileType: 'application/pdf', fileSize: 1234, purpose: 'resume', useForAi: true }] } as any);
      if (url === '/correspondence/42') return Promise.resolve({ data: correspondenceMessages } as any);
      if (url === '/gmail/status') return Promise.resolve({ data: { connected: true, gmailAddress: 'user@example.test', lastSyncedAt: new Date().toISOString() } } as any);
      if (url === '/jobapplications/42/followup-draft') {
        return Promise.resolve({
          data: {
            subject: 'Re: Backend Developer application update',
            body: 'Hi Maria,\n\nI wanted to follow up on the Backend Developer thread and reiterate my fit for owning the API layer.\n\nThanks,\nCasey',
            reason: 'Scheduled follow-up is due.',
            suggestedSendOn: new Date().toISOString(),
            contextSummary: 'Scheduled follow-up is due. Saved application package material is available for reuse.',
            contextSignals: ['Saved cover letter available', 'Saved tailored CV available', 'Thread participants: Maria Recruiter <maria@acme.test>, user@example.test'],
            threadSubject: 'Backend Developer application update',
            lastCorrespondenceFrom: 'Maria Recruiter <maria@acme.test>',
            lastCorrespondenceAt: new Date().toISOString(),
          },
        } as any);
      }
      return Promise.resolve({ data: [] } as any);
    });

    mockedApi.post.mockImplementation((url: string, body?: any) => {
      if (url === '/gmail/refresh-linked-threads') {
        const hasReply = correspondenceMessages.some((message) => message.externalMessageId === 'msg-2');
        if (!hasReply) {
          correspondenceMessages = [
            ...correspondenceMessages,
            {
              id: 701,
              jobApplicationId: 42,
              from: 'Me',
              content: 'Following up on the role.',
              subject: 'Backend Developer follow-up',
              channel: 'Email',
              date: new Date().toISOString(),
              externalMessageId: 'msg-2',
              externalThreadId: 'thread-1',
              externalFrom: 'user@example.test',
              externalTo: 'Maria Recruiter <maria@acme.test>',
            },
          ];
        }

        return Promise.resolve({
          data: {
            jobApplicationId: body.jobApplicationId,
            threadsChecked: 1,
            imported: hasReply ? 0 : 1,
            skipped: hasReply ? 2 : 1,
            hasLinkedThreads: true,
            refreshedAt: new Date().toISOString(),
            threads: [
              {
                threadId: 'thread-1',
                imported: hasReply ? 0 : 1,
                skipped: hasReply ? 2 : 1,
                totalMessages: hasReply ? 2 : 2,
                status: hasReply ? 'already-current' : 'imported-new-messages',
                latestMessageDate: new Date().toISOString(),
              },
            ],
          },
        } as any);
      }

      if (url === '/jobapplications/42/send-followup') {
        return Promise.resolve({ data: {} } as any);
      }

      return Promise.resolve({ data: {} } as any);
    });

    mockedApi.put.mockResolvedValue({ data: {} } as any);
    mockedApi.patch.mockResolvedValue({ data: {} } as any);
    mockedApi.delete.mockResolvedValue({ data: {} } as any);
  });

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

  test('overview entry composes saved package reuse, linked-thread continuity, and grounded follow-up drafting without sending mail', async () => {
    renderLoop();

    fireEvent.click(await screen.findByRole('button', { name: /next action: backend developer/i }));

    await waitFor(() => {
      expect(screen.getByTestId('location-indicator')).toHaveTextContent('/jobs');
    });

    expect(await screen.findByText(/follow-up context/i)).toBeInTheDocument();
    expect(await screen.findByText(/saved application package material is available for reuse/i)).toBeInTheDocument();
    expect(await screen.findByText(/manual send boundary/i)).toBeInTheDocument();
    expect(await screen.findByText(/open job email.*connected provider.*final confirmation/i)).toBeInTheDocument();

    fireEvent.click(screen.getByRole('tab', { name: /tailored cv/i }));

    expect((await screen.findAllByDisplayValue(/tailored for the acme backend role/i)).length).toBeGreaterThan(0);
    expect(await screen.findByDisplayValue('Saved cover letter')).toBeInTheDocument();
    expect(await screen.findByDisplayValue('Saved application answer')).toBeInTheDocument();
    expect(await screen.findByDisplayValue('Saved recruiter message')).toBeInTheDocument();
    expect(await screen.findByText(/saved package material feeds follow-up drafting/i)).toBeInTheDocument();
    expect(await screen.findByText(/these saved copies are what follow-up drafting and later slices can trust and reuse/i)).toBeInTheDocument();

    fireEvent.click(screen.getByRole('tab', { name: /correspondence/i }));

    await waitFor(() => {
      expect(mockedApi.post).toHaveBeenCalledWith('/gmail/refresh-linked-threads', { jobApplicationId: 42 });
    });

    expect(await screen.findByText(/linked gmail thread continuity/i)).toBeInTheDocument();
    expect(await screen.findByText(/without re-importing the whole thread/i)).toBeInTheDocument();
    expect(await screen.findByText(/last linked refresh imported 1 new message/i)).toBeInTheDocument();
    expect(await screen.findByText(/backend developer follow-up/i)).toBeInTheDocument();
    expect(await screen.findByText(/following up on the role\./i)).toBeInTheDocument();

    fireEvent.click(screen.getByRole('tab', { name: /follow up/i }));

    expect(await screen.findByDisplayValue(/i wanted to follow up on the backend developer thread/i)).toBeInTheDocument();
    expect(screen.getByRole('link', { name: /open job email/i })).toHaveAttribute('href', '/correspondence');
    expect(mockedApi.post.mock.calls.some(([url]) => url === '/jobapplications/42/send-followup')).toBe(false);
  });
});
