type VisionResult = {
  qualityScore: number;
  status: "accepted" | "retriage" | "flagged";
  confidence: number;
  summary: string;
  findings: string[];
  requiresHuman: boolean;
  ocrPlate: string | null;
  plateMatch: "matched" | "mismatched" | "not_visible" | "not_applicable";
  ocrChassis: string | null;
  chassisMatch: "matched" | "mismatched" | "not_visible" | "not_applicable";
};

const matchValues = ["matched", "mismatched", "not_visible", "not_applicable"];
const resultSchema = { type: "object", additionalProperties: false, required: ["qualityScore", "status", "confidence", "summary", "findings", "requiresHuman", "ocrPlate", "plateMatch", "ocrChassis", "chassisMatch"], properties: { qualityScore: { type: "integer", minimum: 0, maximum: 100 }, status: { type: "string", enum: ["accepted", "retriage", "flagged"] }, confidence: { type: "integer", minimum: 0, maximum: 100 }, summary: { type: "string", maxLength: 700 }, findings: { type: "array", maxItems: 6, items: { type: "string", maxLength: 180 } }, requiresHuman: { type: "boolean" }, ocrPlate: { type: ["string", "null"], maxLength: 20 }, plateMatch: { type: "string", enum: matchValues }, ocrChassis: { type: ["string", "null"], maxLength: 40 }, chassisMatch: { type: "string", enum: matchValues } } };

function asResult(value: unknown): VisionResult {
  const data = value as Partial<VisionResult>;
  const score = Math.max(0, Math.min(100, Number(data.qualityScore)));
  const confidence = Math.max(0, Math.min(100, Number(data.confidence)));
  if (!Number.isFinite(score) || !Number.isFinite(confidence) || !["accepted", "retriage", "flagged"].includes(String(data.status)) || typeof data.summary !== "string" || !Array.isArray(data.findings) || typeof data.requiresHuman !== "boolean" || !matchValues.includes(String(data.plateMatch)) || !matchValues.includes(String(data.chassisMatch))) throw new Error("A IA retornou um formato de análise inválido.");
  const findings = data.findings.map(String).slice(0, 6);
  if (data.chassisMatch === "mismatched" && !findings.some((item) => item.toLowerCase().includes("chassi divergente"))) findings.push("chassi divergente");
  if (data.plateMatch === "mismatched" && !findings.some((item) => item.toLowerCase().includes("placa divergente"))) findings.push("placa divergente");
  return { qualityScore: score, confidence, status: data.status as VisionResult["status"], summary: data.summary, findings, requiresHuman: data.requiresHuman || data.chassisMatch === "mismatched", ocrPlate: typeof data.ocrPlate === "string" ? data.ocrPlate : null, plateMatch: data.plateMatch as VisionResult["plateMatch"], ocrChassis: typeof data.ocrChassis === "string" ? data.ocrChassis : null, chassisMatch: data.chassisMatch as VisionResult["chassisMatch"] };
}

