import { Router } from "express";
import multer from "multer";
import fs from "node:fs/promises";
import path from "node:path";
import {
  addIncidentAttachments,
  addProductMedia,
  assertOwnedProduct,
  createIncident,
  deleteIncidentAttachment,
  deleteProductMedia,
  getIncident,
  getIncidentStats,
  listIncidents,
  listProductMedia,
  updateIncident,
} from "./repairs.service.js";
import { ensureReceiptStorage, incidentAttachmentsStorageDir, productMediaStorageDir, isFileEncryptionEnabled, writeEncryptedFile } from "../../lib/storage.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 "";
  }
}

function createUpload(dir: string) {
  return multer({
    storage: multer.diskStorage({
      destination: (_req, _file, cb) => {
        ensureReceiptStorage();
        cb(null, dir);
      },
      filename: (_req, file, cb) => {
        const ext = path.extname(file.originalname).toLowerCase() || extensionFromMime(file.mimetype) || ".bin";
        cb(null, `${Date.now()}-${Math.random().toString(36).slice(2)}${ext}`);
      },
    }),
    limits: { fileSize: 15 * 1024 * 1024 },
    fileFilter: (_req, file, cb) => {
      const ext = path.extname(file.originalname).toLowerCase();
      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"];
      if (allowedExt.includes(ext) || allowedMime.includes(file.mimetype)) {
        cb(null, true);
        return;
      }
      cb(new Error("Formato no permitido."));
    },
  });
}

const productMediaUpload = createUpload(productMediaStorageDir);
const incidentUpload = createUpload(incidentAttachmentsStorageDir);

export const repairsRouter = Router();

repairsRouter.get("/products/:productId/media", async (req, res) => {
  const productIdParam = Array.isArray(req.params.productId) ? req.params.productId[0] ?? "" : req.params.productId ?? "";
  const productId = Number.parseInt(productIdParam, 10);
  if (!Number.isFinite(productId)) {
    res.status(400).json({ ok: false, error: "Producto invalido." });
    return;
  }
  try {
    await assertOwnedProduct(req.auth!.sub, productId);
    const media = await listProductMedia(req.auth!.sub, productId);
    res.json({ ok: true, data: media });
  } catch (err) {
    if (err instanceof Error && err.message === "product_not_found") {
      res.status(404).json({ ok: false, error: "Producto no encontrado." });
      return;
    }
    console.error("[repairs] list product media error:", err);
    res.status(500).json({ ok: false, error: "Error cargando fotos." });
  }
});

repairsRouter.post("/products/:productId/media", productMediaUpload.array("files", 12), async (req, res) => {
  const productIdParam = Array.isArray(req.params.productId) ? req.params.productId[0] ?? "" : req.params.productId ?? "";
  const productId = Number.parseInt(productIdParam, 10);
  if (!Number.isFinite(productId)) {
    res.status(400).json({ ok: false, error: "Producto invalido." });
    return;
  }
  try {
    await assertOwnedProduct(req.auth!.sub, productId);
    const files = Array.isArray(req.files) ? req.files : [];
    const caption = typeof req.body?.caption === "string" ? req.body.caption.trim() : "";
    const media = await addProductMedia(
      req.auth!.sub,
      productId,
      files.map((file, index) => ({
        fileUrl: `/files/product-media/${file.filename}`,
        fileName: file.originalname,
        mimeType: file.mimetype,
        kind: file.mimetype === "application/pdf" ? "document" : "photo",
        caption,
        sortOrder: index,
      }))
    );
    if (isFileEncryptionEnabled()) {
      await Promise.all(files.map(async (file) => {
        const filePath = path.join(productMediaStorageDir, file.filename);
        const raw = await fs.readFile(filePath);
        await writeEncryptedFile(filePath, raw);
      }));
    }
    res.status(201).json({ ok: true, data: media });
  } catch (err) {
    if (err instanceof Error && err.message === "product_not_found") {
      res.status(404).json({ ok: false, error: "Producto no encontrado." });
      return;
    }
    console.error("[repairs] add product media error:", err);
    res.status(500).json({ ok: false, error: "Error guardando fotos." });
  }
});

repairsRouter.delete("/products/:productId/media/:mediaId", async (req, res) => {
  const productIdParam = Array.isArray(req.params.productId) ? req.params.productId[0] ?? "" : req.params.productId ?? "";
  const mediaIdParam = Array.isArray(req.params.mediaId) ? req.params.mediaId[0] ?? "" : req.params.mediaId ?? "";
  const productId = Number.parseInt(productIdParam, 10);
  const mediaId = Number.parseInt(mediaIdParam, 10);
  if (!Number.isFinite(productId) || !Number.isFinite(mediaId)) {
    res.status(400).json({ ok: false, error: "Identificador invalido." });
    return;
  }
  try {
    const deleted = await deleteProductMedia(req.auth!.sub, productId, mediaId);
    if (!deleted) {
      res.status(404).json({ ok: false, error: "Adjunto no encontrado." });
      return;
    }
    res.json({ ok: true });
  } catch (err) {
    console.error("[repairs] delete product media error:", err);
    res.status(500).json({ ok: false, error: "Error eliminando foto." });
  }
});

