// controllers/historiqueParcoursController.js
const express = require("express");
const router = express.Router();
const renderPage = require("../utils/renderHelper");
const logger = require("../utils/logger");
const parcoursService = require("../services/parcoursService");
const clientService = require("../services/clientService");
const { fmtDateFR, xmlEsc, cellXml, rowXml } = require("../services/globalService");

/**
 * Construit les filtres et le tri PocketBase à partir des paramètres DataTables
 * @param {Object} params - Paramètres de recherche et filtrage
 * @param {string[]} params.regions - Liste des régions à filtrer
 * @param {Object} params.search - Objet de recherche globale
 * @param {Array} params.columns - Colonnes avec leurs filtres individuels
 * @param {Array} params.order - Ordre de tri
 * @returns {Object} - {filter: string, sort: string}
 */
function buildPocketBaseFilterAndSort({ regions = [], search = { value: "" }, columns = [], order = [] }) {
  const parts = [];

  /**
   * Recherche globale : recherche dans tous les champs pertinents
   */
  const q = (search?.value || "").trim();
  if (q) {
    const esc = q.replace(/"/g, '\\"');
    parts.push(`(
      numParcours ~ "${esc}"
      || contrat.numSaisine ~ "${esc}"
      || contrat.numContrat ~ "${esc}"
      || contrat.produit ~ "${esc}"
      || contrat.type ~ "${esc}"
      || contrat.intermediaire.nom ~ "${esc}"
      || contrat.intermediaire.numPortefeuille ~ "${esc}"
      || contrat.client.nom ~ "${esc}"
      || contrat.client.numClient ~ "${esc}"
      || dernierUtilisateur.prenom ~ "${esc}"
      || dernierUtilisateur.nom ~ "${esc}"
      || dernierUtilisateur.matricule ~ "${esc}"
      || dernierUtilisateur.region.nom ~ "${esc}"
    )`);
  }

  /**
   * Recherche par colonne : filtre spécifique pour chaque colonne
   */
  const colFilter = (idx, fieldPaths) => {
    const v = (columns[idx]?.search?.value || "").trim();
    if (!v) return null;
    const esc = v.replace(/"/g, '\\"');
    return `(${fieldPaths.map(fp => `${fp} ~ "${esc}"`).join(" || ")})`;
  };
  
  const pushIf = (v) => { if (v) parts.push(v); };

  // Filtres par colonne (index correspondant à l'ordre des colonnes DataTables)
  pushIf(colFilter(0, ["numParcours"]));
  pushIf(colFilter(1, ["created"]));
  pushIf(colFilter(2, ["dernierUtilisateur.matricule"]));
  pushIf(colFilter(3, ["dernierUtilisateur.prenom", "dernierUtilisateur.nom"]));
  pushIf(colFilter(4, ["dernierUtilisateur.region.nom"]));
  pushIf(colFilter(5, ["contrat.numSaisine"]));
  pushIf(colFilter(6, ["contrat.numContrat"]));
  pushIf(colFilter(7, ["contrat.produit"]));
  pushIf(colFilter(8, ["contrat.type"]));
  pushIf(colFilter(9, ["contrat.intermediaire.numPortefeuille"]));
  pushIf(colFilter(10, ["contrat.intermediaire.nom"]));
  pushIf(colFilter(11, ["contrat.client.numClient"]));
  pushIf(colFilter(12, ["contrat.client.nom"]));

  const filter = parts.length ? parts.join(" && ") : "";

  /**
   * Construction du tri PocketBase
   * Mapping des index de colonnes DataTables vers les champs PocketBase
   * Le préfixe "-" indique un tri décroissant
   */
  const sortMap = {
    0: "numParcours",
    1: "created",
    2: "dernierUtilisateur.matricule",
    4: "dernierUtilisateur.region.nom",
    6: "contrat.numContrat",
    7: "contrat.produit",
    10: "contrat.intermediaire.nom",
    12: "contrat.client.nom"
  };
  
  let sort = "-created"; // Tri par défaut : date de création décroissante
  if (order && order.length > 0) {
    const { column, dir } = order[0];
    const field = sortMap[column];
    if (field) {
      sort = (dir === "desc" ? "-" : "") + field;
    }
  }

  return { filter, sort };
}

/**
 * Route GET / : Affichage de la page Historique des parcours
 */
router.get("/", (req, res) => {
  renderPage("historiqueParcours.ejs", res);
});

/**
 * /regionUser : requête sur la region de l'user actuel
 */
router.get("/:regionUser", async (req, res) => {
  try {
    const { regionUser } = req.params;
    const data = await parcoursService.getParcoursByRegionsPage([regionUser], 1, 10, { filter: "", sort: "-created" });
    if (data) {
      res.json({ valid: true, data });
    } else {
      res.json({ valid: false });
    }
  } catch (error) {
    logger.log("error", error);
    res.status(500).json({
      valid: false,
      error: "Erreur lors de la récupération des parcours.",
    });
  }
});

/**
 * /datatable : DataTables server-side (gestion de pagination)
 */
router.post("/datatable", async (req, res) => {
  try {
    const {
      draw = 1,
      start = 0,
      length = 10,
      regions = [],
      search = { value: "" },
      columns = [],
      order = []
    } = req.body || {};

    const page = Math.floor(start / length) + 1; // nb de page
    const perPage = Number(length) || 10; //nb d'éléments par page

    const { filter, sort } = buildPocketBaseFilterAndSort({ search, columns, order }); // construction du filtrage côté Back

    const result = await parcoursService.getParcoursByRegionsPage([], page, perPage, { filter, sort });

    /**
     * Construction des lignes de données pour DataTables
     * Traitement séquentiel pour garantir la récupération des clients
     */
    const rows = [];
    for (const parcours of result.items) {
      try {
        const contrat = parcours["@expand"]?.contrat || null;
        
        /**
         * Récupération du client avec fallback
         * L'expand PocketBase ne fonctionne pas toujours pour contrat.client,
         * donc on récupère directement via l'ID si nécessaire
         */
        let client = null;
        if (contrat) {
          // Tentative via expand (si disponible)
          client = contrat["@expand"]?.client || null;
          
          // Fallback : récupération directe via l'ID du client
          if (!client && contrat.client) {
            const clientId = typeof contrat.client === 'string' 
              ? contrat.client 
              : (contrat.client?.id || contrat.client);
            
            if (clientId) {
              try {
                client = await clientService.getClient(clientId);
              } catch (err) {
                // Erreur silencieuse : client non trouvé ou erreur de récupération
                client = null;
              }
            }
          }
          
          // Cas où contrat.client est déjà un objet (expand réussi mais pas dans @expand)
          if (!client && contrat.client && typeof contrat.client === 'object' && contrat.client.numClient) {
            client = contrat.client;
          }
        }
        const lastUser = parcours["@expand"]?.dernierUtilisateur;
        const region = lastUser?.["@expand"]?.region;
        const produit = contrat ? (contrat.produit || "NC") : "NC";

        /**
         * Construction de la ligne DataTables
         * Ordre des colonnes : Numéro Parcours, Date Création, Matricule, Utilisateur, Région,
         * Numéro Saisine, Numéro Contrat, Produit, Type, Portefeuille, Intermédiaire,
         * Numéro Client, Nom Client, Bouton Reprendre, Bouton Générer
         */
        rows.push([
          parcours.numParcours,
          fmtDateFR(parcours.created),
          lastUser?.matricule || "NC",
          lastUser ? `${lastUser.prenom} ${lastUser.nom}`.trim() || "NC" : "NC",
          region ? region.nom : "NC",
          contrat ? (contrat.numSaisine || "NC") : "NC",
          contrat ? (contrat.numContrat || "NC") : "NC",
          produit,
          contrat ? (contrat.type || "NC") : "NC",
          contrat ? (contrat["@expand"]?.intermediaire?.numPortefeuille || "NC") : "NC",
          contrat ? (contrat["@expand"]?.intermediaire?.nom || "NC") : "NC",
          client ? (client.numClient || "NC") : "NC",
          client ? (client.nom || "NC") : "NC",
          `<button type="button" id="btnReprendre" class="btn" onclick="window.location.href='${(!contrat || (!contrat.numSaisine && !contrat.numContrat)) ? `/contrat?numParcours=${parcours.numParcours}` : `/navParcours?numParcours=${parcours.numParcours}&submenu=client`}'"><i class="fas fa-arrow-right"></i></button>`,
          `<button type="button" id="btnGenerate" class="btn" data-produit="${produit}" data-num-parcours="${parcours.numParcours}" ${( !contrat || contrat.produit == "" || (contrat.fac == "" && contrat.rc == "" && contrat.tppc == "") || contrat.client == "" || contrat.intermediaire == "" ) ? 'disabled' : ''}><i class="fa-solid fa-file-arrow-down"></i></button>`
        ]);
      } catch (err) {
        logger.log("error", `Erreur traitement parcours ${parcours?.numParcours || 'inconnu'}:`, err);
        // Ligne par défaut en cas d'erreur
        rows.push(["NC", "NC", "NC", "NC", "NC", "NC", "NC", "NC", "NC", "NC", "NC", "NC", "NC", "", ""]);
      }
    }

    res.json({
      draw: Number(draw),
      recordsTotal: result.totalItems,
      recordsFiltered: result.totalItems,
      data: rows
    });
  } catch (error) {
    logger.log("error", error);
    res.status(500).json({ draw: 0, recordsTotal: 0, recordsFiltered: 0, data: [] });
  }
});

/**
 * EXPORT CSV
 * Exporte l'historique des parcours au format CSV
 * Supporte l'export complet ou filtré selon les paramètres de la requête
 */
router.post("/export/csv", async (req, res) => {
  let aborted = false;
  req.on("aborted", () => {
    aborted = true;
    logger.log("warn", "Client a interrompu la connexion pendant l'export CSV");
  });
  res.on("finish", () => {
    logger.log("info", "Export CSV terminé");
  });

  try {
    const {
      regions = [],
      search = { value: "" },
      columns = [],
      order = [],
      mode = "filtered",
    } = req.body || {};

    const effective = (mode === "full")
      ? { regions: [], search: { value: "" }, columns: [], order }
      : { regions, search, columns, order };

    const { filter, sort } = buildPocketBaseFilterAndSort(effective);

    res.setHeader("Content-Type", "text/csv; charset=utf-8");
    res.setHeader("Content-Disposition", `attachment; filename="historique_parcours.csv"`);

    // BOM UTF-8 pour Excel
    res.write("\uFEFF");

    const headers = [
      "Numéro du Parcours","Date de Création","Matricule","Dernier Utilisateur","Region",
      "Numéro Saisine","Numéro Contrat","Produit","Type","Numéro de Portefeuille",
      "Nom Intermediaire","Numéro de Client","Nom Client"
    ];
    res.write(headers.join(";") + "\n");

    /**
     * OPTIMISATION : getFullList pour récupérer tous les parcours en une requête
     * + batch client pour récupérer tous les clients manquants en une requête
     */
    
    // Construction du filtre régions (identique à getParcoursByRegionsPage)
    let regFilter = "";
    if (Array.isArray(effective.regions) && effective.regions.length > 0) {
      const ors = effective.regions.map(r => `dernierUtilisateur.region.nom = "${r}"`);
      regFilter = `(${ors.join(" || ")})`;
    }
    const finalFilter = [regFilter, filter].filter(Boolean).join(" && ");
    
    // Format avec espaces comme dans le code original qui fonctionnait
    const expandFields = "contrat, contrat.client, contrat.intermediaire, dernierUtilisateur.region";
    
    // Récupération de tous les parcours en une seule requête
    let allParcours;
    try {
      allParcours = await parcoursService.getParcoursFullList({
        filter: finalFilter,
        sort: sort || "-created",
        expand: expandFields,
        batch: 500,
      });
    } catch (err) {
      logger.log("error", "Erreur récupération parcours pour export CSV:", err);
      if (!res.headersSent) {
        return res.status(500).send("Erreur lors de la récupération des données");
      }
      try { res.end(); } catch {}
      return;
    }

    // Collecte des IDs clients manquants (l'expand contrat.client ne fonctionne pas en SDK 0.7.x)
    const missingClientIds = [];
    for (const parcours of allParcours) {
      const contrat = parcours["@expand"]?.contrat;
      if (contrat && contrat.client && !contrat["@expand"]?.client) {
        missingClientIds.push(contrat.client);
      }
    }
    
    // Récupération batch de tous les clients manquants en une seule requête
    const clientsMap = await clientService.getClientsBatch(missingClientIds);

    // Traitement des parcours
    for (const parcours of allParcours) {
      if (aborted) break;

      const contrat = parcours["@expand"]?.contrat || null;
      const intermediaire = contrat ? (contrat["@expand"]?.intermediaire || null) : null;
      
      // Client : d'abord depuis l'expand, sinon depuis le batch
      let client = contrat ? (contrat["@expand"]?.client || null) : null;
      if (!client && contrat && contrat.client) {
        client = clientsMap.get(contrat.client) || null;
      }
      
      const lastUser = parcours["@expand"]?.dernierUtilisateur;
      const region = lastUser?.["@expand"]?.region;

      const row = [
        parcours.numParcours,
        fmtDateFR(parcours.created),
        lastUser?.matricule || "NC",
        lastUser ? `${lastUser.prenom || ""} ${lastUser.nom || ""}`.trim() || "NC" : "NC",
        region ? (region.nom || "NC") : "NC",
        contrat ? (contrat.numSaisine || "NC") : "NC",
        contrat ? (contrat.numContrat || "NC") : "NC",
        contrat ? (contrat.produit || "NC") : "NC",
        contrat ? (contrat.type || "NC") : "NC",
        intermediaire ? (intermediaire.numPortefeuille || "NC") : "NC",
        intermediaire ? (intermediaire.nom || "NC") : "NC",
        client ? (client.numClient || "NC") : "NC",
        client ? (client.nom || "NC") : "NC",
      ];

      const safe = row.map(v => String(v).replaceAll(";", ",").replace(/\r?\n/g, " "));
      try {
        res.write(safe.join(";") + "\n");
      } catch (werr) {
        logger.log("error", werr);
        aborted = true;
        break;
      }
    }

    if (!aborted) {
      res.end();
    }
  } catch (error) {
    logger.log("error", error);
    if (!res.headersSent) {
      return res.status(500).send("Erreur export CSV");
    }
    try { res.end(); } catch {}
  }
});


// ====== UTILITAIRES XML/XLS ======

/**
 * EXPORT XLS (SpreadsheetML 2003)
 * Format XLS utilisé car XLSX est trop complexe à générer manuellement.
 * Le format XLS est toujours supporté par Excel sans perte de données.
 */
router.post("/export/xls", async (req, res) => {
    let aborted = false;
  req.on("aborted", () => {
    aborted = true;
    logger.log("warn", "Client a interrompu la connexion pendant l'export XLS");
  });
  res.on("finish", () => {
    logger.log("info", "Export XLS terminé");
  });

  try {
    const {
      regions = [],
      search = { value: "" },
      columns = [],
      order = [],
      mode = "filtered"
    } = req.body || {};

    const effective = (mode === "full")
      ? { regions: [], search: { value: "" }, columns, order }
      : { regions, search, columns, order };

    const { filter, sort } = buildPocketBaseFilterAndSort(effective);

    const headers = [
      "Numéro du Parcours","Date de Création","Matricule","Dernier Utilisateur","Region",
      "Numéro Saisine","Numéro Contrat","Produit","Type","Numéro de Portefeuille",
      "Nom Intermediaire","Numéro de Client","Nom Client"
    ];

    /**
     * OPTIMISATION : getFullList pour récupérer tous les parcours en une requête
     * + batch client pour récupérer tous les clients manquants en une requête
     */
    
    // Construction du filtre régions (identique à getParcoursByRegionsPage)
    let regFilter = "";
    if (Array.isArray(effective.regions) && effective.regions.length > 0) {
      const ors = effective.regions.map(r => `dernierUtilisateur.region.nom = "${r}"`);
      regFilter = `(${ors.join(" || ")})`;
    }
    const finalFilter = [regFilter, filter].filter(Boolean).join(" && ");
    
    // Format avec espaces comme dans le code original qui fonctionnait
    const expandFields = "contrat, contrat.client, contrat.intermediaire, dernierUtilisateur.region";
    
    // Récupération de tous les parcours en une seule requête
    let allParcours;
    try {
      allParcours = await parcoursService.getParcoursFullList({
        filter: finalFilter,
        sort: sort || "-created",
        expand: expandFields,
        batch: 500,
      });
    } catch (err) {
      logger.log("error", "Erreur récupération parcours pour export XLS:", err);
      if (!res.headersSent) {
        return res.status(500).send("Erreur lors de la récupération des données");
      }
      try { res.end(); } catch {}
      return;
    }

    // Collecte des IDs clients manquants (l'expand contrat.client ne fonctionne pas en SDK 0.7.x)
    const missingClientIds = [];
    for (const parcours of allParcours) {
      const contrat = parcours["@expand"]?.contrat;
      if (contrat && contrat.client && !contrat["@expand"]?.client) {
        missingClientIds.push(contrat.client);
      }
    }
    
    // Récupération batch de tous les clients manquants en une seule requête
    const clientsMap = await clientService.getClientsBatch(missingClientIds);

    const fileName = (mode === "full")
      ? "historique_parcours_complet.xls"
      : "historique_parcours_filtre.xls";

    res.setHeader("Content-Type", "application/vnd.ms-excel; charset=utf-8");
    res.setHeader("Content-Disposition", `attachment; filename="${fileName}"`);

    // En-tête SpreadsheetML 2003
    res.write(
`<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
  xmlns:o="urn:schemas-microsoft-com:office:office"
  xmlns:x="urn:schemas-microsoft-com:office:excel"
  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
  xmlns:html="http://www.w3.org/TR/REC-html40">
  <Styles>
    <Style ss:ID="Default" ss:Name="Normal">
      <Alignment ss:Vertical="Center"/>
      <Borders/>
      <Font ss:FontName="Calibri" ss:Size="11"/>
      <Interior/>
      <NumberFormat/>
      <Protection/>
    </Style>
    <Style ss:ID="Header">
      <Font ss:Bold="1"/>
      <Alignment ss:Horizontal="Center" ss:Vertical="Center"/>
    </Style>
  </Styles>
  <Worksheet ss:Name="Historique">
    <Table>
`
    );

    res.write(
      `<Row ss:StyleID="Header">` +
      headers.map(h => `<Cell><Data ss:Type="String">${xmlEsc(h)}</Data></Cell>`).join("") +
      `</Row>\n`
    );

    // Traitement des parcours
    for (const parcours of allParcours) {
      const contrat = parcours["@expand"]?.contrat || null;
      const intermediaire = contrat ? (contrat["@expand"]?.intermediaire || null) : null;
      
      // Client : d'abord depuis l'expand, sinon depuis le batch
      let client = contrat ? (contrat["@expand"]?.client || null) : null;
      if (!client && contrat && contrat.client) {
        client = clientsMap.get(contrat.client) || null;
      }
      
      const lastUser = parcours["@expand"]?.dernierUtilisateur;
      const region = lastUser?.["@expand"]?.region;

      const row = [
        parcours.numParcours,
        fmtDateFR(parcours.created),
        lastUser?.matricule || "NC",
        lastUser ? `${lastUser.prenom || ""} ${lastUser.nom || ""}`.trim() || "NC" : "NC",
        region ? (region.nom || "NC") : "NC",
        contrat ? (contrat.numSaisine || "NC") : "NC",
        contrat ? (contrat.numContrat || "NC") : "NC",
        contrat ? (contrat.produit || "NC") : "NC",
        contrat ? (contrat.type || "NC") : "NC",
        intermediaire ? (intermediaire.numPortefeuille || "NC") : "NC",
        intermediaire ? (intermediaire.nom || "NC") : "NC",
        client ? (client.numClient || "NC") : "NC",
        client ? (client.nom || "NC") : "NC",
      ].map(v => String(v).replace(/\r?\n/g, " "));

      res.write(rowXml(row) + "\n");
    }

    // Fermeture du fichier XML SpreadsheetML
    res.write(
`    </Table>
  </Worksheet>
</Workbook>`
    );
    res.end();

  } catch (error) {
    logger.log("error", error);
    if (!res.headersSent) return res.status(500).send("Erreur export XLS");
    try { res.end(); } catch {}
  }
});

/**
 * Route GET /details/:numParcours
 * Récupère les détails complets d'un parcours (parcours + contrat + fiche produit)
 * Utilisé pour afficher le panneau de détails dans la datatable
 */
router.get("/details/:numParcours", async (req, res) => {
  try {
    const { numParcours } = req.params;
    const parcours = await parcoursService.getDeepDetailsByNumParcours(numParcours);
    if (!parcours) return res.json({ valid: false, error: "Parcours introuvable" });

    /**
     * Extraction des données pour faciliter l'accès côté frontend
     */
    const contrat = parcours?.["@expand"]?.contrat || null;
    const produit = (contrat?.produit || "").toUpperCase();

    /**
     * Récupération de la fiche produit selon le type (TPPC, RC, FAC)
     * Si l'expand n'a pas fonctionné, on essaie de récupérer directement via l'ID
     */
    let produitRecord = null;
    if (produit === "TPPC") {
      produitRecord = contrat?.["@expand"]?.tppc || null;
    } else if (produit === "RC") {
      produitRecord = contrat?.["@expand"]?.rc || null;
      // Si l'expand n'a pas fonctionné mais qu'on a l'ID, on récupère directement
      if (!produitRecord && contrat?.rc) {
        try {
          const rcId = typeof contrat.rc === 'string' ? contrat.rc : contrat.rc?.id || contrat.rc;
          if (rcId) {
            const rcService = require("../services/rcService");
            const rcData = await rcService.getRCbyId(rcId);
            // fetchInfoByCriteria retourne directement l'item, pas un objet avec items
            produitRecord = rcData || null;
          }
        } catch (e) {
          logger.log("info", `Erreur récupération RC directe pour ${numParcours}: ${e.message}`);
        }
      }
    } else if (produit === "FAC") {
      produitRecord = contrat?.["@expand"]?.fac || null;
      // Si l'expand n'a pas fonctionné mais qu'on a l'ID, on récupère directement
      if (!produitRecord && contrat?.fac) {
        try {
          const facId = typeof contrat.fac === 'string' ? contrat.fac : contrat.fac?.id || contrat.fac;
          if (facId) {
            const facService = require("../services/facService");
            const facData = await facService.getFACbyId(facId);
            // fetchInfoByCriteria retourne directement l'item, pas un objet avec items
            produitRecord = facData || null;
            if (!produitRecord) {
              logger.log("warn", `FAC non trouvé pour ID ${facId} (parcours ${numParcours})`);
            }
          } else {
            logger.log("warn", `Pas d'ID FAC dans contrat pour parcours ${numParcours}`);
          }
        } catch (e) {
          logger.log("warn", `Erreur récupération FAC directe pour ${numParcours}: ${e.message}`);
        }
      } else if (!produitRecord && !contrat?.fac) {
        logger.log("warn", `Contrat FAC sans relation FAC pour parcours ${numParcours}`);
      }
    }

    return res.json({
      valid: true,
      produit,
      parcours,
      contrat,
      produitRecord,
    });
  } catch (e) {
    logger.log("error", e);
    return res.status(500).json({ valid: false, error: "Erreur recherche détails" });
  }
});


module.exports = router;