/**
 * Expense OCR pipeline — expenses-module.
 * Calls Claude Vision via callAI (use case: expense_ocr) to extract receipt fields.
 * Seeded canonical accounting fields: expenseDate, amount (ILS-normalized).
 */
import type { Env } from '@zync/types'
import type { Expense } from '@zync/types'
import { callAI } from '@zync/ai'
import type { AICtx } from '@zync/ai'
import { convertAmount } from '@zync/db/queries'
import { OCR_SYSTEM_PROMPT, buildOcrUserPrompt } from './prompts'
import { ExpenseProcessingError } from './errors'

export interface OcrResult {
  vendorName?: string
  vendorTaxId?: string
  invoiceNumber?: string
  receiptDate?: string
  invoiceTotal?: number
  /** ILS-normalized VAT for storage (foreign receipts converted via convertAmount). */
  vatAmount?: string
  currency?: string
  allocationNumber?: string
  rawOcrText: string
  ocrConfidence: number
  // Canonical accounting fields derived from OCR
  expenseDate?: string
  amount?: string
  /** False for foreign-currency receipts (no reclaimable Israeli input VAT). */
  vatDeductible?: boolean
  /** True when a foreign-currency amount could not be converted to ILS. */
  fxRateMissing?: boolean
}

interface RawOcrResponse {
  vendor_name?: string
  vendor_tax_id?: string
  invoice_number?: string
  receipt_date?: string
  invoice_total?: number
  vat_amount?: number
  currency?: string
  allocation_number?: string
  raw_ocr_text?: string
  confidence?: number
}

export async function runOcr(
  ctx: AICtx,
  expense: Expense,
  fileBytes: ArrayBuffer,
  mediaType: string,
): Promise<OcrResult> {
  // Encode file as base64
  const base64 = Buffer.from(fileBytes).toString('base64')

  const response = await callAI(ctx, {
    tenantId: expense.tenantId,
    userId: expense.createdBy,
    useCase: 'expense_ocr',
    entityType: 'expense',
    entityId: expense.id,
    tier: 'business', // Tier passed from queue consumer
    messages: [
      {
        role: 'user',
        content: [
          {
            type: 'image' as const,
            image: {
              mediaType: mediaType as string,
              data: base64,
            },
          },
          {
            type: 'text',
            text: buildOcrUserPrompt(expense.fileName),
          },
        ],
      },
    ],
    maxTokens: 1024,
  })

  // Parse JSON response
  let parsed: RawOcrResponse
  try {
    const text = response.content
    // Strip markdown code fences if present
    const cleaned = text.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim()
    parsed = JSON.parse(cleaned) as RawOcrResponse
  } catch {
    throw new ExpenseProcessingError('OCR response was not valid JSON', 'terminal')
  }

  const rawOcrText = parsed.raw_ocr_text ?? ''
  const ocrConfidence = typeof parsed.confidence === 'number' ? parsed.confidence : computeConfidence(parsed)

  // Seed canonical accounting fields
  const expenseDate = parsed.receipt_date ?? undefined
  const currency = parsed.currency ?? 'ILS'
  const isForeign = currency !== 'ILS'
  let fxRateMissing = false

  let amount: string | undefined
  if (typeof parsed.invoice_total === 'number') {
    if (!isForeign) {
      amount = String(parsed.invoice_total)
    } else {
      const ilsAmount = await convertAmount(
        ctx.db,
        expense.tenantId,
        String(parsed.invoice_total),
        currency,
        'ILS',
        expenseDate,
      )
      if (ilsAmount != null) {
        amount = ilsAmount
      } else {
        fxRateMissing = true
      }
    }
  }

  let vatAmount: string | undefined
  if (typeof parsed.vat_amount === 'number') {
    if (!isForeign) {
      vatAmount = String(parsed.vat_amount)
    } else {
      const ilsVat = await convertAmount(
        ctx.db,
        expense.tenantId,
        String(parsed.vat_amount),
        currency,
        'ILS',
        expenseDate,
      )
      if (ilsVat != null) {
        vatAmount = ilsVat
      } else {
        fxRateMissing = true
      }
    }
  }

  return {
    vendorName: parsed.vendor_name,
    vendorTaxId: parsed.vendor_tax_id,
    invoiceNumber: parsed.invoice_number,
    receiptDate: parsed.receipt_date,
    invoiceTotal: parsed.invoice_total,
    vatAmount,
    currency,
    allocationNumber: parsed.allocation_number,
    rawOcrText,
    ocrConfidence,
    expenseDate,
    amount,
    vatDeductible: isForeign ? false : undefined,
    fxRateMissing: fxRateMissing || undefined,
  }
}

function computeConfidence(parsed: RawOcrResponse): number {
  // Compute completeness-based confidence
  const fields = [
    parsed.vendor_name,
    parsed.invoice_total,
    parsed.receipt_date,
    parsed.vat_amount,
  ]
  const filled = fields.filter(Boolean).length
  return Math.max(0.5, filled / fields.length)
}
