import Fastify from 'fastify';
import { mkdtemp } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { FileQueueStore } from '@bytelyst/queue';

vi.mock('../../lib/python-bridge.js', () => ({
  sidecarExtract: vi.fn(),
  sidecarExtractBatch: vi.fn(),
  sidecarHealth: vi.fn(),
}));

vi.mock('../../lib/errors.js', () => ({
  BadRequestError: class BadRequestError extends Error {},
}));

vi.mock('./sidecar-monitor.js', () => ({
  startHealthMonitoring: vi.fn(),
  getHealthState: vi.fn(),
  getHealthSummary: vi.fn(),
  checkHealthNow: vi.fn(),
}));

import { extractRoutes } from './routes.js';
import { sidecarExtract } from '../../lib/python-bridge.js';
import { resetJobStore, setJobStoreForTesting } from './jobs.js';

const mockSidecarExtract = vi.mocked(sidecarExtract);

describe('extract jobs e2e', () => {
  let app: ReturnType<typeof Fastify> | undefined;
  let queueFilePath = '';

  beforeEach(async () => {
    const dir = await mkdtemp(join(tmpdir(), 'extract-jobs-e2e-'));
    queueFilePath = join(dir, 'jobs.json');
    process.env.EXTRACTION_QUEUE_BACKEND = 'file';
    process.env.EXTRACTION_QUEUE_FILE = queueFilePath;
    process.env.EXTRACTION_QUEUE_POLL_MS = '5';
    mockSidecarExtract.mockReset();
  });

  afterEach(async () => {
    if (app) {
      await app.close();
      app = undefined;
    }
    await resetJobStore();
    await setJobStoreForTesting(undefined);
  });

  it('persists async jobs across app restart', async () => {
    mockSidecarExtract.mockResolvedValue({
      extractions: [{ extraction_class: 'topic', extraction_text: 'queue' }],
      metadata: { model_id: 'gemini', duration_ms: 20, char_count: 10 },
    });

    await setJobStoreForTesting(new FileQueueStore({ filePath: queueFilePath }));
    app = Fastify({ logger: false });
    await app.register(extractRoutes, { prefix: '/api' });

    const createResponse = await app.inject({
      method: 'POST',
      url: '/api/extract/jobs',
      payload: {
        inputs: [{ text: 'queue durability test', taskId: 'triage' }],
      },
    });

    expect(createResponse.statusCode).toBe(202);
    const created = createResponse.json() as { jobId: string };

    await vi.waitFor(async () => {
      const response = await app!.inject({
        method: 'GET',
        url: `/api/extract/jobs/${created.jobId}`,
      });
      expect(response.statusCode).toBe(200);
      expect((response.json() as { status: string }).status).toBe('completed');
    });

    await app.close();
    app = undefined;

    await setJobStoreForTesting(new FileQueueStore({ filePath: queueFilePath }));
    app = Fastify({ logger: false });
    await app.register(extractRoutes, { prefix: '/api' });

    const persistedResponse = await app.inject({
      method: 'GET',
      url: `/api/extract/jobs/${created.jobId}`,
    });

    expect(persistedResponse.statusCode).toBe(200);
    expect(persistedResponse.json()).toMatchObject({
      id: created.jobId,
      status: 'completed',
      progress: { completed: 1, total: 1 },
      results: [
        {
          extractions: [{ extraction_class: 'topic', extraction_text: 'queue' }],
        },
      ],
    });
  });
});
