/**
 * Optional LLM extraction provider (OFF by default).
 *
 * Configured entirely via runtimeConfig.ap.llm (env-backed): provider, baseUrl,
 * apiKey, model. Built against an OpenAI-compatible chat/JSON interface so it
 * works with OpenAI, OpenRouter, a local Ollama/vLLM server, or Anthropic
 * (native Messages API). The API key stays server-side. Fail-soft: any error
 * returns an empty extraction so the pipeline keeps the self-hosted result.
 */

import type { ExtractionProvider, ExtractionInput } from './types'
import type { ExtractionResult } from '../fieldExtractors'
import type { ZugferdInvoiceData } from '../zugferd/buildInvoiceXml'

const SYSTEM_PROMPT = `You extract structured data from a single supplier invoice.
Return ONLY a compact JSON object, no prose, with these keys (omit unknown ones):
documentNo (string), issueDate (yyyy-mm-dd), dueDate (yyyy-mm-dd), currency (ISO e.g. EUR),
sellerName, sellerVatId, iban, grandTotal (number, gross), netTotal (number),
taxTotal (number), taxRate (number, percent), isTaxIncluded (boolean),
lines (array of {name, qty, netPrice, lineTotal, taxRate}).
Use dot as decimal separator. Do not invent values you cannot find.`

const LLM_CONFIDENCE = 0.85

const mapJsonToResult = (j: any): ExtractionResult => {
  const fields: Partial<ZugferdInvoiceData> = {}
  const confidence: Record<string, number> = {}
  const put = (key: string, v: any) => { if (v !== undefined && v !== null && v !== '') { (fields as any)[key] = v; confidence[key] = LLM_CONFIDENCE } }

  put('documentNo', j.documentNo)
  put('issueDate', j.issueDate)
  put('dueDate', j.dueDate)
  put('currency', j.currency || 'EUR')
  if (typeof j.grandTotal === 'number') put('grandTotal', j.grandTotal)
  if (typeof j.netTotal === 'number') { put('lineTotal', j.netTotal); (fields as any).taxBasisTotal = j.netTotal }
  if (typeof j.taxTotal === 'number') put('taxTotal', j.taxTotal)
  if (typeof j.isTaxIncluded === 'boolean') put('isTaxIncluded', j.isTaxIncluded)

  const seller: any = {}
  if (j.sellerName) seller.name = j.sellerName
  if (j.sellerVatId) seller.vatId = j.sellerVatId
  if (Object.keys(seller).length) { fields.seller = seller; if (seller.name) confidence['seller.name'] = LLM_CONFIDENCE; if (seller.vatId) confidence['seller.vatId'] = LLM_CONFIDENCE }
  if (j.iban) { fields.paymentIban = String(j.iban).replace(/\s+/g, ''); confidence['paymentIban'] = LLM_CONFIDENCE }

  if (typeof j.taxRate === 'number' || typeof j.taxTotal === 'number') {
    fields.taxes = [{ rate: Number(j.taxRate) || 0, amount: Number(j.taxTotal) || 0, basis: Number(j.netTotal) || 0, category: (Number(j.taxRate) || 0) > 0 ? 'S' : 'Z' }]
    confidence['taxes'] = LLM_CONFIDENCE
  }
  if (Array.isArray(j.lines)) {
    fields.lines = j.lines.map((l: any, i: number) => ({
      lineId: i + 1, name: String(l.name || '-'), qty: Number(l.qty) || 1, unitCode: 'C62',
      netPrice: Number(l.netPrice) || 0, lineTotal: Number(l.lineTotal) || 0,
      taxRate: Number(l.taxRate) || Number(j.taxRate) || 0, taxCategory: (Number(l.taxRate) || 0) > 0 ? 'S' : 'Z'
    }))
    confidence['lines'] = LLM_CONFIDENCE
  }
  return { fields, confidence, bbox: {} }
}

const extractJson = (raw: string): any => {
  if (!raw) return null
  const fence = raw.match(/```(?:json)?\s*([\s\S]*?)```/i)
  const candidate = fence ? fence[1] : raw
  const start = candidate.indexOf('{')
  const end = candidate.lastIndexOf('}')
  if (start === -1 || end === -1) return null
  try { return JSON.parse(candidate.slice(start, end + 1)) } catch { return null }
}

export const createLlmProvider = (cfg: { provider: string; baseUrl: string; apiKey: string; model: string }): ExtractionProvider => ({
  name: `llm:${cfg.provider}`,
  async extract(input: ExtractionInput): Promise<ExtractionResult> {
    const empty: ExtractionResult = { fields: {}, confidence: {}, bbox: {} }
    const userText = (input.text || '').slice(0, 16000)
    if (!userText.trim()) return empty
    try {
      if (cfg.provider === 'anthropic') {
        const res: any = await $fetch(`${cfg.baseUrl.replace(/\/$/, '')}/v1/messages`, {
          method: 'POST',
          headers: { 'x-api-key': cfg.apiKey, 'anthropic-version': '2023-06-01', 'content-type': 'application/json' },
          body: {
            model: cfg.model,
            max_tokens: 1500,
            system: SYSTEM_PROMPT,
            messages: [{ role: 'user', content: `Invoice text:\n\n${userText}` }]
          }
        })
        const text = Array.isArray(res?.content) ? res.content.map((c: any) => c.text || '').join('') : ''
        const json = extractJson(text)
        return json ? mapJsonToResult(json) : empty
      }
      // OpenAI-compatible (openai | compatible | ollama)
      const res: any = await $fetch(`${cfg.baseUrl.replace(/\/$/, '')}/chat/completions`, {
        method: 'POST',
        headers: { Authorization: `Bearer ${cfg.apiKey}`, 'content-type': 'application/json' },
        body: {
          model: cfg.model,
          temperature: 0,
          messages: [
            { role: 'system', content: SYSTEM_PROMPT },
            { role: 'user', content: `Invoice text:\n\n${userText}` }
          ]
        }
      })
      const text = res?.choices?.[0]?.message?.content || ''
      const json = extractJson(text)
      return json ? mapJsonToResult(json) : empty
    } catch (e: any) {
      console.warn('[Inbox] LLM provider failed (keeping self-hosted result):', e?.message || e)
      return empty
    }
  }
})

export default createLlmProvider
