/**
 * Bundle (BOM) product image — orchestration.
 *
 *   1. component photos: Strapi m-product of every BOM component (Main_Image or first image),
 *      bytes via fetchStrapiFileBytes;
 *   2. style plan: Claude (vision) looks at the component photos + names/quantities and answers
 *      a small JSON plan (backdrop colours in the context of the photos, accent, arrangement,
 *      cards, per-photo trim). Without ANTHROPIC_API_KEY, on any API error or unparsable
 *      answer the NEUTRAL_PLAN is used — image generation never fails because of the AI step;
 *   3. render (bundleImageCompose.ts), upload to Strapi linked to the bundle product's `image`
 *      relation (existing pictures stay) and set it as Main_Image.
 *
 * Claude analyses pictures — it does not paint them; the picture itself is composed here.
 */
import Anthropic from '@anthropic-ai/sdk'
import strapiHelper from '../strapiHelper'
import { fetchStrapiFileBytes } from '../inbox/strapiAttach'
import { cfg, anthropicClient, anthropicErrorMessage } from '../leads/leadAssessment'
import { composeBundleImage, hasRealTransparency, previewForVision, normalizePlan, NEUTRAL_PLAN, MAX_REPEAT_TILES } from './bundleImageCompose'
import type { BundleTileSource, BundlePlan } from './bundleImageCompose'

export interface BundleComponentInput { productId: number; name: string; value: string; qty: number; strapiDocumentId: string }
export interface BundleProductInput { id: number; name: string; value: string; strapiId: string | number; strapiDocumentId: string }

export interface BundleImageResult {
  status: number
  message: string
  imageId?: number
  url?: string
  hash?: string
  ext?: string
  formats?: any
  plan?: BundlePlan
  planSource: 'claude' | 'neutral'
  model?: string
  usage?: { input: number; output: number }
  tiles?: number
  repeated?: boolean
  warnings: string[]
}

const pickMainImage = (strapiProduct: any): any | null => {
  const images: any[] = Array.isArray(strapiProduct?.image) ? strapiProduct.image : []
  if (!images.length) return null
  const main = strapiProduct?.Main_Image && strapiProduct.Main_Image != 0 ? images.find(i => String(i.id) === String(strapiProduct.Main_Image)) : null
  return main || images[0]
}

/** Component photo bytes (null when the component has no Strapi record / picture). */
const loadComponentPhoto = async (event: any, strapiDocumentId: string, warnings: string[], label: string): Promise<Buffer | null> => {
  if (!strapiDocumentId) { warnings.push(`${label}: kein Medien-Datensatz (Strapi)`); return null }
  try {
    const res: any = await strapiHelper(event, `m-products/${strapiDocumentId}?populate=image`, 'GET', null)
    const img = pickMainImage(res?.data)
    if (!img?.url) { warnings.push(`${label}: kein Bild hinterlegt`); return null }
    const buf = await fetchStrapiFileBytes(event, img.url)
    if (!buf?.length) { warnings.push(`${label}: Bilddatei nicht erreichbar`); return null }
    return buf
  } catch (e: any) {
    warnings.push(`${label}: ${e?.message || 'Bild konnte nicht geladen werden'}`)
    return null
  }
}

// ---- Claude style plan ---------------------------------------------------------------
export const PLAN_SYSTEM = `Du bist Art Director für E-Commerce-Produktbilder. Du erhältst die Komponentenfotos eines Produkt-Bundles (Stückliste) und entscheidest den Stil des zusammengesetzten Bundle-Bildes. Du malst nichts selbst – ein Renderer setzt die Fotos auf eine quadratische Fläche: pro Komponente ein Feld (bei kleinen Mengen mehrfach, sonst mit Mengen-Badge). Antworte ausschließlich mit einem JSON-Objekt, ohne Markdown und ohne Erklärtext.`

