import XLSX from "xlsx";

import { pool } from "../db/pool.js";
import { ApiError } from "../utils/ApiError.js";
import { exportReportAsExcel, exportReportAsPdf } from "../utils/reportExport.js";
import { formatDzdAmount } from "../utils/formatters.js";
import { ensureMgxDriversTables } from "./mgxDriversController.js";

const cleanString = (value) => String(value ?? "").trim();
const nullableString = (value) => {
  const cleaned = cleanString(value);
  return cleaned || null;
};
const nullableNumber = (value) => {
  if (value === "" || value === null || value === undefined) return null;
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : null;
};
const toNumber = (value, fallback = 0) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
};
const lower = (value) => cleanString(value).toLowerCase();

const getCompanyId = (req) => {
  const companyId = Number(req.query.company_id || req.body.company_id || req.user?.companyId || 0);
  if (!companyId) {
    throw new ApiError(400, "Société requise.");
  }
  return companyId;
};

const inferMgxModule = (entityType, action) => {
  const source = lower(entityType || action);
  if (source.includes("vehicle")) return "Parc auto";
  if (source.includes("fuel")) return "Carburant";
  if (source.includes("maintenance") || source.includes("intervention")) return "Maintenance";
  if (source.includes("purchase") || source.includes("reception")) return "Achats";
  if (source.includes("service")) return "Prestations";
  if (source.includes("rental")) return "Locations";
  if (source.includes("supplier")) return "Fournisseurs";
  if (source.includes("treasury")) return "Trésorerie";
  if (source.includes("consumable")) return "Consommables";
  if (source.includes("alert")) return "Alertes";
  if (source.includes("depot") || source.includes("referentiel") || source.includes("setting")) return "Paramétrage";
  if (source.includes("export") || source.includes("report")) return "Reporting";
  return "Administration";
};

const inferAuditActionType = (action) => {
  const normalized = upperAction(action);
  if (normalized.includes("LOGIN")) return "LOGIN";
  if (normalized.includes("IMPORT")) return "IMPORT";
  if (normalized.includes("EXPORT")) return "EXPORT";
  if (normalized.includes("ALERT_TREATED")) return "ALERT_TREATED";
  if (normalized.includes("DELETE")) return "DELETE";
  if (normalized.includes("CREATE")) return "CREATE";
  return "UPDATE";
};

const upperAction = (value) => cleanString(value).toUpperCase();

const buildAuditDetail = (oldData, newData) => {
  const previous = oldData && typeof oldData === "object" ? oldData : {};
  const next = newData && typeof newData === "object" ? newData : {};
  const keys = [...new Set([...Object.keys(previous), ...Object.keys(next)])];
  const changedKeys = keys.filter(
    (key) => JSON.stringify(previous[key] ?? null) !== JSON.stringify(next[key] ?? null),
  );
  return changedKeys.length ? changedKeys.join(", ") : "Aucune différence détaillée.";
};

const parseDateValue = (value) => {
  if (!value) return null;
  if (value instanceof Date && !Number.isNaN(value.getTime())) {
    return value.toISOString().slice(0, 10);
  }
  const raw = cleanString(value);
  if (!raw) return null;
  if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) return raw;
  const parsed = new Date(raw);
  if (Number.isNaN(parsed.getTime())) return null;
  return parsed.toISOString().slice(0, 10);
};

const parseWorkbookRows = (file) => {
  if (!file?.buffer) {
    throw new ApiError(400, "Fichier requis.");
  }
  const workbook = XLSX.read(file.buffer, { type: "buffer", cellDates: true });
  const sheetName = workbook.SheetNames[0];
  if (!sheetName) {
    throw new ApiError(400, "Le fichier ne contient aucune feuille exploitable.");
  }
  const sheet = workbook.Sheets[sheetName];
  const rows = XLSX.utils.sheet_to_json(sheet, {
    defval: "",
    raw: false,
    blankrows: false,
  });
  const columns = rows.length ? Object.keys(rows[0]) : [];
  return { rows, columns, sheetName };
};

const parseMappings = (body, requiredFields = []) => {
  if (!body?.mappings) {
    return null;
  }
  const input = typeof body.mappings === "string" ? JSON.parse(body.mappings) : body.mappings;
  const mappings = {};
  for (const field of requiredFields) {
    mappings[field] = cleanString(input?.[field] || field);
  }
  for (const [key, value] of Object.entries(input || {})) {
    mappings[key] = cleanString(value);
  }
  return mappings;
};

const projectRow = (row, mappings) => {
  const projected = {};
  for (const [target, source] of Object.entries(mappings || {})) {
    projected[target] = row[source];
  }
  return projected;
};

const fetchCompany = async (db, companyId) => {
  const { rows } = await db.query(
    `
      SELECT id, name, nif, rc, address, city, logo_url
      FROM companies
      WHERE id = $1
      LIMIT 1
    `,
    [companyId],
  );
  return rows[0] || { id: companyId, name: "Société" };
};

const parseFileName = (base, extension) =>
  `${String(base || "rapport").toLowerCase().replace(/[^a-z0-9-_]+/gi, "-")}.${extension}`;

const sendExport = async (res, report, exportType) => {
  if (exportType === "pdf") {
    const buffer = await exportReportAsPdf(report);
    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", `attachment; filename="${parseFileName(report.export_filename || report.report_key, "pdf")}"`);
    res.send(Buffer.from(buffer));
    return;
  }
  if (exportType === "excel" || exportType === "xlsx") {
    const buffer = await exportReportAsExcel(report);
    res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    res.setHeader("Content-Disposition", `attachment; filename="${parseFileName(report.export_filename || report.report_key, "xlsx")}"`);
    res.send(Buffer.from(buffer));
    return;
  }
  throw new ApiError(400, "Format d'export non supporté.");
};

const buildBaseReport = ({
  reportKey,
  title,
  company,
  parameters = [],
  sections = [],
  summary = [],
  exportFilename = null,
  landscape = true,
}) => ({
  report_key: reportKey,
  title,
  company,
  parameters,
  sections,
  summary,
  export_filename: exportFilename || reportKey,
  generated_at: new Date().toISOString(),
  generated_by: "Compta Control",
  landscape,
});

const logMgxAudit = async (db, req, { companyId, action, entityType, entityId = null, oldData = null, newData = null }) => {
  await db.query(
    `
      INSERT INTO mgx_audit_log (
        company_id, user_id, action, entity_type, entity_id,
        old_data, new_data, ip_address, created_at
      )
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, CURRENT_TIMESTAMP)
    `,
    [
      companyId,
      req.user?.sub || null,
      action,
      entityType,
      entityId,
      oldData ? JSON.stringify(oldData) : null,
      newData ? JSON.stringify(newData) : null,
      req.ip,
    ],
  );
};

const fetchReferentielMap = async (db, companyId, category) => {
  const { rows } = await db.query(
    `
      SELECT id, code, label, metadata
      FROM mgx_referentiels
      WHERE company_id = $1
        AND category = $2
        AND is_active = true
    `,
    [companyId, category],
  );
  const map = new Map();
  for (const row of rows) {
    map.set(lower(row.label), row);
    if (row.code) map.set(lower(row.code), row);
  }
  return map;
};

const fetchAllReferentielMap = async (db, category) => {
  const { rows } = await db.query(
    `
      SELECT id, company_id, code, label, metadata
      FROM mgx_referentiels
      WHERE category = $1
        AND is_active = true
    `,
    [category],
  );
  const map = new Map();
  const byValue = new Map();
  for (const row of rows) {
    map.set(`${row.company_id}:${lower(row.label)}`, row);
    if (!byValue.has(lower(row.label))) byValue.set(lower(row.label), row);
    if (row.code) map.set(`${row.company_id}:${lower(row.code)}`, row);
    if (row.code && !byValue.has(lower(row.code))) byValue.set(lower(row.code), row);
  }
  return { byCompany: map, byValue };
};

