import Anthropic from '@anthropic-ai/sdk';
import type { CatalogueItem, GenerateResponse } from '../types/index.js';

const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

const CATALOGUE_MAX_ITEMS = 50;

function tokenize(text: string): Set<string> {
  return new Set(
    text.toLowerCase()
      .normalize('NFD').replace(/[̀-ͯ]/g, '')
      .split(/\W+/)
      .filter(w => w.length >= 3)
  );
}

export function filterCatalogue(catalogue: CatalogueItem[], notes: string): CatalogueItem[] {
  if (catalogue.length <= CATALOGUE_MAX_ITEMS) return catalogue;

  const noteTokens = tokenize(notes);
  const scored = catalogue.map(item => {
    const itemTokens = tokenize(`${item.ref} ${item.label}`);
    let score = 0;
    for (const t of itemTokens) {
      if (noteTokens.has(t)) score++;
    }
    return { item, score };
  });

  scored.sort((a, b) => b.score - a.score);
  return scored.slice(0, CATALOGUE_MAX_ITEMS).map(s => s.item);
}

interface Correction {
  designation_ai?:   string;
  designation_user?: string;
  detail_ai?:        string;
  detail_user?:      string;
}

function buildSystemPrompt(catalogue: CatalogueItem[], contexte: string, verbosity: string, corrections: Correction[] = []): string {
  const cataloguePart = catalogue.length > 0
    ? `CATALOGUE DE RÉFÉRENCE (produits/services disponibles) :\n${JSON.stringify(catalogue, null, 2)}\n`
    : `Aucun catalogue fourni — génère des lignes libres (product_ref = null).\n`;

  const verbosityRule = verbosity === 'detailed'
    ? `- designation : titre court mais précis (max 80 car.) mentionnant le type d'intervention et les matériaux si pertinent
- detail : description complète à destination du client (3-5 phrases) : nature exacte des travaux, matériaux ou fournitures utilisés, méthode d'intervention, précautions ou contraintes d'accès. Rédige comme un artisan qui explique son devis.`
    : `- designation : titre court et clair (max 60 car.)
- detail : 1 phrase décrivant l'intervention de façon professionnelle`;

  return `Tu es un expert en chiffrage de travaux de rénovation et maintenance immobilière. Tu rédiges des devis professionnels à partir de notes de visite de chantier.

${cataloguePart}
RÈGLES :
- Chaque tâche identifiée dans les notes = une ligne distincte
- Localisation précise et systématique : "SDB r+1", "cuisine RDC", "chambre parentale", "cage d'escalier", etc.
- ${verbosityRule}
- Si un produit du catalogue correspond → product_ref = sa ref exacte, prix_ht = son prix
- Sinon → product_ref = null, estime un prix HT réaliste aux tarifs du marché artisan français : main d'œuvre 45-70 €/h selon corps de métier, fournitures au prix revendeur + marge 25-35%, pose incluse si "fourniture_pose". Toujours renseigner un prix non nul. flag_incomplet = true pour signaler que c'est une estimation à valider
- type_badge : "forfait_reparation" (main d'œuvre seule), "fourniture_pose" (matériau + pose), "visite_technique" (diagnostic offert)
- visite_technique → prix_ht = 0, offert = true
- Si l'ensemble représente une journée complète → prestation_forfaitaire = true

${contexte ? `CONTEXTE : ${contexte}\n` : ''}${corrections.length > 0 ? buildCorrectionsBlock(corrections) : ''}
SORTIE : JSON pur, sans markdown, sans texte avant ou après. Respecte EXACTEMENT ces noms de clés :

{
  "prestation_forfaitaire": false,
  "forfait_description": null,
  "forfait_prix_ht": null,
  "lignes": [
    {
      "ordre": 1,
      "localisation": "SDB r+1",
      "designation": "Remplacement joint silicone robinet baignoire",
      "detail": "Dépose de l'ancien joint détérioré et application d'un nouveau joint silicone sanitaire sur robinet de baignoire. Nettoyage et dégraissage préalable des surfaces.",
      "type_badge": "forfait_reparation",
      "quantite": 1,
      "unite": "forfait",
      "prix_ht": 85,
      "product_ref": null,
      "offert": false,
      "flag_incomplet": true
    }
  ]
}`;
}