export const buildPlanInstruction = (components: { index: number; name: string; qty: number; hasPhoto: boolean; transparent: boolean }[], productName: string): string => {
  const list = components.map(c => `${c.index}. ${c.name} – Menge ${c.qty}${c.hasPhoto ? (c.transparent ? ' (Foto mit Transparenz / Freisteller)' : ' (Foto mit eigenem Hintergrund)') : ' (kein Foto → Platzhalter)'}`).join('\n')
  return `Bundle: „${productName}“
Komponenten:
${list}

Regeln für deine Entscheidung:
- Der Hintergrund soll zum Stil der Fotos passen (Farbwelt, Helligkeit, Stimmung). Sind die Fotos neutral/weiß oder fehlen sie, bleibe neutral (Weiß bis sehr helles Grau, dezenter Verlauf).
- Fotos mit eigenem opakem Hintergrund wirken nur auf hellem Grund oder auf weißen Karten gut → dann tileCard: true und einen hellen Hintergrund wählen. Freisteller können direkt auf einen Farbverlauf.
- accent ist die Farbe der Mengen-Badges (z. B. „10×“); badgeText muss darauf gut lesbar sein.
- arrangement "row" nur bei höchstens 4 Feldern, sonst "grid".
- trim pro Komponente: true, wenn ein gleichmäßiger Rand/Hintergrund weggeschnitten werden darf, damit das Objekt das Feld füllt; false bei Fotos, die bis zum Rand Motiv zeigen.

JSON-Format (exakt diese Schlüssel):
{"background":{"type":"solid"|"gradient","colors":["#rrggbb","#rrggbb"],"angle":0-359},"accent":"#rrggbb","badgeText":"#rrggbb","arrangement":"grid"|"row","tileCard":true|false,"components":[{"index":1,"trim":true|false}],"rationale":"ein kurzer Satz"}`
}

export const parsePlanJson = (text: string): any | null => {
  const s = String(text || '')
  const start = s.indexOf('{'), end = s.lastIndexOf('}')
  if (start < 0 || end <= start) return null
  try { return JSON.parse(s.slice(start, end + 1)) } catch { return null }
}

const planWithClaude = async (sources: BundleTileSource[], productName: string, warnings: string[]): Promise<{ plan: BundlePlan | null; model?: string; usage?: { input: number; output: number } }> => {
  const { apiKey, model } = cfg()
  if (!apiKey) { warnings.push('ANTHROPIC_API_KEY nicht konfiguriert – neutraler Stil'); return { plan: null } }
  try {
    const client = anthropicClient(90 * 1000)
    const content: Anthropic.ContentBlockParam[] = []
    const meta: { index: number; name: string; qty: number; hasPhoto: boolean; transparent: boolean }[] = []
    for (const s of sources) {
      meta.push({ index: s.index, name: s.name, qty: s.qty, hasPhoto: !!s.buffer, transparent: !!s.buffer && !s.opaque })
      if (s.buffer) {
        const prev = await previewForVision(s.buffer)
        content.push({ type: 'text', text: `Foto Komponente ${s.index}: ${s.name}` })
        content.push({ type: 'image', source: { type: 'base64', media_type: 'image/jpeg', data: prev.toString('base64') } })
      }
    }
    content.push({ type: 'text', text: buildPlanInstruction(meta, productName) })
    const res = await client.messages.create({
      model,
      max_tokens: 1500,
      system: PLAN_SYSTEM,
      output_config: { effort: 'medium' },
      messages: [{ role: 'user', content }]
    } as any)
    const usage = { input: Number((res as any)?.usage?.input_tokens || 0), output: Number((res as any)?.usage?.output_tokens || 0) }
    if ((res as any).stop_reason === 'refusal') { warnings.push('KI-Stilvorschlag abgelehnt – neutraler Stil'); return { plan: null, model, usage } }
    const text = (res.content || []).filter((b: any) => b.type === 'text').map((b: any) => b.text).join('\n')
    const raw = parsePlanJson(text)
    if (!raw) { warnings.push('KI-Antwort nicht lesbar – neutraler Stil'); return { plan: null, model, usage } }
    return { plan: normalizePlan(raw, sources.length), model, usage }
  } catch (e: any) {
    warnings.push(anthropicErrorMessage(e) || e?.message || 'KI nicht erreichbar – neutraler Stil')
    return { plan: null }
  }
}