const findReferentielForCompany = (map, companyId, value) =>
  map.byCompany.get(`${companyId}:${lower(value)}`) || null;

const findReferentielFlexible = (map, companyId, fallbackCompanyId, value) =>
  map.byCompany.get(`${companyId}:${lower(value)}`)
  || map.byCompany.get(`${fallbackCompanyId}:${lower(value)}`)
  || map.byValue.get(lower(value))
  || null;

const fetchDepotMaps = async (db, companyId) => {
  const { rows } = await db.query(
    `
      SELECT id, code, label
      FROM mgx_depots
      WHERE company_id = $1
        AND is_active = true
    `,
    [companyId],
  );
  const byCode = new Map();
  const byLabel = new Map();
  for (const row of rows) {
    if (row.code) byCode.set(lower(row.code), row);
    byLabel.set(lower(row.label), row);
  }
  return { byCode, byLabel };
};

const findDepot = (depotMaps, value) =>
  depotMaps.byCode.get(lower(value)) || depotMaps.byLabel.get(lower(value)) || null;

const fetchAllDepotMaps = async (db) => {
  const { rows } = await db.query(
    `
      SELECT id, company_id, code, label
      FROM mgx_depots
      WHERE is_active = true
    `,
  );
  const byCompanyAndId = new Map();
  const byCompanyAndCode = new Map();
  const byCompanyAndLabel = new Map();
  for (const row of rows) {
    byCompanyAndId.set(`${row.company_id}:${row.id}`, row);
    if (row.code) byCompanyAndCode.set(`${row.company_id}:${lower(row.code)}`, row);
    byCompanyAndLabel.set(`${row.company_id}:${lower(row.label)}`, row);
  }
  return { byCompanyAndId, byCompanyAndCode, byCompanyAndLabel };
};

const findDepotForCompany = (depotMaps, companyId, { depotId = null, depotCode = null }) => {
  if (!companyId) return null;
  if (depotId) {
    const depot = depotMaps.byCompanyAndId.get(`${companyId}:${depotId}`);
    if (depot) return depot;
  }
  const normalizedDepotCode = lower(depotCode);
  return depotMaps.byCompanyAndCode.get(`${companyId}:${normalizedDepotCode}`)
    || depotMaps.byCompanyAndLabel.get(`${companyId}:${normalizedDepotCode}`)
    || null;
};

const findAnyDepot = (depotMaps, { depotId = null, depotCode = null }) => {
  if (depotId) {
    for (const [key, depot] of depotMaps.byCompanyAndId.entries()) {
      if (key.endsWith(`:${depotId}`)) return depot;
    }
  }
  const normalizedDepotCode = lower(depotCode);
  if (!normalizedDepotCode) return null;
  for (const [key, depot] of depotMaps.byCompanyAndCode.entries()) {
    if (key.endsWith(`:${normalizedDepotCode}`)) return depot;
  }
  for (const [key, depot] of depotMaps.byCompanyAndLabel.entries()) {
    if (key.endsWith(`:${normalizedDepotCode}`)) return depot;
  }
  return null;
};

const parseCommitFlag = (body) => {
  const raw = String(body?.commit ?? body?.apply ?? "false").toLowerCase();
  return raw === "true" || raw === "1" || raw === "yes";
};

const parseImportRequest = (req, requiredFields) => {
  const workbook = parseWorkbookRows(req.file);
  const mappings = parseMappings(req.body, requiredFields);
  const commit = parseCommitFlag(req.body);
  return { ...workbook, mappings, commit };
};

const buildPreviewResponse = ({ importType, columns, requiredFields, sampleRows, mappings = null, previewRows = [], summary = null }) => ({
  import_type: importType,
  step: mappings ? "preview" : "mapping",
  columns,
  required_fields: requiredFields,
  sample_rows: sampleRows,
  mappings,
  preview_rows: previewRows,
  summary,
});

const vehicleImportConfig = {
  importType: "vehicles",
  requiredFields: [
    "company_id",
    "depot_id",
    "driver_id",
    "reference",
    "code_camion",
    "plate_number",
    "brand",
    "model",
    "category_label",
    "depot_code",
    "fuel_type_label",
    "status_label",
    "km_initial",
    "chassis_number",
    "assigned_driver",
  ],
};

const supplierImportConfig = {
  importType: "suppliers",
  requiredFields: ["code", "name", "supplier_type_label", "city", "phone"],
};

const driverImportConfig = {
  importType: "drivers",
  requiredFields: ["company_id", "depot_id", "code", "full_name", "phone", "license_number", "license_category", "license_expiry", "depot_code"],
};

const fuelImportConfig = {
  importType: "fuel",
  requiredFields: ["entry_date", "vehicle_plate", "fuel_type_label", "quantity", "unit_price", "km_at_fillup"],
};

const purchaseImportConfig = {
  importType: "purchases",
  requiredFields: ["order_date", "supplier_code", "category_label", "depot_code", "description", "qty", "unit_price"],
};

const consumableImportConfig = {
  importType: "consumables",
  requiredFields: ["code", "label", "category_label", "unit_label", "unit_price", "min_stock"],
};