repairsRouter.get("/incidents", async (req, res) => {
  try {
    const incidents = await listIncidents(req.auth!.sub);
    res.json({ ok: true, data: incidents });
  } catch (err) {
    console.error("[repairs] list incidents error:", err);
    res.status(500).json({ ok: false, error: "Error cargando incidencias." });
  }
});

repairsRouter.get("/incidents/stats", async (req, res) => {
  try {
    const stats = await getIncidentStats(req.auth!.sub);
    res.json({ ok: true, data: stats });
  } catch (err) {
    console.error("[repairs] incident stats error:", err);
    res.status(500).json({ ok: false, error: "Error cargando estadisticas." });
  }
});

repairsRouter.get("/incidents/:id", async (req, res) => {
  const idParam = Array.isArray(req.params.id) ? req.params.id[0] ?? "" : req.params.id ?? "";
  const id = Number.parseInt(idParam, 10);
  if (!Number.isFinite(id)) {
    res.status(400).json({ ok: false, error: "Incidencia invalida." });
    return;
  }
  try {
    const incident = await getIncident(req.auth!.sub, id);
    if (!incident) {
      res.status(404).json({ ok: false, error: "Incidencia no encontrada." });
      return;
    }
    res.json({ ok: true, data: incident });
  } catch (err) {
    console.error("[repairs] get incident error:", err);
    res.status(500).json({ ok: false, error: "Error cargando incidencia." });
  }
});

repairsRouter.post("/incidents", async (req, res) => {
  const body = req.body ?? {};
  const productId = Number.parseInt(String(body.productId ?? ""), 10);
  if (!Number.isFinite(productId)) {
    res.status(400).json({ ok: false, error: "Producto invalido." });
    return;
  }
  try {
    await assertOwnedProduct(req.auth!.sub, productId);
    const incident = await createIncident({
      userId: req.auth!.sub,
      productId,
      title: String(body.title ?? "").trim(),
      incidentType: String(body.incidentType ?? "repair").trim() || "repair",
      detectedAt: String(body.detectedAt ?? new Date().toISOString().slice(0, 10)),
      deliveryPlace: String(body.deliveryPlace ?? "").trim(),
      receivedBy: String(body.receivedBy ?? "").trim(),
      description: String(body.description ?? "").trim(),
      symptom: String(body.symptom ?? "").trim(),
      accessories: String(body.accessories ?? "").trim(),
      satName: String(body.satName ?? "").trim(),
      satReference: String(body.satReference ?? "").trim(),
      diagnosis: String(body.diagnosis ?? "").trim(),
      technicianNotes: String(body.technicianNotes ?? "").trim(),
      estimatedCost: body.estimatedCost === "" || body.estimatedCost == null ? null : Number(body.estimatedCost),
      resolution: String(body.resolution ?? "").trim(),
      replacedWithNew: Boolean(body.replacedWithNew),
      replacedWithRefurbished: Boolean(body.replacedWithRefurbished),
      userRating: body.userRating === "" || body.userRating == null ? null : Number(body.userRating),
      userRatingNotes: String(body.userRatingNotes ?? "").trim(),
      sharePublicRating: Boolean(body.sharePublicRating),
    });
    res.status(201).json({ ok: true, data: incident });
  } catch (err) {
    if (err instanceof Error && err.message === "product_not_found") {
      res.status(404).json({ ok: false, error: "Producto no encontrado." });
      return;
    }
    console.error("[repairs] create incident error:", err);
    res.status(500).json({ ok: false, error: "Error creando incidencia." });
  }
});

