// PROTO — agent de conceptualisation VALABEL. Génère le brief de concept
// initial d'un drop (silhouette, matières, pièces clés, direction) à partir
// de son thème, pour que le styliste et le graphiste le développent ensuite.
//
// Trois moteurs, tentés dans l'ordre : Claude (si configuré), AICOS en repli
// (si Claude est absent ou échoue), puis un générateur par règles qui
// fonctionne toujours, sans dépendance externe — dernier filet, jamais de
// requête bloquée faute de LLM disponible.

import { getAnthropicClient } from "@/lib/anthropic"
import { callAicosChat, AicosConfigError, AicosBalanceError, AicosRequestError } from "@/lib/aicos"
import { STYLE_VOCAB, MATERIAL_VOCAB, type Facet } from "@/lib/facets"

export interface DropConceptInput {
  name: string
  description?: string | null
}

export interface DropConcept {
  text: string
  source: "llm" | "rules"
}

const PROTO_SYSTEM_PROMPT = `Tu es PROTO, l'agent de conceptualisation de VALABEL, marque de streetwear technique premium (drops limités et numérotés, vente D2C, matières fonctionnelles, Bruxelles).

Ton rôle : à partir du thème d'un drop, rédiger le brief de concept de départ — pas la collection finale — que le styliste (réalisation) et le graphiste (branding) développeront ensuite.

Références déclarées, à utiliser comme direction esthétique : ACRONYM (acrn.com), Guerrilla-Group (guerrilla-group.co), Demobaza (demobaza.com), Boris Bidjan Saberi (borisbidjansaberi.com) — drapé utilitaire, teintures brutes, silhouettes techniques et avant-gardistes.

Réponds en français, en 4 sections courtes avec exactement ces titres :
Silhouette :
Familles de matières :
Pièces clés :
Direction :

Une à deux phrases par section. N'invente pas de noms de personnes, de prix, ni de dates.`

function buildUserPrompt(input: DropConceptInput): string {
  return `Thème du drop : "${input.name}"${
    input.description ? `\nDescription : ${input.description}` : ""
  }\n\nRédige le brief de concept initial.`
}

async function generateWithLLM(input: DropConceptInput): Promise<string | null> {
  const anthropic = await getAnthropicClient()
  if (!anthropic) return null
  try {
    const response = await anthropic.messages.create({
      model: "claude-opus-5",
      max_tokens: 1024,
      system: PROTO_SYSTEM_PROMPT,
      messages: [{ role: "user", content: buildUserPrompt(input) }],
    })
    const text = response.content
      .map((block) => (block.type === "text" ? block.text : ""))
      .join("\n")
      .trim()
    return text || null
  } catch (err) {
    console.error("PROTO (Claude) a échoué, tentative AICOS avant le générateur par règles :", err)
    return null
  }
}

// Repli si Claude n'est pas configuré ou a échoué. Ne remonte jamais
// d'erreur à l'appelant : une config incomplète, un solde insuffisant ou une
// requête mal formée doivent tous se traduire par un log clair (à surveiller
// côté admin) puis un repli sur le générateur par règles — jamais une
// création de drop bloquée faute de LLM disponible.
async function generateWithAicos(input: DropConceptInput): Promise<string | null> {
  try {
    const text = await callAicosChat([
      { role: "system", content: PROTO_SYSTEM_PROMPT },
      { role: "user", content: buildUserPrompt(input) },
    ])
    return text.trim() || null
  } catch (err) {
    if (err instanceof AicosConfigError) {
      // Config absente ou clé invalide : cas normal si AICOS n'est pas
      // configuré, pas la peine d'alerter à chaque création de drop.
      return null
    }
    if (err instanceof AicosBalanceError) {
      console.error("PROTO (AICOS) : solde insuffisant — à recharger côté admin AICOS.", err)
      return null
    }
    if (err instanceof AicosRequestError) {
      console.error("PROTO (AICOS) : requête mal formée — vérifie le modèle configuré dans /admin/settings.", err)
      return null
    }
    console.error("PROTO (AICOS) a échoué, repli sur le générateur par règles :", err)
    return null
  }
}