const importVehicles = async (req, res) => {
  const companyId = getCompanyId(req);
  const { rows, columns, mappings, commit } = parseImportRequest(req, vehicleImportConfig.requiredFields);
  if (!mappings) {
    return res.json(buildPreviewResponse({
      importType: vehicleImportConfig.importType,
      columns,
      requiredFields: vehicleImportConfig.requiredFields,
      sampleRows: rows.slice(0, 10),
    }));
  }

  const db = await pool.connect();
  try {
    await ensureMgxDriversTables(db);
    const [categoryMap, fuelTypeMap, statusMap, depotMaps] = await Promise.all([
      fetchAllReferentielMap(db, "vehicle_category"),
      fetchAllReferentielMap(db, "fuel_type"),
      fetchAllReferentielMap(db, "vehicle_status"),
      fetchAllDepotMaps(db),
    ]);
    const { rows: existingRows } = await db.query(
      "SELECT id, company_id, reference, plate_number FROM mgx_vehicles",
    );
    const { rows: driverRows } = await db.query(
      "SELECT id, company_id, full_name FROM mgx_drivers WHERE is_active = true",
    );
    const existingByReference = new Map(existingRows.map((row) => [`${row.company_id}:${lower(row.reference)}`, row]));
    const existingByPlate = new Map(existingRows.map((row) => [`${row.company_id}:${lower(row.plate_number)}`, row]));
    const driversById = new Map(driverRows.map((row) => [Number(row.id), row]));
    const driversByCompanyAndName = new Map(driverRows.map((row) => [`${row.company_id}:${lower(row.full_name)}`, row]));
    const seenReferences = new Set();
    const seenPlates = new Set();

    const previewRows = rows.map((sourceRow, index) => {
      const row = projectRow(sourceRow, mappings);
      const errors = [];
      const warnings = [];
      const rowCompanyId = nullableNumber(row.company_id) || companyId;
      const rowDepotId = nullableNumber(row.depot_id);
      const rowDriverId = nullableNumber(row.driver_id);
      const category = findReferentielFlexible(categoryMap, rowCompanyId, companyId, row.category_label);
      const fuelType = findReferentielFlexible(fuelTypeMap, rowCompanyId, companyId, row.fuel_type_label);
      const status = findReferentielFlexible(statusMap, rowCompanyId, companyId, row.status_label);
      const depot = findAnyDepot(depotMaps, { depotId: rowDepotId, depotCode: row.depot_code });
      const driver = rowDriverId
        ? driversById.get(rowDriverId)
        : driversByCompanyAndName.get(`${rowCompanyId}:${lower(row.assigned_driver)}`);
      const referenceKey = `${rowCompanyId}:${lower(row.reference)}`;
      const plateKey = `${rowCompanyId}:${lower(row.plate_number)}`;
      const duplicatedReference = lower(row.reference) && seenReferences.has(referenceKey);
      const duplicatedPlate = lower(row.plate_number) && seenPlates.has(plateKey);

      if (!rowCompanyId) errors.push("ID société requis");
      if (row.depot_id && !rowDepotId) errors.push(`ID dépôt invalide: ${row.depot_id}`);
      if (row.driver_id && !rowDriverId) errors.push(`ID chauffeur invalide: ${row.driver_id}`);
      if (!cleanString(row.reference)) errors.push("Référence requise");
      if (!cleanString(row.plate_number)) errors.push("Immatriculation requise");
      if (!category) errors.push(`Catégorie '${row.category_label}' introuvable dans les référentiels`);
      if (!fuelType) errors.push(`Type de carburant '${row.fuel_type_label}' non trouvé dans les référentiels`);
      if (!status) errors.push(`Statut '${row.status_label}' non trouvé dans les référentiels`);
      if ((row.depot_id || row.depot_code) && !depot) errors.push(`Dépôt '${row.depot_id || row.depot_code}' introuvable`);
      if ((row.driver_id || row.assigned_driver) && !driver) errors.push(`Chauffeur '${row.driver_id || row.assigned_driver}' introuvable`);
      if (existingByReference.has(referenceKey)) warnings.push(`Référence ${row.reference} déjà existante — sera ignorée`);
      if (existingByPlate.has(plateKey)) warnings.push(`Immatriculation ${row.plate_number} déjà existante — sera ignorée`);
      if (duplicatedReference) warnings.push(`Référence ${row.reference} dupliquée dans le fichier — sera ignorée`);
      if (duplicatedPlate) warnings.push(`Immatriculation ${row.plate_number} dupliquée dans le fichier — sera ignorée`);
      if (lower(row.reference)) seenReferences.add(referenceKey);
      if (lower(row.plate_number)) seenPlates.add(plateKey);

      return {
        row_number: index + 1,
        values: row,
        errors,
        warnings,
        valid: errors.length === 0
          && !existingByReference.has(referenceKey)
          && !existingByPlate.has(plateKey)
          && !duplicatedReference
          && !duplicatedPlate,
        resolved: {
          company_id: rowCompanyId,
          category_ref_id: category?.id || null,
          fuel_type_ref_id: fuelType?.id || null,
          status_ref_id: status?.id || null,
          depot_id: depot?.id || null,
          driver_id: driver?.id || null,
          assigned_driver: driver?.full_name || nullableString(row.assigned_driver),
        },
      };
    });

    if (!commit) {
      return res.json(buildPreviewResponse({
        importType: vehicleImportConfig.importType,
        columns,
        requiredFields: vehicleImportConfig.requiredFields,
        sampleRows: rows.slice(0, 5),
        mappings,
        previewRows: previewRows.slice(0, 50),
        summary: {
          total: previewRows.length,
          success: previewRows.filter((item) => item.valid).length,
          warnings: previewRows.filter((item) => item.warnings.length).length,
          errors: previewRows.filter((item) => item.errors.length).length,
        },
      }));
    }

    await db.query("BEGIN");
    const inserted = [];
    const skipped = [];
    const errored = [];
    for (const preview of previewRows) {
      if (!preview.valid) {
        if (preview.errors.length) errored.push(preview);
        else skipped.push(preview);
        continue;
      }
      const row = preview.values;
      const { rows: insertRows } = await db.query(
        `
          INSERT INTO mgx_vehicles (
            company_id, depot_id, driver_id, reference, code_camion, plate_number, brand, model,
            category_ref_id, fuel_type_ref_id, status_ref_id,
            km_initial, km_current, chassis_number, assigned_driver, is_active, created_by
          )
          VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,true,$16)
          RETURNING id, reference, plate_number
        `,
        [
          preview.resolved.company_id,
          preview.resolved.depot_id,
          preview.resolved.driver_id,
          cleanString(row.reference).toUpperCase(),
          nullableString(row.code_camion ? String(row.code_camion).toUpperCase() : ""),
          cleanString(row.plate_number).toUpperCase(),
          nullableString(row.brand),
          nullableString(row.model),
          preview.resolved.category_ref_id,
          preview.resolved.fuel_type_ref_id,
          preview.resolved.status_ref_id,
          toNumber(row.km_initial),
          toNumber(row.km_initial),
          nullableString(row.chassis_number),
          preview.resolved.assigned_driver,
          req.user?.sub || null,
        ],
      );
      inserted.push(insertRows[0]);
    }
    await logMgxAudit(db, req, {
      companyId,
      action: "IMPORT",
      entityType: "mgx_vehicles",
      newData: { import_type: "vehicles", inserted: inserted.length, skipped: skipped.length, errors: errored.length },
    });
    await db.query("COMMIT");
    return res.json({
      import_type: "vehicles",
      summary: {
        total: previewRows.length,
        success: inserted.length,
        warnings: skipped.length,
        errors: errored.length,
      },
      items: inserted,
      warnings: skipped,
      errors: errored,
    });
  } catch (error) {
    if (!db.release) throw error;
    try { await db.query("ROLLBACK"); } catch {}
    throw error;
  } finally {
    db.release();
  }
};

const importSuppliers = async (req, res) => {
  const companyId = getCompanyId(req);
  const { rows, columns, mappings, commit } = parseImportRequest(req, supplierImportConfig.requiredFields);
  if (!mappings) {
    return res.json(buildPreviewResponse({
      importType: supplierImportConfig.importType,
      columns,
      requiredFields: supplierImportConfig.requiredFields,
      sampleRows: rows.slice(0, 10),
    }));
  }
  const db = await pool.connect();
  try {
    const [typeMap, paymentMethodMap] = await Promise.all([
      fetchReferentielMap(db, companyId, "supplier_type"),
      fetchReferentielMap(db, companyId, "payment_method"),
    ]);
    const { rows: existingRows } = await db.query("SELECT id, code FROM mgx_suppliers WHERE company_id = $1", [companyId]);
    const existingByCode = new Map(existingRows.map((row) => [lower(row.code), row]));
    const previewRows = rows.map((sourceRow, index) => {
      const row = projectRow(sourceRow, mappings);
      const errors = [];
      const warnings = [];
      const supplierType = typeMap.get(lower(row.supplier_type_label));
      const paymentMethod = paymentMethodMap.get(lower(row.payment_method_label));
      if (!cleanString(row.code)) errors.push("Code requis");
      if (!cleanString(row.name)) errors.push("Nom requis");
      if (!supplierType) errors.push(`Type fournisseur '${row.supplier_type_label}' introuvable`);
      if (existingByCode.has(lower(row.code))) warnings.push(`Code ${row.code} déjà existant — sera ignoré`);
      return {
        row_number: index + 1,
        values: row,
        errors,
        warnings,
        valid: errors.length === 0 && !existingByCode.has(lower(row.code)),
        resolved: {
          supplier_type_ref_id: supplierType?.id || null,
          payment_method_ref_id: paymentMethod?.id || null,
        },
      };
    });
    if (!commit) {
      return res.json(buildPreviewResponse({
        importType: supplierImportConfig.importType,
        columns,
        requiredFields: supplierImportConfig.requiredFields,
        sampleRows: rows.slice(0, 5),
        mappings,
        previewRows: previewRows.slice(0, 50),
        summary: {
          total: previewRows.length,
          success: previewRows.filter((item) => item.valid).length,
          warnings: previewRows.filter((item) => item.warnings.length).length,
          errors: previewRows.filter((item) => item.errors.length).length,
        },
      }));
    }
    await db.query("BEGIN");
    const inserted = [];
    const skipped = [];
    const errored = [];
    for (const preview of previewRows) {
      if (!preview.valid) {
        if (preview.errors.length) errored.push(preview);
        else skipped.push(preview);
        continue;
      }
      const row = preview.values;
      const { rows: insertRows } = await db.query(
        `
          INSERT INTO mgx_suppliers (
            company_id, supplier_type_ref_id, code, name, city, phone,
            payment_method_ref_id, payment_terms, is_active
          )
          VALUES ($1,$2,$3,$4,$5,$6,$7,$8,true)
          RETURNING id, code, name
        `,
        [
          companyId,
          preview.resolved.supplier_type_ref_id,
          cleanString(row.code).toUpperCase(),
          cleanString(row.name),
          nullableString(row.city),
          nullableString(row.phone),
          preview.resolved.payment_method_ref_id,
          nullableNumber(row.payment_terms) ?? 30,
        ],
      );
      inserted.push(insertRows[0]);
    }
    await logMgxAudit(db, req, {
      companyId,
      action: "IMPORT",
      entityType: "mgx_suppliers",
      newData: { import_type: "suppliers", inserted: inserted.length, skipped: skipped.length, errors: errored.length },
    });
    await db.query("COMMIT");
    res.json({
      import_type: "suppliers",
      summary: { total: previewRows.length, success: inserted.length, warnings: skipped.length, errors: errored.length },
      items: inserted,
      warnings: skipped,
      errors: errored,
    });
  } catch (error) {
    try { await db.query("ROLLBACK"); } catch {}
    throw error;
  } finally {
    db.release();
  }
};