/** Analisa uma foto no servidor. A chave da OpenAI nunca é enviada ao navegador. */
export async function analyzeVehiclePhoto(args: { image: Buffer; mimeType: string; category: string; vehicleType: string; plate: string; chassis: string }) {
  const apiKey = process.env.OPENAI_API_KEY;
  if (!apiKey) throw new Error("OPENAI_API_KEY não está configurada no servidor.");
  const model = process.env.OPENAI_VISION_MODEL || "gpt-5.4-mini";
  const prompt = `Você faz a triagem e a pré-análise técnica de fotos para uma vistoria veicular brasileira. Analise somente a foto recebida. Categoria solicitada: ${args.category}. Tipo de veículo: ${args.vehicleType}. Placa declarada: ${args.plate}. Chassi declarado: ${args.chassis || "não informado"}.

Primeiro valide se a CAPTURA serve para a vistoria. Retorne status "retriage" somente quando a foto não permitir avaliar o item solicitado: imagem muito desfocada, muito escura/estourada, item cortado, ângulo errado, assunto ausente ou identificador obrigatório ilegível. Uma foto externa com pequenas sombras, reflexos comuns, cenário externo ou enquadramento não perfeito continua aceita se o veículo e a área solicitada estiverem visíveis. Não recuse uma foto boa apenas porque ela não está em condição de estúdio. Quando retornar retriage, escreva em summary uma orientação curta de nova captura e deixe findings vazio.

Se a captura for utilizável, faça a ANÁLISE DO VEÍCULO. Em fotos externas (dianteira, traseira e laterais), examine pintura, para-choques, para-lamas, portas, capô, tampa traseira, lanternas/faróis, rodas e alinhamento aparente. Procure amassados, riscos/arranhões, trincas, peças desalinhadas, corrosão ou danos estruturais. Não invente avarias por sombra, reflexo, sujeira ou baixa resolução. Em summary, descreva apenas o estado do veículo ou escreva "Nenhuma avaria visível identificada nesta imagem."; nunca use summary para elogiar nitidez, iluminação ou enquadramento. Em findings, registre somente achados técnicos reais, com gravidade e local quando possível: por exemplo "avaria leve: arranhão superficial no para-lama traseiro esquerdo", "avaria moderada: amassado na porta dianteira direita" ou "avaria grave: para-choque rachado". Não inclua itens de qualidade da foto em findings. Use status "flagged" para um achado técnico que não exige nova foto; use requiresHuman apenas quando houver indício grave, possível fraude/adulteração ou dúvida material sobre o achado.

Regra obrigatória de identificação: quando a categoria solicitada for "Placa", "Dianteira" ou "Traseira", leia a placa se ela estiver visível. Retorne plateMatch "matched" apenas quando estiver segura da leitura; "mismatched" apenas quando os caracteres forem claramente diferentes; "not_visible" se não for possível ler. Não classifique como divergente por ambiguidade visual entre O e 0. Na categoria "Placa", a placa traseira precisa estar visível e legível; para placas Mercosul, enquadre também o QR Code, pois ele é evidência futura. Divergência de placa nunca exige refazer a foto: use flagged e requiresHuman quando estiver clara. Nas demais categorias, retorne plateMatch "not_applicable" e ocrPlate null. A mesma regra vale para a categoria que contenha "Chassi": leia e compare todo o identificador, usando chassisMatch; para outras categorias, chassisMatch deve ser "not_applicable" e ocrChassis null. Chassi parcialmente legível, mas sem sinais de raspagem ou adulteração, pode ser aceito para análise humana; só exija retriagem se a imagem não permitir verificar minimamente a integridade visual. Em findings, use frases curtas em português. Quando aplicável, prefira estes termos: "chassi divergente", "chassi adulterado", "identificador divergente", "placa divergente", "avaria leve", "avaria moderada", "avaria grave", "dano grave", "longarina", "estrutura comprometida", "airbag acionado" ou "fraude".`;
  const response = await fetch("https://api.openai.com/v1/chat/completions", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }, body: JSON.stringify({ model, reasoning_effort: "minimal", max_completion_tokens: 1800, response_format: { type: "json_schema", json_schema: { name: "vehicle_photo_triage", strict: true, schema: resultSchema } }, messages: [{ role: "user", content: [{ type: "text", text: prompt }, { type: "image_url", image_url: { url: `data:${args.mimeType};base64,${args.image.toString("base64")}`, detail: "high" } }] }] }) });
  if (!response.ok) throw new Error(`A IA recusou a análise (${response.status}): ${(await response.text()).slice(0, 350)}`);
  const body = await response.json() as { choices?: { finish_reason?: string | null; message?: { content?: string | { text?: string }[]; refusal?: string } }[] };
  const choice = body.choices?.[0];
  const message = choice?.message;
  const content = typeof message?.content === "string" ? message.content : Array.isArray(message?.content) ? message.content.map((item) => item.text || "").join("") : "";
  if (!content) throw new Error(message?.refusal ? `A IA recusou a análise: ${message.refusal}` : `A IA não retornou conteúdo para a análise${choice?.finish_reason ? ` (finalização: ${choice.finish_reason})` : ""}.`);
  return { model, result: asResult(JSON.parse(content)) };
}
