import { Router } from "express";
import multer from "multer";
import fs from "node:fs/promises";
import path from "node:path";
import { env } from "../../config/env.js";
import { recordAppEvent } from "../../lib/app-log.js";
import { ensureReceiptStorage, receiptsStorageDir, isFileEncryptionEnabled, readEncryptedFile, writeEncryptedFile } from "../../lib/storage.js";
import { analyzeReceiptPdfFile } from "./receipt-analysis.js";
import { analyzeReceiptPagesWithOpenAi, analyzeReceiptWithOpenAi, isReceiptAiEnabled } from "./receipt-intelligence.js";

function extensionFromMime(mimetype: string): string {
  switch (mimetype) {
    case "application/pdf":
      return ".pdf";
    case "image/jpeg":
      return ".jpg";
    case "image/png":
      return ".png";
    case "image/gif":
      return ".gif";
    case "image/webp":
      return ".webp";
    case "image/heic":
    case "image/heif":
      return ".heic";
    default:
      return "";
  }
}

const storage = multer.diskStorage({
  destination: (_req, _file, cb) => {
    ensureReceiptStorage();
    cb(null, receiptsStorageDir);
  },
  filename: (_req, file, cb) => {
    const ext = path.extname(file.originalname).toLowerCase() || extensionFromMime(file.mimetype) || ".jpg";
    cb(null, `${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`);
  },
});

const upload = multer({
  storage,
  limits: { fileSize: 10 * 1024 * 1024 },
  fileFilter: (_req, file, cb) => {
    const allowedExt = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".pdf", ".heic", ".heif"];
    const allowedMime = ["image/jpeg", "image/png", "image/gif", "image/webp", "application/pdf", "image/heic", "image/heif"];
    const ext = path.extname(file.originalname).toLowerCase();
    if (allowedExt.includes(ext) || allowedMime.includes(file.mimetype)) {
      cb(null, true);
      return;
    }
    cb(new Error("Formato no permitido. Usa JPG, PNG, WEBP o PDF."));
  },
});

export const receiptsRouter = Router();

const receiptAiRequests = new Map<number, number[]>();

function consumeReceiptAiRequest(userId: number): { allowed: true } | { allowed: false; retryAfterSeconds: number } {
  const now = Date.now();
  const windowStart = now - 60 * 60 * 1000;
  const active = (receiptAiRequests.get(userId) ?? []).filter((timestamp) => timestamp > windowStart);
  if (active.length >= env.OPENAI_RECEIPT_MAX_REQUESTS_PER_HOUR) {
    receiptAiRequests.set(userId, active);
    return { allowed: false, retryAfterSeconds: Math.max(1, Math.ceil((active[0]! + 60 * 60 * 1000 - now) / 1000)) };
  }
  active.push(now);
  receiptAiRequests.set(userId, active);
  return { allowed: true };
}

function rejectReceiptAiLimit(res: import("express").Response, retryAfterSeconds: number): void {
  res.setHeader("Retry-After", String(retryAfterSeconds));
  res.status(429).json({ ok: false, error: "Has alcanzado el límite de análisis con IA. Inténtalo de nuevo más tarde." });
}

function toClientAiAnalysis(aiAnalysis: Awaited<ReturnType<typeof analyzeReceiptWithOpenAi>>) {
  const { model: aiModel, products, ...documentFields } = aiAnalysis;
  const primaryProduct = products[0];
  return {
    ...documentFields,
    products,
    aiModel,
    method: "openai-vision",
    name: primaryProduct?.name ?? "",
    brand: primaryProduct?.brand ?? "",
    model: primaryProduct?.model ?? "",
    serialNumber: primaryProduct?.serialNumber ?? "",
  };
}

receiptsRouter.post("/", upload.single("receipt"), async (req, res) => {
  if (!req.file) {
    res.status(400).json({ ok: false, error: "No se recibio ningun archivo." });
    return;
  }

  const filename = req.file.filename;
  if (isFileEncryptionEnabled()) {
    const filePath = path.join(receiptsStorageDir, filename);
    const raw = await fs.readFile(filePath);
    await writeEncryptedFile(filePath, raw);
  }
  res.json({ ok: true, data: { filename, url: `/receipts/${filename}` } });
});