const importDrivers = async (req, res) => {
  const companyId = getCompanyId(req);
  const { rows, columns, mappings, commit } = parseImportRequest(req, driverImportConfig.requiredFields);
  if (!mappings) {
    return res.json(buildPreviewResponse({
      importType: driverImportConfig.importType,
      columns,
      requiredFields: driverImportConfig.requiredFields,
      sampleRows: rows.slice(0, 10),
    }));
  }

  const db = await pool.connect();
  try {
    await ensureMgxDriversTables(db);
    const depotMaps = await fetchAllDepotMaps(db);
    const { rows: existingRows } = await db.query(
      "SELECT id, company_id, code FROM mgx_drivers",
    );
    const existingByCompanyAndCode = new Map(existingRows.map((row) => [`${row.company_id}:${lower(row.code)}`, row]));
    const seenCodes = new Set();

    const previewRows = rows.map((sourceRow, index) => {
      const row = projectRow(sourceRow, mappings);
      const errors = [];
      const warnings = [];
      const rowCompanyId = nullableNumber(row.company_id) || companyId;
      const rowDepotId = nullableNumber(row.depot_id);
      const codeKey = lower(row.code);
      const companyCodeKey = `${rowCompanyId}:${codeKey}`;
      const depot = findAnyDepot(depotMaps, {
        depotId: rowDepotId,
        depotCode: row.depot_code,
      });
      const duplicatedInFile = codeKey && seenCodes.has(companyCodeKey);

      if (!rowCompanyId) errors.push("ID société requis");
      if (row.depot_id && !rowDepotId) errors.push(`ID dépôt invalide: ${row.depot_id}`);
      if (!cleanString(row.code)) errors.push("Code chauffeur requis");
      if (!cleanString(row.full_name)) errors.push("Nom complet requis");
      if ((row.depot_id || row.depot_code) && !depot) errors.push(`Dépôt '${row.depot_id || row.depot_code}' introuvable`);
      if (row.license_expiry && !parseDateValue(row.license_expiry)) errors.push(`Date permis invalide: ${row.license_expiry}`);
      if (existingByCompanyAndCode.has(companyCodeKey)) warnings.push(`Code ${row.code} déjà existant pour la société ${rowCompanyId} — sera ignoré`);
      if (duplicatedInFile) warnings.push(`Code ${row.code} dupliqué dans le fichier — sera ignoré`);
      if (codeKey) seenCodes.add(companyCodeKey);

      return {
        row_number: index + 1,
        values: row,
        errors,
        warnings,
        valid: errors.length === 0 && !existingByCompanyAndCode.has(companyCodeKey) && !duplicatedInFile,
        resolved: {
          company_id: rowCompanyId,
          depot_id: depot?.id || null,
          license_expiry: parseDateValue(row.license_expiry),
        },
      };
    });

    if (!commit) {
      return res.json(buildPreviewResponse({
        importType: driverImportConfig.importType,
        columns,
        requiredFields: driverImportConfig.requiredFields,
        sampleRows: rows.slice(0, 5),
        mappings,
        previewRows: previewRows.slice(0, 50),
        summary: {
          total: previewRows.length,
          success: previewRows.filter((item) => item.valid).length,
          warnings: previewRows.filter((item) => item.warnings.length).length,
          errors: previewRows.filter((item) => item.errors.length).length,
        },
      }));
    }

    await db.query("BEGIN");
    const inserted = [];
    const skipped = [];
    const errored = [];
    for (const preview of previewRows) {
      if (!preview.valid) {
        if (preview.errors.length) errored.push(preview);
        else skipped.push(preview);
        continue;
      }
      const row = preview.values;
      const { rows: insertRows } = await db.query(
        `
          INSERT INTO mgx_drivers (
            company_id, depot_id, code, full_name, phone, license_number,
            license_category, license_expiry, status, is_active, created_by
          )
          VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'active',true,$9)
          RETURNING id, code, full_name
        `,
        [
          preview.resolved.company_id,
          preview.resolved.depot_id,
          cleanString(row.code).toUpperCase(),
          cleanString(row.full_name),
          nullableString(row.phone),
          nullableString(row.license_number),
          nullableString(row.license_category),
          preview.resolved.license_expiry,
          req.user?.sub || null,
        ],
      );
      inserted.push(insertRows[0]);
    }
    await logMgxAudit(db, req, {
      companyId,
      action: "IMPORT",
      entityType: "mgx_drivers",
      newData: { import_type: "drivers", inserted: inserted.length, skipped: skipped.length, errors: errored.length },
    });
    await db.query("COMMIT");
    return res.json({
      import_type: "drivers",
      summary: {
        total: previewRows.length,
        success: inserted.length,
        warnings: skipped.length,
        errors: errored.length,
      },
      items: inserted,
      warnings: skipped,
      errors: errored,
    });
  } catch (error) {
    try { await db.query("ROLLBACK"); } catch {}
    throw error;
  } finally {
    db.release();
  }
};