// ---- Strapi upload --------------------------------------------------------------------
const uploadBundleImage = async (event: any, product: BundleProductInput, buffer: Buffer): Promise<any> => {
  const config: any = useRuntimeConfig()
  const uploadBase = (config.api?.strapiupload || (config.public?.strapi || config.api?.strapi || '').replace(/\/media-api\/?$/, '')).replace(/\/$/, '')
  const stamp = new Date().toISOString().slice(0, 10)
  const fd: any = new FormData()
  fd.append('field', 'image')
  fd.append('ref', 'api::m-product.m-product')
  fd.append('refId', String(product.strapiId))
  fd.append('files', new Blob([buffer], { type: 'image/jpeg' }), `bundle_${product.value || product.id}_${stamp}.jpg`)
  fd.append('fileInfo', JSON.stringify({ caption: String(product.id), alternativeText: `Bundle ${product.value || ''} ${product.name || ''}`.trim() }))
  const up: any = await $fetch(`${uploadBase}/api/upload`, { method: 'POST', headers: { Authorization: `Bearer ${config.api?.strapitoken}` }, body: fd })
  return Array.isArray(up) ? up[0] : (up?.[0] ?? up)
}

// ---- entry point ----------------------------------------------------------------------
export const generateBundleImage = async (event: any, product: BundleProductInput, components: BundleComponentInput[]): Promise<BundleImageResult> => {
  const warnings: string[] = []
  const comps = components.filter(c => Number(c.qty) > 0)
  if (!comps.length) return { status: 422, message: 'Die Stückliste hat keine Komponenten mit Menge > 0', planSource: 'neutral', warnings }
  if (!product.strapiId || !product.strapiDocumentId) return { status: 422, message: 'Der Artikel hat keinen Medien-Datensatz (Strapi) – bitte zuerst ein Bild in der Galerie hochladen', planSource: 'neutral', warnings }

  const sources: BundleTileSource[] = []
  for (let i = 0; i < comps.length; i++) {
    const c = comps[i]
    const label = c.value ? `${c.value} ${c.name}` : c.name
    const buffer = await loadComponentPhoto(event, c.strapiDocumentId, warnings, label)
    const opaque = buffer ? !(await hasRealTransparency(buffer)) : true
    sources.push({ index: i + 1, name: c.name, value: c.value, qty: Number(c.qty), buffer, opaque })
  }
  if (!sources.some(s => s.buffer)) warnings.push('Keine Komponente hat ein Bild – es werden Platzhalter gezeigt')

  const { plan, model, usage } = await planWithClaude(sources, product.name, warnings)
  const effective = plan || NEUTRAL_PLAN
  const rendered = await composeBundleImage(sources, effective)

  let file: any
  try {
    file = await uploadBundleImage(event, product, rendered.buffer)
  } catch (e: any) {
    const detail = e?.data ?? e?.response?._data ?? e?.message
    console.error('[bundle-image] Strapi upload failed:', typeof detail === 'string' ? detail.slice(0, 500) : JSON.stringify(detail || {}).slice(0, 500))
    return { status: 502, message: 'Upload des Bundle-Bildes nach Strapi fehlgeschlagen', planSource: plan ? 'claude' : 'neutral', warnings }
  }
  if (!file?.id) return { status: 502, message: 'Strapi hat keine Datei-ID zurückgegeben', planSource: plan ? 'claude' : 'neutral', warnings }

  try {
    await strapiHelper(event, `m-products/${product.strapiDocumentId}`, 'PUT', { data: { Main_Image: file.id } })
  } catch (e: any) {
    warnings.push('Bild hochgeladen, aber Hauptbild konnte nicht gesetzt werden: ' + (e?.message || ''))
  }

  return {
    status: 200, message: '', imageId: file.id, url: file.url, hash: file.hash, ext: file.ext, formats: file.formats,
    plan: normalizePlan(effective, sources.length), planSource: plan ? 'claude' : 'neutral', model, usage,
    tiles: rendered.tiles, repeated: rendered.repeated, warnings
  }
}

export { MAX_REPEAT_TILES }
