import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { randomUUID } from "node:crypto";
import { NextResponse } from "next/server";
import { getDbPool } from "@/db";
import { analyzeVehiclePhoto } from "@/lib/openai-vision";
import { evaluateInspectionRules } from "@/lib/risk-engine";
import { getCapturePolicy } from "@/lib/capture-policy";
import { compactIdentifier, normalizePlateForDeclared, platesMatch } from "@/lib/vehicle-identifiers";

export const runtime = "nodejs";

async function getLink(token: string) {
  const db = getDbPool();
  const [rows] = await db.execute(
    `SELECT l.id AS link_id, l.inspection_id, l.inspection_cycle AS link_cycle, l.expires_at, l.revoked_at,
            i.external_id, i.tenant_id, i.vehicle_type, i.plate, i.chassis, i.vehicle_description, i.status, i.inspection_cycle
     FROM inspection_access_links l
     INNER JOIN inspections i ON i.id = l.inspection_id
     WHERE l.token = ? LIMIT 1`,
    [token],
  );
  return (rows as Record<string, unknown>[])[0];
}

function validLink(link: Record<string, unknown> | undefined) {
  return link && !link.revoked_at && Number(link.link_cycle) === Number(link.inspection_cycle) && new Date(String(link.expires_at)).getTime() > Date.now();
}

export async function GET(_: Request, { params }: { params: Promise<{ token: string }> }) {
  try {
    const { token } = await params;
    const link = await getLink(token);
    if (!validLink(link)) return NextResponse.json({ error: "Este link não é mais válido." }, { status: 410 });
    const db = getDbPool();
    await db.execute("UPDATE inspection_access_links SET last_accessed_at = NOW() WHERE id = ?", [String(link.link_id)]);
    const [photos] = await db.execute(
      "SELECT category_code, ai_status FROM inspection_photos WHERE inspection_id = ? AND inspection_cycle = ? AND ai_status <> 'retriage' ORDER BY created_at",
      [String(link.inspection_id), Number(link.link_cycle)],
    );
    return NextResponse.json({ inspection: { proposal: link.external_id, vehicleType: link.vehicle_type, plate: link.plate, vehicle: link.vehicle_description }, photos });
  } catch (error) {
    console.error("Erro ao abrir link de vistoria", error);
    return NextResponse.json({ error: "Não foi possível abrir a vistoria." }, { status: 500 });
  }
}

