import { pool } from "../db/pool.js";
import { runMgxAlertEngine } from "../services/mgxAlertEngine.js";
import { ApiError } from "../utils/ApiError.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 formatDzdAmount = (value) => {
  const amount = toNumber(value);
  const sign = amount < 0 ? "-" : "";
  const [integerPart, decimalPart] = Math.abs(amount).toFixed(2).split(".");
  return `${sign}${integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, " ")},${decimalPart} DZD`;
};

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

const resolveActorUserId = (req) =>
  req.user?.accountType === "mgx_mobile_account" ? null : req.user?.sub || null;

const resolveActorMobileAccountId = (req) =>
  req.user?.accountType === "mgx_mobile_account"
    ? Number(req.user?.mobileAccountId || req.user?.mobile_account_id || req.user?.sub || 0) || null
    : null;

const resolveLinkedStaffProfileId = (req) => {
  const linkedType = String(req.user?.linkedEntityType || req.user?.linked_entity_type || "").toLowerCase();
  if (linkedType !== "staff_profile") return null;
  return Number(req.user?.linkedEntityId || req.user?.linked_entity_id || 0) || null;
};

const ensureInterventionMobileColumns = async (db = pool) => {
  await db.query(`
    ALTER TABLE mgx_interventions
      ADD COLUMN IF NOT EXISTS assigned_to_staff_profile_id BIGINT REFERENCES mgx_staff_profiles (id) ON DELETE SET NULL,
      ADD COLUMN IF NOT EXISTS mobile_status VARCHAR(30) NOT NULL DEFAULT 'assigned',
      ADD COLUMN IF NOT EXISTS mobile_timeline JSONB NOT NULL DEFAULT '[]'::jsonb,
      ADD COLUMN IF NOT EXISTS mobile_photos JSONB NOT NULL DEFAULT '[]'::jsonb,
      ADD COLUMN IF NOT EXISTS work_summary TEXT,
      ADD COLUMN IF NOT EXISTS km_at_close NUMERIC(12, 2)
  `);
  await db.query(`
    CREATE INDEX IF NOT EXISTS idx_mgx_interventions_assigned_staff
      ON mgx_interventions (assigned_to_staff_profile_id, mobile_status)
  `);
};

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

const triggerAlertEngine = (req, companyId) => {
  runMgxAlertEngine({
    companyId,
    io: req.app.get("io"),
  }).catch((error) => {
    console.error("MGX alert engine trigger failed after maintenance event", error);
  });
};

const mapRef = (prefix, row) => row[`${prefix}_id`] ? ({
  id: Number(row[`${prefix}_id`]),
  code: row[`${prefix}_code`],
  label: row[`${prefix}_label`],
  color: row[`${prefix}_color`],
  icon: row[`${prefix}_icon`],
  metadata: row[`${prefix}_metadata`] || {},
}) : null;

const vehicleShape = (row) => row.vehicle_id ? ({
  id: Number(row.vehicle_id),
  reference: row.vehicle_reference,
  plate_number: row.plate_number,
  brand: row.vehicle_brand,
  model: row.vehicle_model,
  km_current: toNumber(row.vehicle_km_current),
  depot_id: row.vehicle_depot_id ? Number(row.vehicle_depot_id) : null,
}) : null;

const depotShape = (row) => row.depot_id ? ({
  id: Number(row.depot_id),
  code: row.depot_code,
  label: row.depot_label,
}) : null;

const supplierShape = (row) => row.supplier_id ? ({
  id: Number(row.supplier_id),
  code: row.supplier_code,
  name: row.supplier_name,
}) : null;

const calculatePlanStatus = (row) => {
  const currentKm = toNumber(row.vehicle_km_current);
  const nextKm = row.next_due_km === null ? null : toNumber(row.next_due_km);
  const nextDate = row.next_due_date ? new Date(row.next_due_date) : null;
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const daysRemaining = nextDate ? Math.ceil((nextDate.getTime() - today.getTime()) / 86400000) : null;
  const kmRemaining = nextKm === null ? null : nextKm - currentKm;

  if ((kmRemaining !== null && kmRemaining <= 0) || (daysRemaining !== null && daysRemaining < 0)) {
    return { key: "overdue", label: "🔴 En retard", tone: "danger", km_remaining: kmRemaining, days_remaining: daysRemaining };
  }
  if ((kmRemaining !== null && kmRemaining <= 500) || (daysRemaining !== null && daysRemaining <= 30)) {
    return { key: "soon", label: "⚠️ Bientôt", tone: "warning", km_remaining: kmRemaining, days_remaining: daysRemaining };
  }
  return { key: "ok", label: "✅ OK", tone: "success", km_remaining: kmRemaining, days_remaining: daysRemaining };
};

const mapPlan = (row) => ({
  id: Number(row.id),
  company_id: Number(row.company_id),
  vehicle_id: Number(row.vehicle_id),
  vehicle: vehicleShape(row),
  maintenance_type_ref_id: row.maintenance_type_ref_id ? Number(row.maintenance_type_ref_id) : null,
  maintenance_type: mapRef("maintenance", row),
  label: row.label,
  frequency_km: row.frequency_km === null ? null : toNumber(row.frequency_km),
  frequency_days: row.frequency_days === null ? null : Number(row.frequency_days),
  last_done_date: row.last_done_date,
  last_done_km: row.last_done_km === null ? null : toNumber(row.last_done_km),
  next_due_date: row.next_due_date,
  next_due_km: row.next_due_km === null ? null : toNumber(row.next_due_km),
  is_active: row.is_active,
  status: calculatePlanStatus(row),
  created_at: row.created_at,
  updated_at: row.updated_at,
});