receiptsRouter.post("/analyze", upload.single("receipt"), async (req, res) => {
  if (!req.file) {
    res.status(400).json({ ok: false, error: "No se recibio ningun archivo." });
    return;
  }

  const filename = req.file.filename;
  const url = `/receipts/${filename}`;
  console.log("[receipts] analyze start:", filename);

  try {
    const receiptPath = path.join(receiptsStorageDir, filename);
    const analysisBuffer = isFileEncryptionEnabled()
      ? await readEncryptedFile(receiptPath)
      : await fs.readFile(receiptPath);
    let analysis: any;
    if (isReceiptAiEnabled()) {
      const usage = consumeReceiptAiRequest(req.auth?.sub ?? 0);
      if (!usage.allowed) {
        void recordAppEvent({ level: "warning", source: "receipt-ai", message: "Límite de análisis alcanzado", details: { userId: req.auth?.sub ?? null, pages: 1 } });
        rejectReceiptAiLimit(res, usage.retryAfterSeconds);
        return;
      }
      const aiAnalysis = await analyzeReceiptWithOpenAi({ buffer: analysisBuffer, mimeType: req.file.mimetype, filename: req.file.originalname });
      analysis = toClientAiAnalysis(aiAnalysis);
      void recordAppEvent({
        level: "info",
        source: "receipt-ai",
        message: "Factura analizada con IA",
        details: { userId: req.auth?.sub ?? null, pages: 1, model: aiAnalysis.model, confidence: aiAnalysis.confidence, products: aiAnalysis.products.length },
      });
    } else {
      const tempPath = isFileEncryptionEnabled()
        ? path.join(receiptsStorageDir, `${filename}.tmp.pdf`)
        : receiptPath;
      if (isFileEncryptionEnabled()) {
        await fs.writeFile(tempPath, analysisBuffer);
      }
      analysis = await analyzeReceiptPdfFile(tempPath);
      if (isFileEncryptionEnabled()) {
        await fs.unlink(tempPath).catch(() => {});
      }
    }
    console.log(
      "[receipts] analyze done:",
      filename,
      JSON.stringify({ ...analysis, rawText: analysis.rawText ? `${String(analysis.rawText).slice(0, 80)}...` : "" }),
    );
    res.json({ ok: true, data: { filename, url, analysis } });
  } catch (err) {
    console.error("[receipts] analyze error:", err);
    void recordAppEvent({ level: "error", source: "receipt-ai", message: "Error al analizar una factura", details: { userId: req.auth?.sub ?? null, pages: 1, reason: err instanceof Error ? err.message : "unknown" } });
    res.status(500).json({ ok: false, error: "No se pudo analizar el justificante." });
  }
});

receiptsRouter.post("/analyze-batch", upload.array("receipts", 10), async (req, res) => {
  const files = Array.isArray(req.files) ? req.files : [];
  if (files.length < 2) {
    res.status(400).json({ ok: false, error: "Añade al menos dos páginas para analizarlas juntas." });
    return;
  }
  if (!isReceiptAiEnabled()) {
    res.status(503).json({ ok: false, error: "El análisis inteligente no está configurado." });
    return;
  }
  const usage = consumeReceiptAiRequest(req.auth?.sub ?? 0);
  if (!usage.allowed) {
    void recordAppEvent({ level: "warning", source: "receipt-ai", message: "Límite de análisis multipágina alcanzado", details: { userId: req.auth?.sub ?? null, pages: files.length } });
    rejectReceiptAiLimit(res, usage.retryAfterSeconds);
    return;
  }
  try {
    const pages = await Promise.all(files.map(async (file) => ({
      buffer: isFileEncryptionEnabled()
        ? await readEncryptedFile(path.join(receiptsStorageDir, file.filename))
        : await fs.readFile(path.join(receiptsStorageDir, file.filename)),
      mimeType: file.mimetype,
      filename: file.originalname,
    })));
    const analysis = await analyzeReceiptPagesWithOpenAi(pages);
    void recordAppEvent({
      level: "info",
      source: "receipt-ai",
      message: "Factura multipágina analizada con IA",
      details: { userId: req.auth?.sub ?? null, pages: files.length, model: analysis.model, confidence: analysis.confidence, products: analysis.products.length },
    });
    res.json({ ok: true, data: { urls: files.map((file) => `/receipts/${file.filename}`), analysis: toClientAiAnalysis(analysis) } });
  } catch (err) {
    console.error("[receipts] batch analyze error:", err);
    void recordAppEvent({ level: "error", source: "receipt-ai", message: "Error al analizar una factura multipágina", details: { userId: req.auth?.sub ?? null, pages: files.length, reason: err instanceof Error ? err.message : "unknown" } });
    res.status(502).json({ ok: false, error: "No se pudo analizar el documento completo." });
  }
});