const importFuel = async (req, res) => {
  const companyId = getCompanyId(req);
  const { rows, columns, mappings, commit } = parseImportRequest(req, fuelImportConfig.requiredFields);
  if (!mappings) {
    return res.json(buildPreviewResponse({
      importType: fuelImportConfig.importType,
      columns,
      requiredFields: fuelImportConfig.requiredFields,
      sampleRows: rows.slice(0, 10),
    }));
  }
  const db = await pool.connect();
  try {
    const [fuelTypeMap, depotMaps] = await Promise.all([
      fetchReferentielMap(db, companyId, "fuel_type"),
      fetchDepotMaps(db, companyId),
    ]);
    const { rows: vehicleRows } = await db.query(
      "SELECT id, plate_number, depot_id FROM mgx_vehicles WHERE company_id = $1 AND is_active = true",
      [companyId],
    );
    const vehicleMap = new Map(vehicleRows.map((row) => [lower(row.plate_number), row]));
    const previewRows = rows.map((sourceRow, index) => {
      const row = projectRow(sourceRow, mappings);
      const errors = [];
      const vehicle = vehicleMap.get(lower(row.vehicle_plate));
      const fuelType = fuelTypeMap.get(lower(row.fuel_type_label));
      const depot = row.depot_code ? findDepot(depotMaps, row.depot_code) : null;
      if (!parseDateValue(row.entry_date)) errors.push("Date invalide");
      if (!vehicle) errors.push(`Véhicule '${row.vehicle_plate}' introuvable`);
      if (!fuelType) errors.push(`Type de carburant '${row.fuel_type_label}' non trouvé dans les référentiels`);
      if (toNumber(row.quantity) <= 0) errors.push("Quantité invalide");
      if (toNumber(row.unit_price) < 0) errors.push("Prix unitaire invalide");
      if (nullableNumber(row.km_at_fillup) === null) errors.push("Kilométrage requis");
      return {
        row_number: index + 1,
        values: row,
        errors,
        warnings: [],
        valid: errors.length === 0,
        resolved: {
          vehicle_id: vehicle?.id || null,
          depot_id: depot?.id || vehicle?.depot_id || null,
          fuel_type_ref_id: fuelType?.id || null,
        },
      };
    });
    if (!commit) {
      return res.json(buildPreviewResponse({
        importType: fuelImportConfig.importType,
        columns,
        requiredFields: fuelImportConfig.requiredFields,
        sampleRows: rows.slice(0, 5),
        mappings,
        previewRows: previewRows.slice(0, 50),
        summary: {
          total: previewRows.length,
          success: previewRows.filter((item) => item.valid).length,
          warnings: 0,
          errors: previewRows.filter((item) => item.errors.length).length,
        },
      }));
    }
    await db.query("BEGIN");
    const inserted = [];
    const errored = [];
    for (const preview of previewRows) {
      if (!preview.valid) {
        errored.push(preview);
        continue;
      }
      const row = preview.values;
      const totalAmount = nullableNumber(row.total_amount) ?? toNumber(row.quantity) * toNumber(row.unit_price);
      const { rows: insertRows } = await db.query(
        `
          INSERT INTO mgx_fuel_entries (
            company_id, vehicle_id, depot_id, fuel_type_ref_id, entry_date,
            quantity, unit_price, total_amount, km_at_fillup, station_supplier,
            receipt_number, notes, created_by
          )
          VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
          RETURNING id
        `,
        [
          companyId,
          preview.resolved.vehicle_id,
          preview.resolved.depot_id,
          preview.resolved.fuel_type_ref_id,
          parseDateValue(row.entry_date),
          toNumber(row.quantity),
          toNumber(row.unit_price),
          totalAmount,
          toNumber(row.km_at_fillup),
          nullableString(row.station_supplier),
          nullableString(row.receipt_number),
          nullableString(row.notes),
          req.user?.sub || null,
        ],
      );
      await db.query(
        `
          UPDATE mgx_vehicles
          SET km_current = GREATEST(COALESCE(km_current, 0), $1), updated_at = CURRENT_TIMESTAMP
          WHERE id = $2
        `,
        [toNumber(row.km_at_fillup), preview.resolved.vehicle_id],
      );
      inserted.push(insertRows[0]);
    }
    await logMgxAudit(db, req, {
      companyId,
      action: "IMPORT",
      entityType: "mgx_fuel_entries",
      newData: { import_type: "fuel", inserted: inserted.length, errors: errored.length },
    });
    await db.query("COMMIT");
    res.json({
      import_type: "fuel",
      summary: { total: previewRows.length, success: inserted.length, warnings: 0, errors: errored.length },
      items: inserted,
      warnings: [],
      errors: errored,
    });
  } catch (error) {
    try { await db.query("ROLLBACK"); } catch {}
    throw error;
  } finally {
    db.release();
  }
};

const importConsumables = async (req, res) => {
  const companyId = getCompanyId(req);
  const { rows, columns, mappings, commit } = parseImportRequest(req, consumableImportConfig.requiredFields);
  if (!mappings) {
    return res.json(buildPreviewResponse({
      importType: consumableImportConfig.importType,
      columns,
      requiredFields: consumableImportConfig.requiredFields,
      sampleRows: rows.slice(0, 10),
    }));
  }
  const db = await pool.connect();
  try {
    const [categoryMap, unitMap] = await Promise.all([
      fetchReferentielMap(db, companyId, "consumable_category"),
      fetchReferentielMap(db, companyId, "unit_of_measure"),
    ]);
    const { rows: existingRows } = await db.query("SELECT id, code FROM mgx_consumable_items WHERE company_id = $1", [companyId]);
    const existingByCode = new Map(existingRows.map((row) => [lower(row.code), row]));
    const previewRows = rows.map((sourceRow, index) => {
      const row = projectRow(sourceRow, mappings);
      const errors = [];
      const warnings = [];
      const category = categoryMap.get(lower(row.category_label));
      const unit = unitMap.get(lower(row.unit_label));
      if (!cleanString(row.code)) errors.push("Code requis");
      if (!cleanString(row.label)) errors.push("Libellé requis");
      if (!category) errors.push(`Catégorie '${row.category_label}' introuvable`);
      if (!unit) errors.push(`Unité '${row.unit_label}' introuvable`);
      if (existingByCode.has(lower(row.code))) warnings.push(`Code ${row.code} déjà existant — sera ignoré`);
      return {
        row_number: index + 1,
        values: row,
        errors,
        warnings,
        valid: errors.length === 0 && !existingByCode.has(lower(row.code)),
        resolved: {
          category_ref_id: category?.id || null,
          unit_ref_id: unit?.id || null,
        },
      };
    });
    if (!commit) {
      return res.json(buildPreviewResponse({
        importType: consumableImportConfig.importType,
        columns,
        requiredFields: consumableImportConfig.requiredFields,
        sampleRows: rows.slice(0, 5),
        mappings,
        previewRows: previewRows.slice(0, 50),
        summary: {
          total: previewRows.length,
          success: previewRows.filter((item) => item.valid).length,
          warnings: previewRows.filter((item) => item.warnings.length).length,
          errors: previewRows.filter((item) => item.errors.length).length,
        },
      }));
    }
    await db.query("BEGIN");
    const inserted = [];
    const skipped = [];
    const errored = [];
    for (const preview of previewRows) {
      if (!preview.valid) {
        if (preview.errors.length) errored.push(preview);
        else skipped.push(preview);
        continue;
      }
      const row = preview.values;
      const { rows: insertRows } = await db.query(
        `
          INSERT INTO mgx_consumable_items (
            company_id, code, label, category_ref_id, unit_ref_id,
            unit_price, min_stock_qty, is_active
          )
          VALUES ($1,$2,$3,$4,$5,$6,$7,true)
          RETURNING id, code, label
        `,
        [
          companyId,
          cleanString(row.code).toUpperCase(),
          cleanString(row.label),
          preview.resolved.category_ref_id,
          preview.resolved.unit_ref_id,
          toNumber(row.unit_price),
          toNumber(row.min_stock),
        ],
      );
      inserted.push(insertRows[0]);
    }
    await logMgxAudit(db, req, {
      companyId,
      action: "IMPORT",
      entityType: "mgx_consumable_items",
      newData: { import_type: "consumables", inserted: inserted.length, skipped: skipped.length, errors: errored.length },
    });
    await db.query("COMMIT");
    res.json({
      import_type: "consumables",
      summary: { total: previewRows.length, success: inserted.length, warnings: skipped.length, errors: errored.length },
      items: inserted,
      warnings: skipped,
      errors: errored,
    });
  } catch (error) {
    try { await db.query("ROLLBACK"); } catch {}
    throw error;
  } finally {
    db.release();
  }
};