const planSelect = `
  SELECT
    p.*,
    v.reference AS vehicle_reference,
    v.plate_number,
    v.brand AS vehicle_brand,
    v.model AS vehicle_model,
    v.km_current AS vehicle_km_current,
    v.depot_id AS vehicle_depot_id,
    maintenance.id AS maintenance_id,
    maintenance.code AS maintenance_code,
    maintenance.label AS maintenance_label,
    maintenance.color AS maintenance_color,
    maintenance.icon AS maintenance_icon,
    maintenance.metadata AS maintenance_metadata
  FROM mgx_maintenance_plans p
  JOIN mgx_vehicles v ON v.id = p.vehicle_id
  LEFT JOIN mgx_referentiels maintenance ON maintenance.id = p.maintenance_type_ref_id
`;

const mapInterventionLine = (row) => ({
  id: Number(row.id),
  intervention_id: Number(row.intervention_id),
  item_id: row.item_id ? Number(row.item_id) : null,
  unit_ref_id: row.unit_ref_id ? Number(row.unit_ref_id) : null,
  description: row.description,
  quantity: toNumber(row.quantity),
  unit_price: toNumber(row.unit_price),
  total: toNumber(row.total),
  line_type: row.line_type,
});

const mapIntervention = (row, lines = []) => ({
  id: Number(row.id),
  company_id: Number(row.company_id),
  vehicle_id: row.vehicle_id ? Number(row.vehicle_id) : null,
  vehicle: vehicleShape(row),
  depot_id: row.depot_id ? Number(row.depot_id) : null,
  depot: depotShape(row),
  intervention_type: row.intervention_type,
  maintenance_type_ref_id: row.maintenance_type_ref_id ? Number(row.maintenance_type_ref_id) : null,
  maintenance_type: mapRef("maintenance", row),
  label: row.label,
  description: row.description,
  start_date: row.start_date,
  end_date: row.end_date,
  km_at_intervention: row.km_at_intervention === null ? null : toNumber(row.km_at_intervention),
  supplier_id: row.supplier_id ? Number(row.supplier_id) : null,
  supplier: supplierShape(row),
  parts_cost: toNumber(row.parts_cost),
  labor_cost: toNumber(row.labor_cost),
  total_cost: toNumber(row.total_cost),
  total_cost_formatted: formatDzdAmount(row.total_cost),
  status_ref_id: row.status_ref_id ? Number(row.status_ref_id) : null,
  status: mapRef("status", row),
  assigned_to_staff_profile_id: row.assigned_to_staff_profile_id ? Number(row.assigned_to_staff_profile_id) : null,
  assigned_to: row.assigned_staff_id ? {
    id: Number(row.assigned_staff_id),
    full_name: row.assigned_staff_name,
    phone: row.assigned_staff_phone,
    depot_id: row.assigned_staff_depot_id ? Number(row.assigned_staff_depot_id) : null,
  } : null,
  mobile_status: row.mobile_status || "assigned",
  mobile_timeline: Array.isArray(row.mobile_timeline) ? row.mobile_timeline : [],
  mobile_photos: Array.isArray(row.mobile_photos) ? row.mobile_photos : [],
  work_summary: row.work_summary,
  km_at_close: row.km_at_close === null ? null : toNumber(row.km_at_close),
  invoice_number: row.invoice_number,
  file_url: row.file_url,
  notes: row.notes,
  next_km: row.next_km === null ? null : toNumber(row.next_km),
  next_date: row.next_date,
  lines,
  created_at: row.created_at,
  updated_at: row.updated_at,
});

const statusMatches = (status, matcher) => {
  if (!status) return false;
  const code = String(status.code || "").trim().toLowerCase();
  const label = String(status.label || "").trim().toLowerCase();
  return matcher(code, label);
};

const isCompletedInterventionStatus = (status) => statusMatches(
  status,
  (code, label) => code === "done" || label.includes("termin"),
);

const isCancelledInterventionStatus = (status) => statusMatches(
  status,
  (code, label) => code === "cancel" || label.includes("annul"),
);

const interventionSelect = `
  SELECT
    i.*,
    v.reference AS vehicle_reference,
    v.plate_number,
    v.brand AS vehicle_brand,
    v.model AS vehicle_model,
    v.km_current AS vehicle_km_current,
    v.depot_id AS vehicle_depot_id,
    d.code AS depot_code,
    d.label AS depot_label,
    maintenance.id AS maintenance_id,
    maintenance.code AS maintenance_code,
    maintenance.label AS maintenance_label,
    maintenance.color AS maintenance_color,
    maintenance.icon AS maintenance_icon,
    maintenance.metadata AS maintenance_metadata,
    status.id AS status_id,
    status.code AS status_code,
    status.label AS status_label,
    status.color AS status_color,
    status.icon AS status_icon,
    status.metadata AS status_metadata,
    supplier.code AS supplier_code,
    supplier.name AS supplier_name,
    assigned_staff.id AS assigned_staff_id,
    assigned_staff.full_name AS assigned_staff_name,
    assigned_staff.phone AS assigned_staff_phone,
    assigned_staff.depot_id AS assigned_staff_depot_id
  FROM mgx_interventions i
  LEFT JOIN mgx_vehicles v ON v.id = i.vehicle_id
  LEFT JOIN mgx_depots d ON d.id = i.depot_id
  LEFT JOIN mgx_referentiels maintenance ON maintenance.id = i.maintenance_type_ref_id
  LEFT JOIN mgx_referentiels status ON status.id = i.status_ref_id
  LEFT JOIN mgx_suppliers supplier ON supplier.id = i.supplier_id
  LEFT JOIN mgx_staff_profiles assigned_staff ON assigned_staff.id = i.assigned_to_staff_profile_id
`;