export async function POST(request: Request, { params }: { params: Promise<{ token: string }> }) {
  try {
    const { token } = await params;
    const link = await getLink(token);
    if (!validLink(link)) return NextResponse.json({ error: "Este link não é mais válido." }, { status: 410 });
    const form = await request.formData();
    const category = String(form.get("category") || "").trim();
    const image = form.get("image");
    const latitude = Number(form.get("latitude"));
    const longitude = Number(form.get("longitude"));
    if (!category || !(image instanceof File) || !image.type.startsWith("image/")) {
      return NextResponse.json({ error: "Envie uma foto válida e informe a categoria." }, { status: 400 });
    }
    if (!Number.isFinite(latitude) || !Number.isFinite(longitude) || latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) {
      return NextResponse.json({ error: "A localização é obrigatória. Autorize o acesso à localização do aparelho e tente novamente." }, { status: 400 });
    }
    if (image.size > 10 * 1024 * 1024) return NextResponse.json({ error: "A foto excede o limite de 10 MB." }, { status: 413 });

    const extension = image.type === "image/png" ? "png" : image.type === "image/webp" ? "webp" : "jpg";
    const photoId = randomUUID();
    const storageKey = path.posix.join("inspections", String(link.inspection_id), `${photoId}.${extension}`);
    const storageRoot = process.env.STORAGE_PATH || "/app/storage";
    const diskPath = path.join(storageRoot, storageKey);
    await mkdir(path.dirname(diskPath), { recursive: true });
    await writeFile(diskPath, Buffer.from(await image.arrayBuffer()));

    const db = getDbPool();
    await db.execute(
      "INSERT INTO inspection_photos (id, inspection_id, inspection_cycle, category_code, storage_key, latitude, longitude, capture_source, ai_status) VALUES (?, ?, ?, ?, ?, ?, ?, 'camera', 'pending')",
      [photoId, String(link.inspection_id), Number(link.link_cycle), category, storageKey, latitude, longitude],
    );
    await db.execute("UPDATE inspections SET status = 'in_progress' WHERE id = ? AND status IN ('received', 'link_sent')", [String(link.inspection_id)]);
    let analysisMessage = "Foto recebida. A análise de IA está em processamento.";
    try {
      const { model, result } = await analyzeVehiclePhoto({ image: Buffer.from(await image.arrayBuffer()), mimeType: image.type, category, vehicleType: String(link.vehicle_type), plate: String(link.plate), chassis: String(link.chassis) });
      const policy = await getCapturePolicy(String(link.tenant_id));
      const categoryName = category.toLocaleLowerCase("pt-BR");
      const isPlatePhoto = categoryName === "placa";
      const isExteriorPlateCheckPhoto = categoryName === "dianteira" || categoryName === "traseira";
      const isPlateCheckPhoto = isPlatePhoto || isExteriorPlateCheckPhoto;
      const isChassisPhoto = categoryName.includes("chassi");
      const plateRead = normalizePlateForDeclared(result.ocrPlate, String(link.plate));
      const chassisRead = compactIdentifier(result.ocrChassis);
      const declaredPlate = compactIdentifier(String(link.plate));
      const declaredChassis = compactIdentifier(String(link.chassis));
      const plateMatch = isPlateCheckPhoto && plateRead ? platesMatch(plateRead, declaredPlate) ? "matched" : "mismatched" : isPlateCheckPhoto ? "not_visible" : "not_applicable";
      const chassisMatch = isChassisPhoto && declaredChassis && chassisRead ? chassisRead === declaredChassis ? "matched" : "mismatched" : isChassisPhoto && chassisRead ? "not_applicable" : isChassisPhoto ? "not_visible" : "not_applicable";
      const plateUnreadable = isPlatePhoto && plateMatch === "not_visible";
      const plateMismatch = isPlateCheckPhoto && plateMatch === "mismatched";
      const chassisUnreadable = isChassisPhoto && chassisMatch === "not_visible";
      const softQualityAlert = result.status === "retriage";
      const qualityNeedsRetake = softQualityAlert && policy.qualityAction === "retriage";
      const plateNeedsRetake = plateUnreadable && policy.plateUnreadableAction === "retriage";
      // Placa divergente é um bloqueio de decisão, nunca de continuidade do roteiro.
      const approvalBlockedByPlate = plateMismatch && policy.plateMismatchAction === "block_approval";
      const plateWasNormalizedToMatch = isPlateCheckPhoto && result.plateMatch === "mismatched" && plateMatch === "matched";
      const requiresHuman = (result.requiresHuman && !plateWasNormalizedToMatch)
        || softQualityAlert
        || (plateUnreadable && policy.plateUnreadableAction === "human_review")
        || (plateMismatch && policy.plateMismatchAction === "human_review")
        || (isChassisPhoto && (chassisMatch === "mismatched" || chassisUnreadable));
      const needsRetake = qualityNeedsRetake || plateNeedsRetake;
      const analysisStatus = requiresHuman ? "requires_human" : "completed";
      const photoStatus = needsRetake ? "retriage" : (requiresHuman || approvalBlockedByPlate) ? "flagged" : result.status;
      const captureSummary = plateMismatch
        ? approvalBlockedByPlate
          ? `A placa lida (${result.ocrPlate || "não identificada"}) é diferente da informada (${link.plate}). Você pode concluir as fotos, mas a aprovação ficará impedida conforme a regra deste cliente.`
          : `A placa lida (${result.ocrPlate || "não identificada"}) é diferente da informada (${link.plate}). Você pode continuar; a vistoria será encaminhada para análise humana.`
        : plateUnreadable && plateNeedsRetake
          ? "Não foi possível ler todos os caracteres da placa traseira. Refaça a foto aproximando a placa e mantendo também o QR Code visível, se houver."
          : plateUnreadable
            ? "A placa traseira não pôde ser lida com segurança. A foto foi aceita para análise humana; você pode continuar o roteiro."
            : chassisUnreadable
              ? "Leitura do chassi inconclusiva. A foto foi aceita para análise humana, que verificará visualmente sinais de raspagem ou adulteração."
              : softQualityAlert && !qualityNeedsRetake
                ? `${result.summary} A foto foi aceita com alerta e seguirá para conferência humana; você pode continuar o roteiro.`
                : result.summary;
      const captureFindings = [
        ...result.findings.filter((finding) => !(plateWasNormalizedToMatch && finding.toLocaleLowerCase("pt-BR").includes("placa divergente"))),
        ...(plateMismatch ? ["placa divergente"] : []),
        ...(chassisMatch === "mismatched" ? ["chassi divergente"] : []),
      ];
      await db.execute("INSERT INTO inspection_photo_analyses (id, inspection_photo_id, provider, status, confidence, summary, findings, requires_human, ocr_plate, plate_match, ocr_chassis, chassis_match) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [randomUUID(), photoId, model, requiresHuman ? "requires_human" : analysisStatus, result.confidence, captureSummary, JSON.stringify(captureFindings), requiresHuman, isPlateCheckPhoto ? plateRead || null : null, plateMatch, isChassisPhoto ? result.ocrChassis : null, chassisMatch]);
      await db.execute("UPDATE inspection_photos SET ai_quality_score = ?, ai_status = ? WHERE id = ?", [result.qualityScore, photoStatus, photoId]);
      analysisMessage = needsRetake ? `Refaça esta foto antes de continuar. ${captureSummary}` : approvalBlockedByPlate ? `Foto recebida. ${captureSummary}` : requiresHuman ? `Foto recebida e encaminhada para conferência humana. ${captureSummary}` : "Foto recebida e aprovada na triagem por IA.";
      const routes: Record<string, number> = { light: 8, motorcycle: 6, heavy: 6 };
      const [countRows] = await db.execute("SELECT COUNT(*) AS total FROM inspection_photos WHERE inspection_id = ? AND inspection_cycle = ? AND ai_status <> 'retriage'", [String(link.inspection_id), Number(link.link_cycle)]);
      const captured = Number((countRows as { total: number }[])[0]?.total || 0);
      if (captured >= (routes[String(link.vehicle_type)] || 99)) {
        try {
          const evaluation = await evaluateInspectionRules(String(link.tenant_id), String(link.external_id));
          analysisMessage += ` Risco calculado: ${evaluation.score} pontos.`;
        } catch (ruleError) {
          console.error("A análise foi concluída, mas o motor de regras não pôde ser aplicado", ruleError);
          analysisMessage += " A análise foi salva; o cálculo de risco poderá ser executado no painel.";
        }
      }
    } catch (analysisError) {
      const reason = analysisError instanceof Error ? analysisError.message : "Falha desconhecida";
      await db.execute("INSERT INTO inspection_photo_analyses (id, inspection_photo_id, provider, status, summary, findings, requires_human) VALUES (?, ?, 'openai', 'failed', ?, JSON_ARRAY(), FALSE)", [randomUUID(), photoId, `Falha técnica na análise automática: ${reason.slice(0, 600)}`]);
      await db.execute("UPDATE inspection_photos SET ai_status = 'retriage' WHERE id = ?", [photoId]);
      analysisMessage = "A análise automática ficou indisponível antes de avaliar esta foto. Isso não é uma reprovação de foco, iluminação ou enquadramento. Aguarde alguns instantes e tente novamente; se persistir, acione o suporte.";
      console.error("Erro na análise de IA", analysisError);
    }
    const [latestRows] = await db.execute("SELECT ai_status FROM inspection_photos WHERE id = ?", [photoId]);
    const latest = (latestRows as { ai_status: string }[])[0];
    return NextResponse.json({ id: photoId, message: analysisMessage, captureAccepted: latest?.ai_status !== "retriage" }, { status: 201 });
  } catch (error) {
    console.error("Erro ao receber foto", error);
    return NextResponse.json({ error: "Não foi possível armazenar esta foto." }, { status: 500 });
  }
}