const importPurchases = async (req, res) => {
  const companyId = getCompanyId(req);
  const { rows, columns, mappings, commit } = parseImportRequest(req, purchaseImportConfig.requiredFields);
  if (!mappings) {
    return res.json(buildPreviewResponse({
      importType: purchaseImportConfig.importType,
      columns,
      requiredFields: purchaseImportConfig.requiredFields,
      sampleRows: rows.slice(0, 10),
    }));
  }
  const db = await pool.connect();
  try {
    const [categoryMap, unitMap, depotMaps] = await Promise.all([
      fetchReferentielMap(db, companyId, "purchase_category"),
      fetchReferentielMap(db, companyId, "unit_of_measure"),
      fetchDepotMaps(db, companyId),
    ]);
    const { rows: supplierRows } = await db.query("SELECT id, code FROM mgx_suppliers WHERE company_id = $1 AND is_active = true", [companyId]);
    const supplierMap = new Map(supplierRows.map((row) => [lower(row.code), row]));
    const previewRows = rows.map((sourceRow, index) => {
      const row = projectRow(sourceRow, mappings);
      const errors = [];
      const supplier = supplierMap.get(lower(row.supplier_code));
      const category = categoryMap.get(lower(row.category_label));
      const unit = unitMap.get(lower(row.unit_label));
      const depot = findDepot(depotMaps, row.depot_code);
      if (!parseDateValue(row.order_date)) errors.push("Date commande invalide");
      if (!supplier) errors.push(`Fournisseur '${row.supplier_code}' introuvable`);
      if (!category) errors.push(`Catégorie achat '${row.category_label}' introuvable`);
      if (!depot) errors.push(`Dépôt '${row.depot_code}' introuvable`);
      if (!cleanString(row.description)) errors.push("Description ligne requise");
      if (toNumber(row.qty) <= 0) errors.push("Quantité invalide");
      if (toNumber(row.unit_price) < 0) errors.push("Prix unitaire invalide");
      return {
        row_number: index + 1,
        values: row,
        errors,
        warnings: [],
        valid: errors.length === 0,
        resolved: {
          supplier_id: supplier?.id || null,
          category_ref_id: category?.id || null,
          depot_id: depot?.id || null,
          unit_ref_id: unit?.id || null,
        },
      };
    });
    if (!commit) {
      return res.json(buildPreviewResponse({
        importType: purchaseImportConfig.importType,
        columns,
        requiredFields: purchaseImportConfig.requiredFields,
        sampleRows: rows.slice(0, 5),
        mappings,
        previewRows: previewRows.slice(0, 50),
        summary: {
          total: previewRows.length,
          success: previewRows.filter((item) => item.valid).length,
          warnings: 0,
          errors: previewRows.filter((item) => item.errors.length).length,
        },
      }));
    }
    await db.query("BEGIN");
    const grouped = new Map();
    const errors = [];
    for (const preview of previewRows) {
      if (!preview.valid) {
        errors.push(preview);
        continue;
      }
      const row = preview.values;
      const key = [
        parseDateValue(row.order_date),
        preview.resolved.supplier_id,
        preview.resolved.category_ref_id,
        preview.resolved.depot_id,
      ].join(":");
      if (!grouped.has(key)) {
        grouped.set(key, {
          order_date: parseDateValue(row.order_date),
          supplier_id: preview.resolved.supplier_id,
          category_ref_id: preview.resolved.category_ref_id,
          depot_id: preview.resolved.depot_id,
          lines: [],
        });
      }
      grouped.get(key).lines.push({
        description: cleanString(row.description),
        quantity_ordered: toNumber(row.qty),
        unit_price: toNumber(row.unit_price),
        total: toNumber(row.qty) * toNumber(row.unit_price),
        unit_ref_id: preview.resolved.unit_ref_id,
      });
    }
    const inserted = [];
    let sequence = 1;
    for (const group of grouped.values()) {
      const total = group.lines.reduce((sum, line) => sum + line.total, 0);
      const orderNumber = `BC-IMP-${new Date().toISOString().slice(0, 10).replace(/-/g, "")}-${String(sequence).padStart(3, "0")}`;
      sequence += 1;
      const { rows: orderRows } = await db.query(
        `
          INSERT INTO mgx_purchase_orders (
            company_id, depot_id, supplier_id, order_number, order_date,
            payment_terms, category_ref_id, total_ht, total_ttc, notes, created_by
          )
          VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
          RETURNING id, order_number
        `,
        [
          companyId,
          group.depot_id,
          group.supplier_id,
          orderNumber,
          group.order_date,
          30,
          group.category_ref_id,
          total,
          total,
          "Import MGX",
          req.user?.sub || null,
        ],
      );
      const orderId = Number(orderRows[0].id);
      for (const line of group.lines) {
        await db.query(
          `
            INSERT INTO mgx_purchase_order_lines (
              order_id, description, quantity_ordered, quantity_received,
              unit_price, total, unit_ref_id, notes
            )
            VALUES ($1,$2,$3,0,$4,$5,$6,$7)
          `,
          [orderId, line.description, line.quantity_ordered, line.unit_price, line.total, line.unit_ref_id, "Import MGX"],
        );
      }
      inserted.push(orderRows[0]);
    }
    await logMgxAudit(db, req, {
      companyId,
      action: "IMPORT",
      entityType: "mgx_purchase_orders",
      newData: { import_type: "purchases", inserted_orders: inserted.length, errors: errors.length },
    });
    await db.query("COMMIT");
    res.json({
      import_type: "purchases",
      summary: { total: previewRows.length, success: inserted.length, warnings: 0, errors: errors.length },
      items: inserted,
      warnings: [],
      errors,
    });
  } catch (error) {
    try { await db.query("ROLLBACK"); } catch {}
    throw error;
  } finally {
    db.release();
  }
};

const hasLoginHistoryModuleColumn = async (db) => {
  const { rows } = await db.query(
    `
      SELECT EXISTS (
        SELECT 1
        FROM information_schema.columns
        WHERE table_name = 'login_history'
          AND column_name = 'module_accessed'
      ) AS present
    `,
  );
  return Boolean(rows[0]?.present);
};

const buildAuditRows = (rows) =>
  rows.map((row) => ({
    id: Number(row.id),
    company_id: row.company_id ? Number(row.company_id) : null,
    user_id: row.user_id ? Number(row.user_id) : null,
    user_name: row.user_full_name || row.user_email || "Système",
    action: row.action,
    action_type: inferAuditActionType(row.action),
    module: inferMgxModule(row.entity_type, row.action),
    entity_type: row.entity_type,
    entity_id: row.entity_id ? Number(row.entity_id) : null,
    entity_label: row.entity_id ? `${row.entity_type} #${row.entity_id}` : row.entity_type,
    ip_address: row.ip_address || null,
    detail: buildAuditDetail(row.old_data, row.new_data),
    old_data: row.old_data,
    new_data: row.new_data,
    created_at: row.created_at ? new Date(row.created_at).toISOString() : null,
  }));

const baseAuditQuery = `
  SELECT
    al.id,
    al.company_id,
    al.user_id,
    al.action,
    al.entity_type,
    al.entity_id,
    al.old_data,
    al.new_data,
    al.ip_address,
    al.created_at,
    u.full_name AS user_full_name,
    u.email AS user_email
  FROM mgx_audit_log al
  LEFT JOIN users u ON u.id = al.user_id
`;

const getVehiclesExport = async (db, companyId, req) => {
  const params = [companyId];
  const filters = ["v.company_id = $1"];
  if (req.query.depot_id) {
    params.push(Number(req.query.depot_id));
    filters.push(`v.depot_id = $${params.length}`);
  }
  if (req.query.category) {
    params.push(Number(req.query.category));
    filters.push(`v.category_ref_id = $${params.length}`);
  }
  if (req.query.status) {
    params.push(Number(req.query.status));
    filters.push(`v.status_ref_id = $${params.length}`);
  }
  if (String(req.query.active_only || "true") === "true") {
    filters.push("v.is_active = true");
  }
  const { rows } = await db.query(
    `
      SELECT
        v.reference,
        v.plate_number,
        v.brand,
        v.model,
        category.label AS category_label,
        status.label AS status_label,
        fuel.label AS fuel_type_label,
        depot.label AS depot_label,
        v.km_initial,
        v.km_current,
        v.acquisition_date,
        v.acquisition_value,
        v.assigned_driver,
        v.is_active
      FROM mgx_vehicles v
      LEFT JOIN mgx_referentiels category ON category.id = v.category_ref_id
      LEFT JOIN mgx_referentiels status ON status.id = v.status_ref_id
      LEFT JOIN mgx_referentiels fuel ON fuel.id = v.fuel_type_ref_id
      LEFT JOIN mgx_depots depot ON depot.id = v.depot_id
      WHERE ${filters.join(" AND ")}
      ORDER BY v.reference ASC
    `,
    params,
  );
  return rows;
};