function buildCorrectionsBlock(corrections: Correction[]): string {
  const relevant = corrections.filter(c =>
    (c.designation_ai && c.designation_user && c.designation_ai !== c.designation_user) ||
    (c.detail_ai && c.detail_user && c.detail_ai !== c.detail_user)
  );
  if (relevant.length === 0) return '';

  const examples = relevant.slice(0, 5).map((c, i) => {
    const lines = [`Exemple ${i + 1} :`];
    if (c.designation_ai && c.designation_user && c.designation_ai !== c.designation_user) {
      lines.push(`  designation IA : "${c.designation_ai}"`);
      lines.push(`  designation corrigée : "${c.designation_user}"`);
    }
    if (c.detail_ai && c.detail_user && c.detail_ai !== c.detail_user) {
      lines.push(`  détail IA : "${c.detail_ai.slice(0, 200)}"`);
      lines.push(`  détail corrigé : "${c.detail_user.slice(0, 200)}"`);
    }
    return lines.join('\n');
  }).join('\n\n');

  return `\nCORRECTIONS PASSÉES (exemples de ce que cet utilisateur préfère — adapte ton style en conséquence) :\n${examples}\n`;
}

function buildUserContent(
  notes: string,
  photos: string[]
): Anthropic.MessageParam['content'] {
  const content: Anthropic.ContentBlockParam[] = [];

  for (const photo of photos) {
    const match = photo.match(/^data:(image\/[a-z]+);base64,(.+)$/);
    if (!match) continue;
    const mediaType = match[1] as 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';
    const data      = match[2];
    content.push({
      type:   'image',
      source: { type: 'base64', media_type: mediaType, data },
    });
  }

  const ocrHint = photos.length > 0
    ? '\n\nSi les photos montrent des étiquettes produits, références, cotes ou texte lisible, extrait ces informations et intègre-les dans les lignes du devis (références produits, dimensions, marques).'
    : '';

  content.push({
    type: 'text',
    text: `Notes de visite :\n${notes}${ocrHint}`,
  });

  return content;
}

function stripMarkdown(raw: string): string {
  return raw
    .replace(/^```json\s*/i, '')
    .replace(/^```\s*/i, '')
    .replace(/\s*```\s*$/i, '')
    .trim();
}

export async function generateProposalLines(
  notes: string,
  photos: string[],
  catalogue: CatalogueItem[],
  contexte: string = '',
  verbosity: string = 'concise',
  corrections: Correction[] = []
): Promise<GenerateResponse> {
  const filteredCatalogue = filterCatalogue(catalogue, notes);
  const systemPrompt = buildSystemPrompt(filteredCatalogue, contexte, verbosity, corrections);
  const userContent  = buildUserContent(notes, photos);

  const controller = new AbortController();
  const timeout    = setTimeout(() => controller.abort(), 90_000);

  let message;
  try {
    message = await client.messages.create(
      { model: 'claude-sonnet-4-6', max_tokens: 4096, system: systemPrompt, messages: [{ role: 'user', content: userContent }] },
      { signal: controller.signal }
    );
  } finally {
    clearTimeout(timeout);
  }

  const rawText = message.content
    .filter((b): b is Anthropic.TextBlock => b.type === 'text')
    .map(b => b.text)
    .join('');

  const cleaned = stripMarkdown(rawText);

  let parsed: GenerateResponse;
  try {
    parsed = JSON.parse(cleaned) as GenerateResponse;
  } catch {
    throw Object.assign(new Error('JSON parse failed'), {
      code:    'PARSE_ERROR',
      rawText: rawText.slice(0, 500),
    });
  }

  if (!parsed.lignes || !Array.isArray(parsed.lignes)) {
    throw Object.assign(new Error('Missing lignes array'), {
      code:    'PARSE_ERROR',
      rawText: rawText.slice(0, 500),
    });
  }

  return parsed;
}