// ─── Générateur par règles (aucune dépendance externe) ─────────────────────

const KEYWORD_MATERIALS: Record<string, string[]> = {
  mauve: ["scotchlite"],
  violet: ["scotchlite"],
  orange: ["scotchlite"],
  fluo: ["scotchlite", "photoluminescent"],
  phosphor: ["photoluminescent"],
  kaki: ["cordura"],
  khaki: ["cordura"],
  vert: ["photoluminescent"],
  eau: ["neoprene", "econyl"],
  aqua: ["neoprene", "econyl"],
  marin: ["neoprene", "econyl"],
  nuit: ["fil-reflechissant"],
  nocturne: ["fil-reflechissant", "scotchlite"],
  chrome: ["prismatique", "iridescent"],
  metal: ["iridescent"],
  noir: ["neoprene", "graphene"],
  glace: ["aerogel"],
  bio: ["mylo", "pinatex"],
  organique: ["mylo", "pinatex"],
  connect: ["conductive", "nfc"],
  digital: ["conductive"],
}

const DEFAULT_MATERIALS = ["neoprene", "cordura"]
const DEFAULT_STYLES = ["techwear", "urbain"]

function pickMaterials(text: string): string[] {
  const found = new Set<string>()
  for (const [kw, mats] of Object.entries(KEYWORD_MATERIALS)) {
    if (text.includes(kw)) mats.forEach((m) => found.add(m))
  }
  for (const m of MATERIAL_VOCAB) {
    if (text.includes(m.label.toLowerCase())) found.add(m.slug)
  }
  return found.size ? [...found].slice(0, 3) : DEFAULT_MATERIALS
}

function pickStyles(text: string): string[] {
  const found = new Set<string>()
  for (const s of STYLE_VOCAB) {
    if (text.includes(s.label.toLowerCase()) || text.includes(s.slug)) found.add(s.slug)
  }
  return found.size ? [...found].slice(0, 2) : DEFAULT_STYLES
}

function label(slug: string, vocab: Facet[]): string {
  return vocab.find((v) => v.slug === slug)?.label ?? slug
}

function generateRuleBasedConcept(input: DropConceptInput): string {
  const text = `${input.name} ${input.description ?? ""}`.toLowerCase()
  const materials = pickMaterials(text)
  const styles = pickStyles(text)
  const materialLabels = materials.map((m) => label(m, MATERIAL_VOCAB)).join(", ")
  const styleLabels = styles
    .map((s) => label(s, STYLE_VOCAB))
    .join(", ")
    .toLowerCase()

  return [
    `Silhouette : Un vestiaire ${styleLabels}, construit autour d'une seule coupe déclinée en variations, dans l'esprit du drapé utilitaire d'ACRONYM et Demobaza.`,
    `Familles de matières : ${materialLabels}, avec des finitions inspirées des teintures brutes de Boris Bidjan Saberi.`,
    `Pièces clés : un hoodie, une veste technique et une pièce de merchandising — même hiérarchie que les drops précédents.`,
    `Direction : Une identité forte et reconnaissable, pensée pour tenir sur deux ou trois pièces plutôt qu'une collection large — brief de départ pour le styliste et le graphiste.`,
  ].join("\n\n")
}

// ─── Point d'entrée ─────────────────────────────────────────────────────────

export async function generateDropConcept(input: DropConceptInput): Promise<DropConcept> {
  const claudeText = await generateWithLLM(input)
  if (claudeText) return { text: claudeText, source: "llm" }

  const aicosText = await generateWithAicos(input)
  if (aicosText) return { text: aicosText, source: "llm" }

  return { text: generateRuleBasedConcept(input), source: "rules" }
}