const getFuelExportRows = async (db, companyId, req) => {
  const params = [companyId];
  const filters = ["f.company_id = $1"];
  if (req.query.from) {
    params.push(req.query.from);
    filters.push(`f.entry_date >= $${params.length}`);
  }
  if (req.query.to) {
    params.push(req.query.to);
    filters.push(`f.entry_date <= $${params.length}`);
  }
  const { rows } = await db.query(
    `
      SELECT
        f.entry_date,
        v.plate_number,
        v.reference,
        depot.label AS depot_label,
        fuel.label AS fuel_type_label,
        f.quantity,
        f.unit_price,
        f.total_amount,
        f.km_at_fillup,
        f.consumption_per_100,
        f.station_supplier,
        f.is_anomaly
      FROM mgx_fuel_entries f
      LEFT JOIN mgx_vehicles v ON v.id = f.vehicle_id
      LEFT JOIN mgx_depots depot ON depot.id = f.depot_id
      LEFT JOIN mgx_referentiels fuel ON fuel.id = f.fuel_type_ref_id
      WHERE ${filters.join(" AND ")}
      ORDER BY f.entry_date DESC, f.id DESC
    `,
    params,
  );
  return rows;
};

const getMaintenanceExportRows = async (db, companyId, req) => {
  const params = [companyId];
  const filters = ["i.company_id = $1"];
  if (req.query.from) {
    params.push(req.query.from);
    filters.push(`i.start_date >= $${params.length}`);
  }
  if (req.query.to) {
    params.push(req.query.to);
    filters.push(`i.start_date <= $${params.length}`);
  }
  const { rows } = await db.query(
    `
      SELECT
        i.start_date,
        v.plate_number,
        mt.label AS maintenance_type_label,
        i.label,
        s.name AS supplier_name,
        i.parts_cost,
        i.labor_cost,
        i.total_cost,
        st.label AS status_label,
        i.next_date,
        i.next_km
      FROM mgx_interventions i
      LEFT JOIN mgx_vehicles v ON v.id = i.vehicle_id
      LEFT JOIN mgx_referentiels mt ON mt.id = i.maintenance_type_ref_id
      LEFT JOIN mgx_referentiels st ON st.id = i.status_ref_id
      LEFT JOIN mgx_suppliers s ON s.id = i.supplier_id
      WHERE ${filters.join(" AND ")}
      ORDER BY i.start_date DESC, i.id DESC
    `,
    params,
  );
  return rows;
};

const getTreasuryExportRows = async (db, companyId, req) => {
  const params = [companyId];
  const filters = ["tm.company_id = $1"];
  if (req.query.from) {
    params.push(req.query.from);
    filters.push(`tm.movement_date >= $${params.length}`);
  }
  if (req.query.to) {
    params.push(req.query.to);
    filters.push(`tm.movement_date <= $${params.length}`);
  }
  const { rows } = await db.query(
    `
      SELECT
        tm.movement_date,
        tm.treasury_type,
        tm.account_name,
        tm.direction,
        tm.amount,
        tm.label,
        expense.label AS expense_type_label,
        depot.label AS depot_label,
        tm.receipt_number
      FROM mgx_treasury_movements tm
      LEFT JOIN mgx_referentiels expense ON expense.id = tm.expense_type_ref_id
      LEFT JOIN mgx_depots depot ON depot.id = tm.depot_id
      WHERE ${filters.join(" AND ")}
      ORDER BY tm.account_name ASC, tm.movement_date ASC, tm.id ASC
    `,
    params,
  );
  let runningByAccount = new Map();
  return rows.map((row) => {
    const key = row.account_name || "Sans compte";
    const previous = runningByAccount.get(key) || 0;
    const next = previous + (row.direction === "in" ? toNumber(row.amount) : -toNumber(row.amount));
    runningByAccount.set(key, next);
    return { ...row, running_balance: next };
  });
};

const getSuppliersExportRows = async (db, companyId) => {
  const { rows } = await db.query(
    `
      SELECT
        s.code,
        s.name,
        type.label AS supplier_type_label,
        s.city,
        s.phone,
        s.email,
        s.current_balance,
        s.is_active
      FROM mgx_suppliers s
      LEFT JOIN mgx_referentiels type ON type.id = s.supplier_type_ref_id
      WHERE s.company_id = $1
      ORDER BY s.name ASC
    `,
    [companyId],
  );
  return rows;
};

