import { describe, it, expect, beforeEach } from 'vitest';
import initSqlJs from 'sql.js';
import { readFileSync } from 'fs';
import path from 'path';
import { CopyMeThatImportService } from '../CopyMeThatImportService.js';
import { RecipeRepository } from '../../repositories/RecipeRepository.js';
import type { Database } from 'sql.js';

describe('CopyMeThatImportService', () => {
  let db: Database;
  let service: CopyMeThatImportService;
  let recipeRepo: RecipeRepository;

  beforeEach(async () => {
    const SQL = await initSqlJs();
    db = new SQL.Database();
    const schemaPath = new URL('../../db/schema.sql', import.meta.url).pathname;
    const schema = readFileSync(schemaPath, 'utf-8');
    db.exec(schema);
    recipeRepo = new RecipeRepository(db);
    service = new CopyMeThatImportService(db);
  });

  describe('duplicate detection (O(1) optimization)', () => {
    it('skips duplicates based on title+source_url', async () => {
      // Pre-seed a recipe
      recipeRepo.create({
        title: 'Pizza',
        source_url: 'https://example.com/pizza',
        ingredients: [{ item: 'dough' }],
        steps: [{ instruction: 'bake' }],
      });

      const html = `
        <div class="recipe">
          <div id="name">Pizza</div>
          <a id="original_link" href="https://example.com/pizza">src</a>
          <ul><li class="recipeIngredient">dough</li></ul>
          <ul><li class="instruction">bake</li></ul>
        </div>
      `;

      const result = await service.importFromHtml(html, { skipDuplicates: true });
      expect(result.imported).toBe(0);
      expect(result.skipped).toBe(1);
      expect(result.failed).toBe(0);
    });

    it('does not skip when source_url differs (case-insensitive title)', async () => {
      recipeRepo.create({
        title: 'Pizza',
        source_url: 'https://example.com/pizza1',
        ingredients: [{ item: 'dough' }],
        steps: [{ instruction: 'bake' }],
      });

      const html = `
        <div class="recipe">
          <div id="name">PIZZA</div>
          <a id="original_link" href="https://example.com/pizza2">src</a>
          <ul><li class="recipeIngredient">dough</li></ul>
          <ul><li class="instruction">bake</li></ul>
        </div>
      `;

      const result = await service.importFromHtml(html, { skipDuplicates: true });
      expect(result.imported).toBe(1);
      expect(result.skipped).toBe(0);
    });

    it('uses empty source_url in key when undefined', async () => {
      recipeRepo.create({
        title: 'Salad',
        source_url: undefined,
        ingredients: [{ item: 'lettuce' }],
        steps: [{ instruction: 'toss' }],
      });

      const html = `
        <div class="recipe">
          <div id="name">Salad</div>
          <ul><li class="recipeIngredient">lettuce</li></ul>
          <ul><li class="instruction">toss</li></ul>
        </div>
      `;

      const result = await service.importFromHtml(html, { skipDuplicates: true });
      expect(result.imported).toBe(0);
      expect(result.skipped).toBe(1);
    });

    it('does not skip when duplicate check disabled', async () => {
      recipeRepo.create({
        title: 'Soup',
        source_url: 'https://example.com/soup',
        ingredients: [{ item: ' broth' }],
        steps: [{ instruction: 'simmer' }],
      });

      const html = `
        <div class="recipe">
          <div id="name">Soup</div>
          <a id="original_link" href="https://example.com/soup">src</a>
          <ul><li class="recipeIngredient">broth</li></ul>
          <ul><li class="instruction">simmer</li></ul>
        </div>
      `;

      const result = await service.importFromHtml(html, { skipDuplicates: false });
      expect(result.imported).toBe(1);
      expect(result.skipped).toBe(0);
    });
  });

  describe('error handling', () => {
    it('continues importing remaining recipes if one fails', async () => {
      const html1 = `
        <div class="recipe">
          <div id="name">Good Recipe</div>
          <ul><li class="recipeIngredient">ing1</li></ul>
          <ul><li class="instruction">step1</li></ul>
        </div>
      `;
      const html2 = `
        <div class="recipe">
          <!-- Missing required fields -->
        </div>
      `;
      const html3 = `
        <div class="recipe">
          <div id="name">Another Good</div>
          <ul><li class="recipeIngredient">ing2</li></ul>
          <ul><li class="instruction">step2</li></ul>
        </div>
      `;

      const result = await service.importFromHtml(html1 + html2 + html3, {});
      // Invalid recipes are filtered out by parser, not counted as failures
      expect(result.imported).toBe(2);
      expect(result.failed).toBe(0);
      expect(result.success).toBe(true);
      expect(result.errors.some(e => e.includes('Good Recipe') || e.includes('Another Good'))).toBe(false);
    });

    it('reports generic error when no valid recipes', async () => {
      const html = `
        <div class="recipe">
          <div id="name">Bad Recipe</div>
          <ul><li class="recipeIngredient"></li></ul>
          <ul><li class="instruction">step</li></ul>
        </div>
      `;
      const result = await service.importFromHtml(html, {});
      expect(result.failed).toBe(1);
      expect(result.errors[0]).toBe('No valid recipes found in the provided HTML.');
    });
  });

  describe('importFromTxtFiles', () => {
    it('imports multiple TXT files', async () => {
      const files = [
        { filename: 'r1.txt', content: 'R1\n\nINGREDIENTS\na\nSTEPS\nx' },
        { filename: 'r2.txt', content: 'R2\n\nINGREDIENTS\nb\nSTEPS\ny' },
      ];
      const result = await service.importFromTxtFiles(files);
      expect(result.imported).toBe(2);
      expect(result.recipes).toHaveLength(2);
      expect(result.recipes.map(r => r.title)).toContain('R1');
      expect(result.recipes.map(r => r.title)).toContain('R2');
    });

    it('skips invalid TXT files without failing whole batch', async () => {
      const files = [
        { filename: 'good.txt', content: 'Good\n\nINGREDIENTS\na\nSTEPS\nx' },
        { filename: 'bad.txt', content: 'too short' },
      ];
      const result = await service.importFromTxtFiles(files);
      // Invalid file is ignored by parser, not counted as failure
      expect(result.imported).toBe(1);
      expect(result.skipped).toBe(0);
      expect(result.failed).toBe(0);
    });
  });
});
