import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import express from 'express';
import request from 'supertest';
import { createImportRoutes } from '../routes/import.js';

describe('Import API', () => {
  let app: express.Application;

  beforeEach(() => {
    app = express();
    app.use(express.json());
    app.use('/api/import', createImportRoutes());
  });

  afterEach(() => {
    vi.unstubAllGlobals();
    vi.restoreAllMocks();
  });

  it('imports from URL via schema.org JSON-LD', async () => {
    const html = `
      <html>
        <head>
          <script type="application/ld+json">
            {
              "@context": "https://schema.org",
              "@type": "Recipe",
              "name": "Test Pasta",
              "description": "Quick pasta",
              "recipeIngredient": ["200g pasta", "1 tbsp olive oil"],
              "recipeInstructions": ["Boil water", "Cook pasta"],
              "url": "https://example.com/test-pasta"
            }
          </script>
        </head>
      </html>
    `;

    vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(html, {
      status: 200,
      headers: { 'content-type': 'text/html; charset=utf-8' },
    })));

    const response = await request(app)
      .post('/api/import/url')
      .send({ url: 'https://example.com/test-pasta' })
      .expect(200);

    expect(response.body.success).toBe(true);
    expect(response.body.data.draft_recipe.title).toBe('Test Pasta');
    expect(response.body.data.draft_recipe.ingredients).toHaveLength(2);
    expect(response.body.data.draft_recipe.instructions).toEqual(['Boil water', 'Cook pasta']);
    expect(response.body.data.parse.schema_org_used).toBe(true);
    expect(response.body.data.parse.heuristic_used).toBe(false);
  });

  it('validates URL request payload', async () => {
    const response = await request(app)
      .post('/api/import/url')
      .send({ url: 'not-a-url' })
      .expect(400);

    expect(response.body.success).toBe(false);
    expect(String(response.body.error).toLowerCase()).toContain('valid url');
  });

  it('maps timeout errors to 504', async () => {
    const abortError = new Error('aborted');
    abortError.name = 'AbortError';

    vi.stubGlobal('fetch', vi.fn().mockRejectedValue(abortError));

    const response = await request(app)
      .post('/api/import/url')
      .send({ url: 'https://example.com/slow' })
      .expect(504);

    expect(response.body.success).toBe(false);
    expect(String(response.body.error).toLowerCase()).toContain('timed out');
  });

  it('maps network errors to 502', async () => {
    vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('network failed')));

    const response = await request(app)
      .post('/api/import/url')
      .send({ url: 'https://example.com/unreachable' })
      .expect(502);

    expect(response.body.success).toBe(false);
    expect(String(response.body.error).toLowerCase()).toContain('network error');
  });

  it('maps unsupported content type errors to 415', async () => {
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response('{"ok":true}', {
      status: 200,
      headers: { 'content-type': 'application/json' },
    })));

    const response = await request(app)
      .post('/api/import/url')
      .send({ url: 'https://example.com/api-endpoint' })
      .expect(415);

    expect(response.body.success).toBe(false);
    expect(String(response.body.error).toLowerCase()).toContain('html page');
  });
});
