import db from '@adonisjs/lucid/services/db'
import ImportBatch from '#models/import_batch'
import ImportDraft from '#models/import_draft'
import Client from '#models/client'
import Plan from '#models/plan'
import { getOcrProvider } from '#services/ocr/index'
import type { OcrResult } from '#services/ocr/ocr_provider'

export type DraftFields = {
  clientId: string | null
  clientName: string
  clientEmail: string | null
  numero: string
  amountTtcCents: number
  issueDate: string
  dueDate: string
  planId: string | null
}

export type DraftConfidence = Partial<{
  clientId: number
  clientName: number
  clientEmail: number
  numero: number
  amountTtcCents: number
  issueDate: number
  dueDate: number
  planId: number
}>

/**
 * Une "source" de draft : un filename + (optionnellement) une storageKey
 * MinIO du PDF stocké. Mock OCR ignore storageKey, Mistral l'exige.
 */
export type ImportSource = {
  filename: string
  storageKey: string | null
}

/**
 * Compose `extracted` + `confidence` à partir du résultat OCR. Tente un
 * match client immédiat (case-insensitive) pour pré-remplir `clientId`.
 */
async function buildDraftFromOcr(
  organizationId: string,
  ocr: OcrResult,
  defaultPlanId: string | null
): Promise<{ extracted: DraftFields; confidence: DraftConfidence }> {
  const matchedClient = await Client.query()
    .where('organization_id', organizationId)
    .whereILike('name', ocr.fields.clientName.value)
    .first()

  return {
    extracted: {
      clientId: matchedClient?.id ?? null,
      clientName: matchedClient?.name ?? ocr.fields.clientName.value,
      clientEmail: matchedClient?.email ?? ocr.fields.clientEmail.value,
      numero: ocr.fields.numero.value,
      amountTtcCents: ocr.fields.amountTtcCents.value,
      issueDate: ocr.fields.issueDate.value,
      dueDate: ocr.fields.dueDate.value,
      planId: defaultPlanId,
    },
    confidence: {
      clientName: matchedClient ? 1 : ocr.fields.clientName.confidence,
      clientEmail: ocr.fields.clientEmail.confidence,
      numero: ocr.fields.numero.confidence,
      amountTtcCents: ocr.fields.amountTtcCents.confidence,
      issueDate: ocr.fields.issueDate.confidence,
      dueDate: ocr.fields.dueDate.confidence,
      planId: 1,
    },
  }
}

/**
 * Crée un batch + N drafts à partir de N sources (filename + storageKey).
 * Le provider OCR (mock ou mistral) est résolu à l'intérieur.
 *
 * - Mock : storageKey=null OK, extraction depuis filename
 * - Mistral : storageKey requis, extraction depuis le PDF stocké
 */
export async function createImportBatch(
  organizationId: string,
  sources: ImportSource[]
): Promise<ImportBatch> {
  const ocr = getOcrProvider()

  // Plan par défaut = premier is_default de l'org (provisionné au signup).
  const defaultPlan = await Plan.query()
    .where('organization_id', organizationId)
    .where('is_default', true)
    .orderBy('name', 'asc')
    .first()

  // OCR fait HORS transaction (calls réseau lents, on ne tient pas de lock
  // PG pendant). Si l'OCR échoue, l'erreur remonte avant le INSERT.
  type DraftPayload = {
    filename: string
    storageKey: string | null
    extracted: DraftFields
    edited: DraftFields
    confidence: DraftConfidence
  }
  const drafts: DraftPayload[] = []

  for (const src of sources) {
    const result = await ocr.extract(src)
    const { extracted, confidence } = await buildDraftFromOcr(
      organizationId,
      result,
      defaultPlan?.id ?? null
    )
    drafts.push({
      filename: src.filename,
      storageKey: src.storageKey,
      extracted,
      edited: { ...extracted },
      confidence,
    })
  }

  return db.transaction(async (trx) => {
    const batch = await ImportBatch.create({ organizationId }, { client: trx })

    for (const d of drafts) {
      await ImportDraft.create(
        {
          batchId: batch.id,
          filename: d.filename,
          pdfStorageKey: d.storageKey,
          extracted: d.extracted,
          edited: d.edited,
          confidence: d.confidence,
          status: 'pending',
          invoiceId: null,
        },
        { client: trx }
      )
    }

    await batch.load('drafts')
    return batch
  })
}

/**
 * Wrapper compat : V1 mock JSON `{filenames}` → sources avec storageKey null.
 * @deprecated Préférer `createImportBatch` avec sources explicites.
 */
export async function createImportBatchFromFilenames(
  organizationId: string,
  filenames: string[]
): Promise<ImportBatch> {
  return createImportBatch(
    organizationId,
    filenames.map((filename) => ({ filename, storageKey: null }))
  )
}
