import { describe, expect, it, vi, beforeAll, afterAll, beforeEach } from 'vitest';
import { createServiceApp, type FastifyApp } from '@bytelyst/fastify-core';

vi.mock('../../lib/config.js', () => ({
  config: {
    SERVICE_NAME: 'cowork-service',
    EXTRACTION_SERVICE_URL: 'http://mock-extraction:4005',
  },
}));
vi.mock('../../lib/product-config.js', () => ({
  PRODUCT_ID: 'clawcowork',
}));

import { extractionRoutes } from './routes.js';

let app: FastifyApp;

const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch);

beforeAll(async () => {
  app = await createServiceApp({
    name: 'extraction-routes-test',
    version: '0.0.1',
    logger: false,
  });
  await app.register(extractionRoutes);
});

afterAll(async () => {
  await app.close();
});

beforeEach(() => {
  mockFetch.mockReset();
});

describe('extraction proxy routes', () => {
  describe('POST /api/extract', () => {
    it('proxies extraction request to extraction-service', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ entities: [{ type: 'person', name: 'Alice' }], text: 'Hello Alice' }),
      });

      const res = await app.inject({
        method: 'POST',
        url: '/api/extract',
        payload: { text: 'Hello Alice', task: 'entity-extraction' },
      });

      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.payload);
      expect(body.entities).toHaveLength(1);
      const fetchUrl = mockFetch.mock.calls[0][0] as string;
      expect(fetchUrl).toBe('http://mock-extraction:4005/extract');
    });

    it('returns 502 when extraction-service is unreachable', async () => {
      mockFetch.mockRejectedValue(new Error('ECONNREFUSED'));

      const res = await app.inject({
        method: 'POST',
        url: '/api/extract',
        payload: { text: 'test' },
      });

      expect(res.statusCode).toBe(502);
      const body = JSON.parse(res.payload);
      expect(body.error).toBe('Extraction service unavailable');
    });

    it('forwards extraction-service non-ok status', async () => {
      mockFetch.mockResolvedValue({ ok: false, status: 422 });

      const res = await app.inject({
        method: 'POST',
        url: '/api/extract',
        payload: { text: 'test' },
      });

      expect(res.statusCode).toBe(422);
    });
  });

  describe('GET /api/extract/models', () => {
    it('lists available extraction models', async () => {
      mockFetch.mockResolvedValue({
        ok: true,
        json: async () => ({ models: ['gemini-2.5-flash', 'gpt-4o-mini'], default: 'gemini-2.5-flash' }),
      });

      const res = await app.inject({ method: 'GET', url: '/api/extract/models' });

      expect(res.statusCode).toBe(200);
      const body = JSON.parse(res.payload);
      expect(body.models).toContain('gemini-2.5-flash');
    });

    it('returns 502 when extraction-service is unreachable', async () => {
      mockFetch.mockRejectedValue(new Error('ECONNREFUSED'));

      const res = await app.inject({ method: 'GET', url: '/api/extract/models' });

      expect(res.statusCode).toBe(502);
    });
  });
});