const buildPlanPayload = (body) => {
  const frequencyKm = nullableNumber(body.frequency_km);
  const frequencyDays = nullableNumber(body.frequency_days);
  const lastDoneKm = nullableNumber(body.last_done_km);
  const lastDoneDate = nullableString(body.last_done_date);
  let nextDueDate = nullableString(body.next_due_date);
  let nextDueKm = nullableNumber(body.next_due_km);

  if (!nextDueKm && lastDoneKm !== null && frequencyKm !== null) nextDueKm = lastDoneKm + frequencyKm;
  if (!nextDueDate && lastDoneDate && frequencyDays !== null) {
    const date = new Date(lastDoneDate);
    date.setDate(date.getDate() + frequencyDays);
    nextDueDate = date.toISOString().slice(0, 10);
  }

  return {
    company_id: Number(body.company_id),
    vehicle_id: nullableNumber(body.vehicle_id),
    maintenance_type_ref_id: nullableNumber(body.maintenance_type_ref_id),
    label: nullableString(body.label),
    frequency_km: frequencyKm,
    frequency_days: frequencyDays,
    last_done_date: lastDoneDate,
    last_done_km: lastDoneKm,
    next_due_date: nextDueDate,
    next_due_km: nextDueKm,
    is_active: body.is_active !== false,
  };
};

const normalizeLines = (lines = []) => lines
  .map((line) => {
    const quantity = toNumber(line.quantity, 1);
    const unitPrice = toNumber(line.unit_price);
    const rawLineType = cleanString(line.line_type || "part").toLowerCase();
    const normalizedLineType = rawLineType === "piece" ? "part" : rawLineType;
    return {
      item_id: nullableNumber(line.item_id),
      unit_ref_id: nullableNumber(line.unit_ref_id),
      description: nullableString(line.description),
      quantity,
      unit_price: unitPrice,
      total: line.total === "" || line.total === undefined || line.total === null ? quantity * unitPrice : toNumber(line.total),
      line_type: normalizedLineType,
    };
  })
  .filter((line) => line.description || line.total > 0);

const buildInterventionPayload = (body) => {
  const lines = normalizeLines(body.lines || []);
  const partsCost = lines
    .filter((line) => line.line_type === "part")
    .reduce((sum, line) => sum + line.total, 0);
  const laborCost = lines
    .filter((line) => line.line_type !== "part")
    .reduce((sum, line) => sum + line.total, 0);

  return {
    company_id: Number(body.company_id),
    vehicle_id: nullableNumber(body.vehicle_id),
    depot_id: nullableNumber(body.depot_id),
    intervention_type: cleanString(body.intervention_type || "preventive"),
    maintenance_type_ref_id: nullableNumber(body.maintenance_type_ref_id),
    label: nullableString(body.label),
    description: nullableString(body.description),
    start_date: nullableString(body.start_date) || new Date().toISOString().slice(0, 10),
    end_date: nullableString(body.end_date),
    km_at_intervention: nullableNumber(body.km_at_intervention),
    supplier_id: nullableNumber(body.supplier_id),
    assigned_to_staff_profile_id: nullableNumber(body.assigned_to_staff_profile_id || body.assigned_to),
    parts_cost: partsCost,
    labor_cost: laborCost,
    total_cost: partsCost + laborCost,
    status_ref_id: nullableNumber(body.status_ref_id),
    invoice_number: nullableString(body.invoice_number),
    file_url: nullableString(body.file_url),
    notes: nullableString(body.notes),
    next_km: nullableNumber(body.next_km),
    next_date: nullableString(body.next_date),
    lines,
  };
};

const fetchInterventionById = async (db, id, companyId) => {
  const { rows } = await db.query(
    `
      ${interventionSelect}
      WHERE i.id = $1
        AND i.company_id = $2
      LIMIT 1
    `,
    [id, companyId],
  );
  if (!rows[0]) return null;

  const { rows: lineRows } = await db.query(
    "SELECT * FROM mgx_intervention_lines WHERE intervention_id = $1 ORDER BY id ASC",
    [id],
  );
  return mapIntervention(rows[0], lineRows.map(mapInterventionLine));
};

const createConsumableMovementRow = async (db, {
  companyId,
  itemId,
  depotId,
  movementDate,
  quantity,
  unitPrice,
  totalValue,
  referenceType,
  referenceId,
  notes,
  createdBy,
}) => {
  const { rows } = await db.query(
    `
      INSERT INTO mgx_consumable_movements (
        company_id, item_id, depot_id, movement_type, movement_date,
        quantity, unit_price, total_value, reference_type, reference_id, notes, created_by
      )
      VALUES ($1, $2, $3, 'out', $4, $5, $6, $7, $8, $9, $10, $11)
      RETURNING id
    `,
    [
      companyId,
      itemId,
      depotId,
      movementDate,
      quantity,
      unitPrice,
      totalValue,
      referenceType,
      referenceId,
      notes,
      createdBy,
    ],
  );
  return Number(rows[0].id);
};

