/**
 * Document Classifier Agent — @soi96/document-classifier
 *
 * Extracts structured metadata from legal documents using Claude Haiku.
 * Supports Thai and English. Called exclusively by the Client Manager agent.
 *
 * POST /classify  — classify a single document
 * GET  /health    — liveness check
 *
 * Port: 3300 (override with PORT env var)
 */

import Anthropic from '@anthropic-ai/sdk'
import express, { Request, Response } from 'express'
import { z } from 'zod'

// ─── Types ────────────────────────────────────────────────────────────────────

const DOC_TYPES = [
  'contract', 'agreement', 'court_filing', 'pleading', 'affidavit',
  'correspondence', 'identity', 'financial', 'evidence', 'deed',
  'power_of_attorney', 'report', 'opinion', 'permit', 'other',
] as const

type DocType = (typeof DOC_TYPES)[number]

interface ClassifyResult {
  docType: DocType
  parties: string[]
  documentDate: string | null   // ISO date YYYY-MM-DD or null
  summary: string               // 2-3 sentences in English
  suggestedFolder: string       // exact name from available folders
  tags: string[]
  confidence: number            // 0-1
  detectedLanguage: 'th' | 'en' | 'mixed' | 'other'
}

// ─── Request schema ───────────────────────────────────────────────────────────

const ClassifyRequestSchema = z.object({
  documentId: z.string(),
  contentBase64: z.string(),
  mimeType: z.string(),
  fileName: z.string(),
  clientName: z.string().optional(),
  folderNumber: z.string().optional(),
  availableFolders: z.array(z.string()).optional(),
})

type ClassifyRequest = z.infer<typeof ClassifyRequestSchema>

// ─── Anthropic client ─────────────────────────────────────────────────────────

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
})

// ─── Core classification logic ────────────────────────────────────────────────

async function classifyDocument(req: ClassifyRequest): Promise<ClassifyResult> {
  const { contentBase64, mimeType, fileName, clientName, folderNumber, availableFolders } = req

  const folders = availableFolders?.length
    ? availableFolders
    : ['Correspondence', 'Contracts', 'Court Filings', 'Identity', 'Financial', 'Reports', 'Evidence', 'Misc']

  const systemPrompt = `You are a legal document classifier for a law firm in Thailand. You read legal documents in Thai and English and extract structured metadata.

Available filing folders: ${folders.join(', ')}
${clientName ? `Client name: ${clientName}` : ''}
${folderNumber ? `Client folder number: ${folderNumber}` : ''}

Respond ONLY with a single valid JSON object. No markdown, no explanation, no extra text. Use this exact schema:

{
  "docType": "<one of: ${DOC_TYPES.join(' | ')}>",
  "parties": ["<party name>", ...],
  "documentDate": "<YYYY-MM-DD or null>",
  "summary": "<2-3 sentence summary in English describing what this document is and its significance>",
  "suggestedFolder": "<exact folder name from the available list above>",
  "tags": ["<keyword>", ...],
  "confidence": <0.0 to 1.0>,
  "detectedLanguage": "<th | en | mixed | other>"
}

Rules:
- documentDate: the date printed ON the document (not today's date). null if not found.
- parties: extract all named parties (individuals, companies, government bodies).
- suggestedFolder: must exactly match one of the available folder names provided.
- confidence: reflect your certainty about the docType classification.
- For Thai documents, still write the summary in English.`

  // Build the content block based on MIME type.
  // Typed as `any` because the 'document' PDF block type was added to the API
  // after SDK v0.24.0 and is not yet in the generated type definitions.
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let docBlock: any

  if (mimeType === 'application/pdf') {
    docBlock = {
      type: 'document',
      source: { type: 'base64', media_type: 'application/pdf', data: contentBase64 },
    }
  } else if (['image/jpeg', 'image/png', 'image/gif', 'image/webp'].includes(mimeType)) {
    docBlock = {
      type: 'image',
      source: {
        type: 'base64',
        media_type: mimeType,
        data: contentBase64,
      },
    }
  } else {
    // Plain text, .doc, .docx preview, etc. — decode and pass as text
    const text = Buffer.from(contentBase64, 'base64').toString('utf-8')
    docBlock = {
      type: 'text',
      text: `[File: ${fileName}]\n\n${text}`,
    }
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const message = await (anthropic.messages.create as any)({
    model: 'claude-haiku-4-5-20251001',
    max_tokens: 1024,
    system: systemPrompt,
    messages: [
      {
        role: 'user',
        content: [
          docBlock,
          { type: 'text', text: `Classify this document: ${fileName}` },
        ],
      },
    ],
  })

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const rawText: string = message.content.find((b: any) => b.type === 'text')?.text ?? ''

  // Extract JSON — strip any accidental markdown fences
  const jsonMatch = rawText.match(/\{[\s\S]*\}/)
  if (!jsonMatch) {
    throw new Error(`No JSON found in classifier response. Raw: ${rawText.slice(0, 200)}`)
  }

  const parsed = JSON.parse(jsonMatch[0]) as ClassifyResult

  // Validate suggestedFolder falls within available list — fall back to Misc
  if (!folders.includes(parsed.suggestedFolder)) {
    parsed.suggestedFolder = 'Misc'
  }

  // Validate docType
  if (!DOC_TYPES.includes(parsed.docType as DocType)) {
    parsed.docType = 'other'
  }

  return parsed
}

// ─── Express server ───────────────────────────────────────────────────────────

const app = express()
app.use(express.json({ limit: '50mb' }))

app.get('/health', (_req: Request, res: Response) => {
  res.json({ status: 'ok', service: 'document-classifier', model: 'claude-haiku-4-5-20251001' })
})

app.post('/classify', async (req: Request, res: Response) => {
  const parseResult = ClassifyRequestSchema.safeParse(req.body)
  if (!parseResult.success) {
    res.status(400).json({ ok: false, error: parseResult.error.message })
    return
  }

  try {
    const result = await classifyDocument(parseResult.data)
    res.json({ ok: true, documentId: parseResult.data.documentId, result })
  } catch (err: unknown) {
    const message = err instanceof Error ? err.message : String(err)
    console.error(`[classifier] error for ${parseResult.data.documentId}:`, message)
    res.status(500).json({ ok: false, error: message })
  }
})

const PORT = parseInt(process.env.PORT ?? '3300', 10)
app.listen(PORT, () => {
  console.log(`[document-classifier] listening on :${PORT}`)
})