repairsRouter.patch("/incidents/:id", async (req, res) => {
  const idParam = Array.isArray(req.params.id) ? req.params.id[0] ?? "" : req.params.id ?? "";
  const id = Number.parseInt(idParam, 10);
  if (!Number.isFinite(id)) {
    res.status(400).json({ ok: false, error: "Incidencia invalida." });
    return;
  }
  const body = req.body ?? {};
  try {
    const incident = await updateIncident(req.auth!.sub, id, {
      title: body.title === undefined ? undefined : String(body.title).trim(),
      incidentType: body.incidentType === undefined ? undefined : String(body.incidentType).trim(),
      status: body.status as any,
      detectedAt: body.detectedAt === undefined ? undefined : String(body.detectedAt),
      deliveredAt: body.deliveredAt === undefined ? undefined : (body.deliveredAt ? String(body.deliveredAt) : null),
      deliveryPlace: body.deliveryPlace === undefined ? undefined : String(body.deliveryPlace).trim(),
      receivedBy: body.receivedBy === undefined ? undefined : String(body.receivedBy).trim(),
      description: body.description === undefined ? undefined : String(body.description).trim(),
      symptom: body.symptom === undefined ? undefined : String(body.symptom).trim(),
      accessories: body.accessories === undefined ? undefined : String(body.accessories).trim(),
      satName: body.satName === undefined ? undefined : String(body.satName).trim(),
      satReference: body.satReference === undefined ? undefined : String(body.satReference).trim(),
      diagnosis: body.diagnosis === undefined ? undefined : String(body.diagnosis).trim(),
      technicianNotes: body.technicianNotes === undefined ? undefined : String(body.technicianNotes).trim(),
      estimatedCost: body.estimatedCost === undefined ? undefined : (body.estimatedCost === "" ? null : Number(body.estimatedCost)),
      resolution: body.resolution === undefined ? undefined : String(body.resolution).trim(),
      replacedWithNew: body.replacedWithNew === undefined ? undefined : Boolean(body.replacedWithNew),
      replacedWithRefurbished: body.replacedWithRefurbished === undefined ? undefined : Boolean(body.replacedWithRefurbished),
      userRating: body.userRating === undefined ? undefined : (body.userRating === "" ? null : Number(body.userRating)),
      userRatingNotes: body.userRatingNotes === undefined ? undefined : String(body.userRatingNotes).trim(),
      sharePublicRating: body.sharePublicRating === undefined ? undefined : Boolean(body.sharePublicRating),
      closedAt: body.closedAt === undefined ? undefined : (body.closedAt ? String(body.closedAt) : null),
    });
    if (!incident) {
      res.status(404).json({ ok: false, error: "Incidencia no encontrada." });
      return;
    }
    res.json({ ok: true, data: incident });
  } catch (err) {
    console.error("[repairs] update incident error:", err);
    res.status(500).json({ ok: false, error: "Error actualizando incidencia." });
  }
});

repairsRouter.post("/incidents/:id/attachments", incidentUpload.array("files", 12), async (req, res) => {
  const idParam = Array.isArray(req.params.id) ? req.params.id[0] ?? "" : req.params.id ?? "";
  const id = Number.parseInt(idParam, 10);
  if (!Number.isFinite(id)) {
    res.status(400).json({ ok: false, error: "Incidencia invalida." });
    return;
  }
  try {
    const incident = await getIncident(req.auth!.sub, id);
    if (!incident) {
      res.status(404).json({ ok: false, error: "Incidencia no encontrada." });
      return;
    }
    const files = Array.isArray(req.files) ? req.files : [];
    const caption = typeof req.body?.caption === "string" ? req.body.caption.trim() : "";
    const attachments = await addIncidentAttachments(
      req.auth!.sub,
      id,
      files.map((file, index) => ({
        fileUrl: `/files/incident-attachments/${file.filename}`,
        fileName: file.originalname,
        mimeType: file.mimetype,
        kind: file.mimetype === "application/pdf" ? "document" : "attachment",
        caption,
        sortOrder: index,
      }))
    );
    if (isFileEncryptionEnabled()) {
      await Promise.all(files.map(async (file) => {
        const filePath = path.join(incidentAttachmentsStorageDir, file.filename);
        const raw = await fs.readFile(filePath);
        await writeEncryptedFile(filePath, raw);
      }));
    }
    res.status(201).json({ ok: true, data: attachments });
  } catch (err) {
    console.error("[repairs] add attachment error:", err);
    res.status(500).json({ ok: false, error: "Error guardando adjuntos." });
  }
});

repairsRouter.delete("/incidents/:id/attachments/:attachmentId", async (req, res) => {
  const idParam = Array.isArray(req.params.id) ? req.params.id[0] ?? "" : req.params.id ?? "";
  const attachmentIdParam = Array.isArray(req.params.attachmentId) ? req.params.attachmentId[0] ?? "" : req.params.attachmentId ?? "";
  const id = Number.parseInt(idParam, 10);
  const attachmentId = Number.parseInt(attachmentIdParam, 10);
  if (!Number.isFinite(id) || !Number.isFinite(attachmentId)) {
    res.status(400).json({ ok: false, error: "Identificador invalido." });
    return;
  }
  try {
    const deleted = await deleteIncidentAttachment(req.auth!.sub, id, attachmentId);
    if (!deleted) {
      res.status(404).json({ ok: false, error: "Adjunto no encontrado." });
      return;
    }
    res.json({ ok: true });
  } catch (err) {
    console.error("[repairs] delete attachment error:", err);
    res.status(500).json({ ok: false, error: "Error eliminando adjunto." });
  }
});