const upsertInterventionLines = async (db, interventionId, lines) => {
  await db.query("DELETE FROM mgx_intervention_lines WHERE intervention_id = $1", [interventionId]);
  for (const line of lines) {
    await db.query(
      `
        INSERT INTO mgx_intervention_lines (
          intervention_id, item_id, description, quantity, unit_ref_id, unit_price, total, line_type
        )
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
      `,
      [interventionId, line.item_id, line.description, line.quantity, line.unit_ref_id, line.unit_price, line.total, line.line_type],
    );
  }
};

const syncInterventionConsumableMovements = async (db, {
  companyId,
  interventionId,
  depotId,
  movementDate,
  lines,
  applyStock,
  createdBy,
}) => {
  const referenceType = "maintenance_intervention";
  const { rows: existingMovementRows } = await db.query(
    `
      SELECT id, item_id, quantity
      FROM mgx_consumable_movements
      WHERE company_id = $1
        AND reference_type = $2
        AND reference_id = $3
    `,
    [companyId, referenceType, interventionId],
  );

  for (const movement of existingMovementRows) {
    await db.query(
      `
        UPDATE mgx_consumable_items
        SET current_stock_qty = current_stock_qty + $2,
            updated_at = CURRENT_TIMESTAMP
        WHERE id = $1
      `,
      [movement.item_id, movement.quantity],
    );
  }

  if (existingMovementRows.length) {
    await db.query(
      `
        DELETE FROM mgx_consumable_movements
        WHERE company_id = $1
          AND reference_type = $2
          AND reference_id = $3
      `,
      [companyId, referenceType, interventionId],
    );
  }

  if (!applyStock) return;

  const consumableLines = lines.filter((line) => line.line_type === "part" && line.item_id && line.quantity > 0);
  for (const line of consumableLines) {
    const { rows: itemRows } = await db.query(
      `
        SELECT id, label, current_stock_qty, unit_price, is_consumable
        FROM mgx_consumable_items
        WHERE id = $1
          AND company_id = $2
        LIMIT 1
      `,
      [line.item_id, companyId],
    );
    const item = itemRows[0];
    if (!item) throw new ApiError(400, `Article consommable introuvable pour "${line.description || "ligne"}".`);
    if (!item.is_consumable) continue;
    if (toNumber(item.current_stock_qty) < toNumber(line.quantity)) {
      throw new ApiError(400, `Stock insuffisant pour "${item.label || line.description || "article"}".`);
    }

    await db.query(
      `
        UPDATE mgx_consumable_items
        SET current_stock_qty = current_stock_qty - $2,
            updated_at = CURRENT_TIMESTAMP
        WHERE id = $1
      `,
      [line.item_id, line.quantity],
    );

    await createConsumableMovementRow(db, {
      companyId,
      itemId: line.item_id,
      depotId,
      movementDate,
      quantity: line.quantity,
      unitPrice: line.unit_price ?? item.unit_price,
      totalValue: line.total || toNumber(line.quantity) * toNumber(line.unit_price ?? item.unit_price),
      referenceType,
      referenceId: interventionId,
      notes: line.description || null,
      createdBy,
    });
  }
};

const updateMatchingPlan = async (db, intervention) => {
  if (!intervention.vehicle_id || !intervention.maintenance_type_ref_id) return;

  await db.query(
    `
      UPDATE mgx_maintenance_plans
      SET
        last_done_date = COALESCE($1, last_done_date),
        last_done_km = COALESCE($2, last_done_km),
        next_due_date = COALESCE($3, next_due_date),
        next_due_km = COALESCE($4, next_due_km),
        updated_at = NOW()
      WHERE company_id = $5
        AND vehicle_id = $6
        AND maintenance_type_ref_id = $7
        AND is_active = true
    `,
    [
      intervention.end_date || intervention.start_date,
      intervention.km_at_intervention,
      intervention.next_date,
      intervention.next_km,
      intervention.company_id,
      intervention.vehicle_id,
      intervention.maintenance_type_ref_id,
    ],
  );
};