export const mgxAdminOpsController = {
  importVehicles,
  importSuppliers,
  importDrivers,
  importFuel,
  importPurchases,
  importConsumables,

  async exportVehicles(req, res) {
    const companyId = getCompanyId(req);
    const format = lower(req.query.format || "excel");
    const db = await pool.connect();
    try {
      const [company, rows] = await Promise.all([fetchCompany(db, companyId), getVehiclesExport(db, companyId, req)]);
      await logMgxAudit(db, req, {
        companyId,
        action: "EXPORT",
        entityType: "mgx_vehicles",
        newData: { format, rows: rows.length },
      });
      const report = buildBaseReport({
        reportKey: "mgx-vehicles",
        title: "Liste des véhicules MGX",
        company,
        sections: [
          {
            title: "Véhicules",
            columns: [
              { key: "reference", label: "Référence" },
              { key: "plate_number", label: "Immatriculation" },
              { key: "brand", label: "Marque" },
              { key: "model", label: "Modèle" },
              { key: "category_label", label: "Catégorie" },
              { key: "status_label", label: "Statut" },
              { key: "fuel_type_label", label: "Carburant" },
              { key: "depot_label", label: "Dépôt" },
              { key: "km_current", label: "Km actuel" },
            ],
            rows,
            empty_message: "Aucun véhicule trouvé.",
          },
        ],
      });
      await sendExport(res, report, format);
    } finally {
      db.release();
    }
  },

  async exportFuel(req, res) {
    const companyId = getCompanyId(req);
    const format = lower(req.query.format || "excel");
    const db = await pool.connect();
    try {
      const [company, rows] = await Promise.all([fetchCompany(db, companyId), getFuelExportRows(db, companyId, req)]);
      await logMgxAudit(db, req, {
        companyId,
        action: "EXPORT",
        entityType: "mgx_fuel_entries",
        newData: { format, rows: rows.length },
      });
      const report = buildBaseReport({
        reportKey: "mgx-fuel-history",
        title: "Historique carburant MGX",
        company,
        sections: [{
          title: "Saisies carburant",
          columns: [
            { key: "entry_date", label: "Date", type: "date" },
            { key: "plate_number", label: "Véhicule" },
            { key: "fuel_type_label", label: "Carburant" },
            { key: "quantity", label: "Quantité" },
            { key: "unit_price", label: "Prix unit.", type: "amount" },
            { key: "total_amount", label: "Montant", type: "amount" },
            { key: "km_at_fillup", label: "Km" },
            { key: "station_supplier", label: "Station" },
          ],
          rows,
          empty_message: "Aucune saisie carburant.",
        }],
      });
      await sendExport(res, report, format);
    } finally {
      db.release();
    }
  },

  async exportSuppliers(req, res) {
    const companyId = getCompanyId(req);
    const format = lower(req.query.format || "excel");
    const db = await pool.connect();
    try {
      const [company, rows] = await Promise.all([fetchCompany(db, companyId), getSuppliersExportRows(db, companyId)]);
      await logMgxAudit(db, req, {
        companyId,
        action: "EXPORT",
        entityType: "mgx_suppliers",
        newData: { format, rows: rows.length },
      });
      const report = buildBaseReport({
        reportKey: "mgx-suppliers",
        title: "Liste des fournisseurs MGX",
        company,
        sections: [{
          title: "Fournisseurs & prestataires",
          columns: [
            { key: "code", label: "Code" },
            { key: "name", label: "Nom" },
            { key: "supplier_type_label", label: "Type" },
            { key: "city", label: "Ville" },
            { key: "phone", label: "Téléphone" },
            { key: "email", label: "Email" },
            { key: "current_balance", label: "Balance", type: "amount" },
            { key: "is_active", label: "Actif", type: "boolean" },
          ],
          rows,
          empty_message: "Aucun fournisseur.",
        }],
      });
      await sendExport(res, report, format);
    } finally {
      db.release();
    }
  },

  async exportMaintenance(req, res) {
    const companyId = getCompanyId(req);
    const format = lower(req.query.format || "excel");
    const db = await pool.connect();
    try {
      const [company, rows] = await Promise.all([fetchCompany(db, companyId), getMaintenanceExportRows(db, companyId, req)]);
      await logMgxAudit(db, req, {
        companyId,
        action: "EXPORT",
        entityType: "mgx_interventions",
        newData: { format, rows: rows.length },
      });
      const report = buildBaseReport({
        reportKey: "mgx-maintenance-history",
        title: "Historique entretiens MGX",
        company,
        sections: [{
          title: "Interventions",
          columns: [
            { key: "start_date", label: "Date", type: "date" },
            { key: "plate_number", label: "Véhicule" },
            { key: "maintenance_type_label", label: "Type" },
            { key: "label", label: "Libellé" },
            { key: "supplier_name", label: "Prestataire" },
            { key: "total_cost", label: "Coût", type: "amount" },
            { key: "status_label", label: "Statut" },
          ],
          rows,
          empty_message: "Aucune intervention.",
        }],
      });
      await sendExport(res, report, format);
    } finally {
      db.release();
    }
  },

  async exportTreasury(req, res) {
    const companyId = getCompanyId(req);
    const format = lower(req.query.format || "excel");
    const db = await pool.connect();
    try {
      const [company, rows] = await Promise.all([fetchCompany(db, companyId), getTreasuryExportRows(db, companyId, req)]);
      await logMgxAudit(db, req, {
        companyId,
        action: "EXPORT",
        entityType: "mgx_treasury_movements",
        newData: { format, rows: rows.length },
      });
      const report = buildBaseReport({
        reportKey: "mgx-treasury-journal",
        title: "Journal de trésorerie MGX",
        company,
        sections: [{
          title: "Mouvements",
          columns: [
            { key: "movement_date", label: "Date", type: "date" },
            { key: "treasury_type", label: "Type" },
            { key: "account_name", label: "Compte" },
            { key: "label", label: "Libellé" },
            { key: "expense_type_label", label: "Type dépense" },
            { key: "amount", label: "Montant", type: "amount" },
            { key: "running_balance", label: "Solde", type: "amount" },
            { key: "depot_label", label: "Dépôt" },
          ],
          rows,
          empty_message: "Aucun mouvement de trésorerie.",
        }],
      });
      await sendExport(res, report, format);
    } finally {
      db.release();
    }
  },

  async listAudit(req, res) {
    const companyId = getCompanyId(req);
    const params = [companyId];
    const filters = ["al.company_id = $1"];
    if (req.query.user_id) {
      params.push(Number(req.query.user_id));
      filters.push(`al.user_id = $${params.length}`);
    }
    if (req.query.from) {
      params.push(req.query.from);
      filters.push(`al.created_at >= $${params.length}::timestamptz`);
    }
    if (req.query.to) {
      params.push(req.query.to);
      filters.push(`al.created_at < ($${params.length}::date + INTERVAL '1 day')`);
    }
    if (req.query.action) {
      params.push(cleanString(req.query.action));
      filters.push(`upper(al.action) = upper($${params.length})`);
    }
    if (req.query.module) {
      const wantedModule = cleanString(req.query.module);
      filters.push(`(
        CASE
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%vehicle%' THEN 'Parc auto'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%fuel%' THEN 'Carburant'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%maintenance%' OR lower(coalesce(al.entity_type, '')) LIKE '%intervention%' THEN 'Maintenance'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%purchase%' OR lower(coalesce(al.entity_type, '')) LIKE '%reception%' THEN 'Achats'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%service%' THEN 'Prestations'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%rental%' THEN 'Locations'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%supplier%' THEN 'Fournisseurs'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%treasury%' THEN 'Trésorerie'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%consumable%' THEN 'Consommables'
          WHEN lower(coalesce(al.entity_type, '')) LIKE '%alert%' THEN 'Alertes'
          ELSE 'Paramétrage'
        END
      ) = $${params.length + 1}`);
      params.push(wantedModule);
    }
    const [auditResult, usersResult] = await Promise.all([
      pool.query(
        `
          ${baseAuditQuery}
          WHERE ${filters.join(" AND ")}
          ORDER BY al.created_at DESC
          LIMIT 500
        `,
        params,
      ),
      pool.query(
        `
          SELECT DISTINCT u.id, u.full_name
          FROM mgx_audit_log al
          JOIN users u ON u.id = al.user_id
          WHERE al.company_id = $1
          ORDER BY u.full_name ASC
        `,
        [companyId],
      ),
    ]);
    res.json({
      items: buildAuditRows(auditResult.rows),
      filters: {
        users: usersResult.rows.map((row) => ({ id: Number(row.id), full_name: row.full_name })),
      },
    });
  },

  async getAuditDetail(req, res) {
    const companyId = getCompanyId(req);
    const { rows } = await pool.query(
      `
        ${baseAuditQuery}
        WHERE al.company_id = $1
          AND al.id = $2
        LIMIT 1
      `,
      [companyId, Number(req.params.id)],
    );
    if (!rows[0]) {
      throw new ApiError(404, "Enregistrement d'audit MGX introuvable.");
    }
    res.json({ item: buildAuditRows(rows)[0] });
  },

  async listLogins(req, res) {
    const companyId = getCompanyId(req);
    const includeModuleColumn = await hasLoginHistoryModuleColumn(pool);
    const params = [companyId];
    const filters = ["(lh.company_id = $1 OR lh.company_id IS NULL)"];
    if (req.query.from) {
      params.push(req.query.from);
      filters.push(`lh.attempted_at >= $${params.length}::timestamptz`);
    }
    if (req.query.to) {
      params.push(req.query.to);
      filters.push(`lh.attempted_at < ($${params.length}::date + INTERVAL '1 day')`);
    }
    const { rows } = await pool.query(
      `
        SELECT
          lh.id,
          lh.company_id,
          lh.user_id,
          lh.email,
          lh.ip_address,
          lh.browser,
          lh.user_agent,
          lh.result,
          lh.reason,
          lh.attempted_at,
          ${includeModuleColumn ? "lh.module_accessed" : "NULL::text AS module_accessed"},
          u.full_name
        FROM login_history lh
        LEFT JOIN users u ON u.id = lh.user_id
        WHERE ${filters.join(" AND ")}
        ORDER BY lh.attempted_at DESC
        LIMIT 500
      `,
      params,
    );
    res.json({
      items: rows.map((row) => ({
        id: Number(row.id),
        company_id: row.company_id ? Number(row.company_id) : null,
        user_id: row.user_id ? Number(row.user_id) : null,
        user_name: row.full_name || row.email || "Inconnu",
        email: row.email,
        ip_address: row.ip_address || null,
        browser: row.browser || "Inconnu",
        user_agent: row.user_agent || "",
        result: row.result,
        reason: row.reason || null,
        module_accessed: row.module_accessed || "—",
        attempted_at: row.attempted_at ? new Date(row.attempted_at).toISOString() : null,
      })),
    });
  },
};