export const mgxMaintenanceController = {
  async listSuppliers(req, res) {
    const companyId = getCompanyId(req);
    const search = cleanString(req.query.search).toLowerCase();
    const values = [companyId];
    const conditions = ["company_id = $1", "is_active = true"];
    if (search) {
      values.push(`%${search}%`);
      conditions.push(`(lower(name) LIKE $${values.length} OR lower(COALESCE(code, '')) LIKE $${values.length})`);
    }
    const { rows } = await pool.query(
      `
        SELECT *
        FROM mgx_suppliers
        WHERE ${conditions.join(" AND ")}
        ORDER BY name ASC
        LIMIT 200
      `,
      values,
    );
    res.json({
      items: rows.map((row) => ({
        id: Number(row.id),
        code: row.code,
        name: row.name,
        supplier_type_ref_id: row.supplier_type_ref_id ? Number(row.supplier_type_ref_id) : null,
        phone: row.phone,
        email: row.email,
      })),
    });
  },

  async listPlans(req, res) {
    const companyId = getCompanyId(req);
    const { rows } = await pool.query(
      `
        ${planSelect}
        WHERE p.company_id = $1
          AND p.is_active = true
        ORDER BY p.next_due_date ASC NULLS LAST, p.next_due_km ASC NULLS LAST
      `,
      [companyId],
    );
    res.json({ items: rows.map(mapPlan) });
  },

  async savePlan(req, res) {
    const companyId = getCompanyId(req);
    const payload = buildPlanPayload({ ...req.body, company_id: companyId });
    if (!payload.vehicle_id || !payload.maintenance_type_ref_id) {
      throw new ApiError(400, "Véhicule et type d'entretien sont obligatoires.");
    }
    const client = await pool.connect();
    try {
      await client.query("BEGIN");
      const isUpdate = Boolean(req.params.id);
      let oldData = null;
      let row;
      if (isUpdate) {
        const { rows: oldRows } = await client.query("SELECT * FROM mgx_maintenance_plans WHERE id = $1 AND company_id = $2", [req.params.id, companyId]);
        oldData = oldRows[0];
        if (!oldData) throw new ApiError(404, "Plan introuvable.");
        const result = await client.query(
          `
            UPDATE mgx_maintenance_plans
            SET vehicle_id = $1, maintenance_type_ref_id = $2, label = $3,
                frequency_km = $4, frequency_days = $5, last_done_date = $6,
                last_done_km = $7, next_due_date = $8, next_due_km = $9,
                is_active = $10, updated_at = NOW()
            WHERE id = $11 AND company_id = $12
            RETURNING *
          `,
          [
            payload.vehicle_id,
            payload.maintenance_type_ref_id,
            payload.label,
            payload.frequency_km,
            payload.frequency_days,
            payload.last_done_date,
            payload.last_done_km,
            payload.next_due_date,
            payload.next_due_km,
            payload.is_active,
            req.params.id,
            companyId,
          ],
        );
        row = result.rows[0];
      } else {
        const result = await client.query(
          `
            INSERT INTO mgx_maintenance_plans (
              company_id, vehicle_id, maintenance_type_ref_id, label, frequency_km,
              frequency_days, last_done_date, last_done_km, next_due_date, next_due_km, is_active
            )
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
            RETURNING *
          `,
          [
            companyId,
            payload.vehicle_id,
            payload.maintenance_type_ref_id,
            payload.label,
            payload.frequency_km,
            payload.frequency_days,
            payload.last_done_date,
            payload.last_done_km,
            payload.next_due_date,
            payload.next_due_km,
            payload.is_active,
          ],
        );
        row = result.rows[0];
      }
      await logMgxAudit(client, req, {
        companyId,
        action: isUpdate ? "UPDATE" : "CREATE",
        entityType: "mgx_maintenance_plan",
        entityId: row.id,
        oldData,
        newData: row,
      });
      await client.query("COMMIT");
      res.status(isUpdate ? 200 : 201).json({ item: row });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  async deletePlan(req, res) {
    const companyId = getCompanyId(req);
    const { rows } = await pool.query(
      `
        UPDATE mgx_maintenance_plans
        SET is_active = false, updated_at = NOW()
        WHERE id = $1 AND company_id = $2
        RETURNING *
      `,
      [req.params.id, companyId],
    );
    if (!rows[0]) throw new ApiError(404, "Plan introuvable.");
    await logMgxAudit(pool, req, {
      companyId,
      action: "DELETE",
      entityType: "mgx_maintenance_plan",
      entityId: req.params.id,
      oldData: rows[0],
    });
    res.json({ item: rows[0] });
  },

  async dueSoon(req, res) {
    const companyId = getCompanyId(req);
    const days = Number(req.query.days || 30);
    const { rows } = await pool.query(
      `
        ${planSelect}
        WHERE p.company_id = $1
          AND p.is_active = true
      `,
      [companyId],
    );
    const items = rows.map(mapPlan).filter((plan) => plan.status.key !== "ok" || (plan.status.days_remaining !== null && plan.status.days_remaining <= days));
    res.json({ items });
  },

  async listInterventions(req, res) {
    await ensureInterventionMobileColumns();
    const companyId = getCompanyId(req);
    const conditions = ["i.company_id = $1"];
    const values = [companyId];
    for (const [queryKey, column] of [
      ["vehicle_id", "i.vehicle_id"],
      ["depot_id", "i.depot_id"],
      ["type", "i.maintenance_type_ref_id"],
      ["status", "i.status_ref_id"],
    ]) {
      const value = nullableNumber(req.query[queryKey]);
      if (value) {
        values.push(value);
        conditions.push(`${column} = $${values.length}`);
      }
    }
    if (req.query.intervention_type) {
      values.push(req.query.intervention_type);
      conditions.push(`i.intervention_type = $${values.length}`);
    }
    const assignedTo = nullableNumber(req.query.assigned_to) || resolveLinkedStaffProfileId(req);
    if (assignedTo) {
      values.push(assignedTo);
      conditions.push(`i.assigned_to_staff_profile_id = $${values.length}`);
    }
    if (req.query.mobile_status) {
      values.push(req.query.mobile_status);
      conditions.push(`i.mobile_status = $${values.length}`);
    }
    if (req.query.from) {
      values.push(req.query.from);
      conditions.push(`i.start_date >= $${values.length}`);
    }
    if (req.query.to) {
      values.push(req.query.to);
      conditions.push(`i.start_date <= $${values.length}`);
    }
    const { rows } = await pool.query(
      `
        ${interventionSelect}
        WHERE ${conditions.join(" AND ")}
        ORDER BY i.start_date DESC, i.id DESC
        LIMIT 500
      `,
      values,
    );
    res.json({ items: rows.map((row) => mapIntervention(row)) });
  },

  async getIntervention(req, res) {
    await ensureInterventionMobileColumns();
    const companyId = getCompanyId(req);
    const item = await fetchInterventionById(pool, req.params.id, companyId);
    if (!item) throw new ApiError(404, "Intervention introuvable.");
    res.json({ item });
  },

  async saveIntervention(req, res) {
    await ensureInterventionMobileColumns();
    const companyId = getCompanyId(req);
    const payload = buildInterventionPayload({ ...req.body, company_id: companyId });
    if (!payload.vehicle_id || !payload.maintenance_type_ref_id || !payload.label) {
      throw new ApiError(400, "Véhicule, type d'entretien et libellé sont obligatoires.");
    }
    const client = await pool.connect();
    try {
      await client.query("BEGIN");
      const isUpdate = Boolean(req.params.id);
      let row;
      let oldData = null;
      let savedIntervention = null;
      if (isUpdate) {
        oldData = await fetchInterventionById(client, req.params.id, companyId);
        if (!oldData) throw new ApiError(404, "Intervention introuvable.");
        const result = await client.query(
          `
            UPDATE mgx_interventions
            SET vehicle_id = $1, depot_id = $2, intervention_type = $3,
                maintenance_type_ref_id = $4, label = $5, description = $6,
                start_date = $7, end_date = $8, km_at_intervention = $9,
                supplier_id = $10, parts_cost = $11, labor_cost = $12,
                total_cost = $13, status_ref_id = $14, invoice_number = $15,
                file_url = $16, notes = $17, next_km = $18, next_date = $19,
                assigned_to_staff_profile_id = COALESCE($22, assigned_to_staff_profile_id),
                updated_at = NOW()
            WHERE id = $20 AND company_id = $21
            RETURNING *
          `,
          [
            payload.vehicle_id,
            payload.depot_id,
            payload.intervention_type,
            payload.maintenance_type_ref_id,
            payload.label,
            payload.description,
            payload.start_date,
            payload.end_date,
            payload.km_at_intervention,
            payload.supplier_id,
            payload.parts_cost,
            payload.labor_cost,
            payload.total_cost,
            payload.status_ref_id,
            payload.invoice_number,
            payload.file_url,
            payload.notes,
            payload.next_km,
            payload.next_date,
            req.params.id,
            companyId,
            payload.assigned_to_staff_profile_id,
          ],
        );
        row = result.rows[0];
        await upsertInterventionLines(client, req.params.id, payload.lines);
        const updated = await fetchInterventionById(client, req.params.id, companyId);
        savedIntervention = updated;
        await syncInterventionConsumableMovements(client, {
          companyId,
          interventionId: Number(req.params.id),
          depotId: payload.depot_id,
          movementDate: payload.end_date || payload.start_date,
          lines: payload.lines,
          applyStock: isCompletedInterventionStatus(updated.status) && !isCancelledInterventionStatus(updated.status),
          createdBy: resolveActorUserId(req),
        });
      } else {
        const result = await client.query(
          `
            INSERT INTO mgx_interventions (
              company_id, vehicle_id, depot_id, intervention_type, maintenance_type_ref_id,
              label, description, start_date, end_date, km_at_intervention, supplier_id,
              parts_cost, labor_cost, total_cost, status_ref_id, invoice_number, file_url,
              notes, next_km, next_date, created_by, assigned_to_staff_profile_id
            )
            VALUES (
              $1, $2, $3, $4, $5,
              $6, $7, $8, $9, $10, $11,
              $12, $13, $14, $15, $16, $17,
              $18, $19, $20, $21, $22
            )
            RETURNING *
          `,
          [
            companyId,
            payload.vehicle_id,
            payload.depot_id,
            payload.intervention_type,
            payload.maintenance_type_ref_id,
            payload.label,
            payload.description,
            payload.start_date,
            payload.end_date,
            payload.km_at_intervention,
            payload.supplier_id,
            payload.parts_cost,
            payload.labor_cost,
            payload.total_cost,
            payload.status_ref_id,
            payload.invoice_number,
            payload.file_url,
            payload.notes,
            payload.next_km,
            payload.next_date,
            resolveActorUserId(req),
            payload.assigned_to_staff_profile_id,
          ],
        );
        row = result.rows[0];
        await upsertInterventionLines(client, row.id, payload.lines);
        const created = await fetchInterventionById(client, row.id, companyId);
        savedIntervention = created;
        await syncInterventionConsumableMovements(client, {
          companyId,
          interventionId: Number(row.id),
          depotId: payload.depot_id,
          movementDate: payload.end_date || payload.start_date,
          lines: payload.lines,
          applyStock: isCompletedInterventionStatus(created.status) && !isCancelledInterventionStatus(created.status),
          createdBy: resolveActorUserId(req),
        });
      }
      if (
        savedIntervention?.end_date
        && isCompletedInterventionStatus(savedIntervention.status)
        && !isCancelledInterventionStatus(savedIntervention.status)
      ) {
        await updateMatchingPlan(client, savedIntervention);
      }
      await logMgxAudit(client, req, {
        companyId,
        action: isUpdate ? "UPDATE" : "CREATE",
        entityType: "mgx_intervention",
        entityId: row.id,
        oldData,
        newData: { ...row, lines: payload.lines },
      });
      await client.query("COMMIT");
      const item = await fetchInterventionById(pool, row.id, companyId);
      res.status(isUpdate ? 200 : 201).json({ item });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  async closeIntervention(req, res) {
    await ensureInterventionMobileColumns();
    const companyId = getCompanyId(req);
    const client = await pool.connect();
    try {
      await client.query("BEGIN");
      const oldData = await fetchInterventionById(client, req.params.id, companyId);
      if (!oldData) throw new ApiError(404, "Intervention introuvable.");
      const endDate = nullableString(req.body.end_date) || new Date().toISOString().slice(0, 10);
      const statusRefId = nullableNumber(req.body.status_ref_id) || oldData.status_ref_id;
      const { rows } = await client.query(
        `
          UPDATE mgx_interventions
          SET end_date = $1,
              status_ref_id = $2,
              next_km = COALESCE($3, next_km),
              next_date = COALESCE($4, next_date),
              mobile_status = 'closed',
              work_summary = COALESCE($7, work_summary),
              km_at_close = COALESCE($8, km_at_close),
              mobile_photos = COALESCE($9::jsonb, mobile_photos),
              mobile_timeline = mobile_timeline || $10::jsonb,
              updated_at = NOW()
          WHERE id = $5 AND company_id = $6
          RETURNING *
        `,
        [
          endDate,
          statusRefId,
          nullableNumber(req.body.next_km),
          nullableString(req.body.next_date),
          req.params.id,
          companyId,
          nullableString(req.body.work_summary),
          nullableNumber(req.body.km_at_close || req.body.km_current),
          JSON.stringify(Array.isArray(req.body.photos) ? req.body.photos : []),
          JSON.stringify([{
            status: "closed",
            comment: nullableString(req.body.work_summary) || nullableString(req.body.comment),
            actor: resolveActorMobileAccountId(req) ? "mobile" : "web",
            at: new Date().toISOString(),
          }]),
        ],
      );
      const updated = await fetchInterventionById(client, req.params.id, companyId);
      await syncInterventionConsumableMovements(client, {
        companyId,
        interventionId: Number(req.params.id),
        depotId: updated.depot_id,
        movementDate: updated.end_date || updated.start_date,
        lines: updated.lines || [],
        applyStock: isCompletedInterventionStatus(updated.status) && !isCancelledInterventionStatus(updated.status),
        createdBy: resolveActorUserId(req),
      });
      if (
        updated.end_date
        && isCompletedInterventionStatus(updated.status)
        && !isCancelledInterventionStatus(updated.status)
      ) {
        await updateMatchingPlan(client, updated);
      }
      await logMgxAudit(client, req, {
        companyId,
        action: "CLOSE",
        entityType: "mgx_intervention",
        entityId: req.params.id,
        oldData,
        newData: rows[0],
      });
      await client.query("COMMIT");
      req.app.get("io")?.to(`company:${companyId}`).emit("maintenance:completed", {
        intervention_id: Number(req.params.id),
        vehicle: updated.vehicle?.plate_number,
        label: updated.label,
      });
      triggerAlertEngine(req, companyId);
      res.json({ item: updated });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  async takeChargeIntervention(req, res) {
    await ensureInterventionMobileColumns();
    const companyId = getCompanyId(req);
    const staffProfileId = resolveLinkedStaffProfileId(req);
    const client = await pool.connect();

    try {
      await client.query("BEGIN");
      const oldData = await fetchInterventionById(client, req.params.id, companyId);
      if (!oldData) throw new ApiError(404, "Intervention introuvable.");
      if (oldData.assigned_to_staff_profile_id && staffProfileId && oldData.assigned_to_staff_profile_id !== staffProfileId) {
        throw new ApiError(403, "Intervention affectée à un autre mécanicien.");
      }

      const timelineEvent = {
        status: "taken",
        comment: nullableString(req.body.comment) || "Prise en charge",
        actor: resolveActorMobileAccountId(req) ? "mobile" : "web",
        at: new Date().toISOString(),
      };
      const { rows } = await client.query(
        `
          UPDATE mgx_interventions
          SET mobile_status = 'taken',
              assigned_to_staff_profile_id = COALESCE(assigned_to_staff_profile_id, $3),
              mobile_timeline = mobile_timeline || $4::jsonb,
              updated_at = NOW()
          WHERE id = $1
            AND company_id = $2
          RETURNING *
        `,
        [req.params.id, companyId, staffProfileId, JSON.stringify([timelineEvent])],
      );

      await logMgxAudit(client, req, {
        companyId,
        action: "TAKE_CHARGE",
        entityType: "mgx_intervention",
        entityId: req.params.id,
        oldData,
        newData: rows[0],
      });
      await client.query("COMMIT");
      res.json({ item: await fetchInterventionById(pool, req.params.id, companyId) });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  async updateInterventionStatus(req, res) {
    await ensureInterventionMobileColumns();
    const companyId = getCompanyId(req);
    const nextStatus = nullableString(req.body.status);
    const allowedStatuses = new Set(["assigned", "taken", "diagnosing", "in_progress", "waiting_part", "done", "closed"]);

    if (!nextStatus || !allowedStatuses.has(nextStatus)) {
      throw new ApiError(400, "Statut intervention invalide.");
    }

    const staffProfileId = resolveLinkedStaffProfileId(req);
    const client = await pool.connect();

    try {
      await client.query("BEGIN");
      const oldData = await fetchInterventionById(client, req.params.id, companyId);
      if (!oldData) throw new ApiError(404, "Intervention introuvable.");
      if (oldData.assigned_to_staff_profile_id && staffProfileId && oldData.assigned_to_staff_profile_id !== staffProfileId) {
        throw new ApiError(403, "Intervention affectée à un autre mécanicien.");
      }

      const photos = Array.isArray(req.body.photos) ? req.body.photos : [];
      const timelineEvent = {
        status: nextStatus,
        comment: nullableString(req.body.comment),
        km_current: nullableNumber(req.body.km_current),
        actor: resolveActorMobileAccountId(req) ? "mobile" : "web",
        at: new Date().toISOString(),
      };
      const { rows } = await client.query(
        `
          UPDATE mgx_interventions
          SET mobile_status = $3,
              assigned_to_staff_profile_id = COALESCE(assigned_to_staff_profile_id, $4),
              mobile_photos = mobile_photos || $5::jsonb,
              mobile_timeline = mobile_timeline || $6::jsonb,
              updated_at = NOW()
          WHERE id = $1
            AND company_id = $2
          RETURNING *
        `,
        [req.params.id, companyId, nextStatus, staffProfileId, JSON.stringify(photos), JSON.stringify([timelineEvent])],
      );

      await logMgxAudit(client, req, {
        companyId,
        action: "UPDATE_MOBILE_STATUS",
        entityType: "mgx_intervention",
        entityId: req.params.id,
        oldData,
        newData: rows[0],
      });
      await client.query("COMMIT");
      res.json({ item: await fetchInterventionById(pool, req.params.id, companyId) });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  async getReports(req, res) {
    const companyId = getCompanyId(req);
    const from = req.query.from || new Date(new Date().getFullYear(), 0, 1).toISOString().slice(0, 10);
    const to = req.query.to || new Date().toISOString().slice(0, 10);
    const conditions = ["i.company_id = $1", "i.start_date BETWEEN $2 AND $3"];
    const values = [companyId, from, to];
    for (const [queryKey, column] of [["depot_id", "i.depot_id"], ["vehicle_id", "i.vehicle_id"], ["maintenance_type_ref_id", "i.maintenance_type_ref_id"]]) {
      const value = nullableNumber(req.query[queryKey]);
      if (value) {
        values.push(value);
        conditions.push(`${column} = $${values.length}`);
      }
    }
    const whereSql = conditions.join(" AND ");
    const [summary, byType, topVehicles, monthly, ratio] = await Promise.all([
      pool.query(`SELECT COALESCE(SUM(total_cost), 0) AS total_cost, COUNT(*)::int AS count FROM mgx_interventions i WHERE ${whereSql}`, values),
      pool.query(
        `
          SELECT mt.id, mt.label, mt.color, COALESCE(SUM(i.total_cost), 0) AS amount, COUNT(*)::int AS count
          FROM mgx_interventions i
          LEFT JOIN mgx_referentiels mt ON mt.id = i.maintenance_type_ref_id
          WHERE ${whereSql}
          GROUP BY mt.id, mt.label, mt.color
          ORDER BY amount DESC
        `,
        values,
      ),
      pool.query(
        `
          SELECT v.id, v.plate_number, v.brand, v.model, COALESCE(SUM(i.total_cost), 0) AS amount, COUNT(*)::int AS count
          FROM mgx_interventions i
          JOIN mgx_vehicles v ON v.id = i.vehicle_id
          WHERE ${whereSql}
          GROUP BY v.id, v.plate_number, v.brand, v.model
          ORDER BY amount DESC
          LIMIT 10
        `,
        values,
      ),
      pool.query(
        `
          SELECT date_trunc('month', i.start_date)::date AS month_start, COALESCE(SUM(i.total_cost), 0) AS amount
          FROM mgx_interventions i
          WHERE ${whereSql}
          GROUP BY month_start
          ORDER BY month_start ASC
        `,
        values,
      ),
      pool.query(
        `
          SELECT
            v.id,
            v.plate_number,
            COALESCE(SUM(i.total_cost), 0) AS maintenance_cost,
            COALESCE(SUM(f.km_since_last), 0) AS km_done
          FROM mgx_vehicles v
          LEFT JOIN mgx_interventions i ON i.vehicle_id = v.id AND i.company_id = $1 AND i.start_date BETWEEN $2 AND $3
          LEFT JOIN mgx_fuel_entries f ON f.vehicle_id = v.id AND f.company_id = $1 AND f.entry_date BETWEEN $2 AND $3
          WHERE v.company_id = $1
          GROUP BY v.id, v.plate_number
          HAVING COALESCE(SUM(i.total_cost), 0) > 0
          ORDER BY maintenance_cost DESC
          LIMIT 20
        `,
        [companyId, from, to],
      ),
    ]);
    res.json({
      summary: {
        total_cost: toNumber(summary.rows[0]?.total_cost),
        total_cost_formatted: formatDzdAmount(summary.rows[0]?.total_cost),
        count: Number(summary.rows[0]?.count || 0),
      },
      by_type: byType.rows.map((row) => ({
        id: row.id ? Number(row.id) : null,
        name: row.label || "Non défini",
        color: row.color || "#F59E0B",
        value: toNumber(row.amount),
        amount_formatted: formatDzdAmount(row.amount),
        count: Number(row.count || 0),
      })),
      top_vehicles: topVehicles.rows.map((row) => ({
        id: Number(row.id),
        plate_number: row.plate_number,
        brand: row.brand,
        model: row.model,
        amount: toNumber(row.amount),
        amount_formatted: formatDzdAmount(row.amount),
        count: Number(row.count || 0),
      })),
      monthly: monthly.rows.map((row) => ({
        month: row.month_start,
        label: new Intl.DateTimeFormat("fr-DZ", { month: "short", year: "2-digit" }).format(row.month_start),
        amount: toNumber(row.amount),
      })),
      ratio: ratio.rows.map((row) => ({
        id: Number(row.id),
        plate_number: row.plate_number,
        maintenance_cost: toNumber(row.maintenance_cost),
        km_done: toNumber(row.km_done),
        ratio: toNumber(row.km_done) > 0 ? toNumber(row.maintenance_cost) / toNumber(row.km_done) : 0,
      })),
    });
  },
};
