import { pool } from "../db/pool.js";
import { ApiError } from "../utils/ApiError.js";

const cleanString = (value) => String(value ?? "").trim();

const nullableString = (value) => {
  const cleaned = cleanString(value);
  return cleaned || null;
};

const toNumber = (value) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : null;
};

const normalizeDetails = (value) => {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return {};
  }

  return value;
};

const getCompanyId = (req) => {
  const companyId = toNumber(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 getActorSnapshot = (req, fallbackLabel = "MGX") => ({
  full_name: cleanString(req.user?.fullName || req.user?.full_name || req.user?.email || fallbackLabel) || fallbackLabel,
  role_type: cleanString(req.user?.role_type || req.user?.role || "") || null,
  account_type: req.user?.accountType || "user",
  mobile_account_id: toNumber(req.user?.mobileAccountId ?? req.user?.mobile_account_id),
  user_id: req.user?.accountType === "mgx_mobile_account" ? null : toNumber(req.user?.sub),
  at: new Date().toISOString(),
});

const addDetailSnapshot = (details, key, snapshot) => ({
  ...normalizeDetails(details),
  [key]: snapshot,
});

const getSubmittedByName = (row) => row.submitted_by_name || row.details?.submitted_by_snapshot?.full_name || null;
const getResolvedByName = (row) => row.resolved_by_name || row.details?.resolved_by_snapshot?.full_name || 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)
  `);
};

const getDriverIdFilter = (req) => {
  const explicitDriverId = toNumber(req.query.driver_id ?? req.query.linked_entity_id);
  if (explicitDriverId) {
    return explicitDriverId;
  }

  const linkedEntityType = String(req.user?.linkedEntityType || req.user?.linked_entity_type || "").toLowerCase();
  const role = String(req.user?.role || req.user?.role_type || "").toLowerCase();
  const isDriverContext = linkedEntityType === "driver" || role === "chauffeur" || role === "mgx_chauffeur";

  if (!isDriverContext) {
    return null;
  }

  return toNumber(req.user?.linkedEntityId ?? req.user?.linked_entity_id);
};

const buildReportTimeline = (row) => {
  const timeline = [
    {
      id: "created",
      label: "Créée",
      actor: getSubmittedByName(row) || "Mobile MGX",
      at: row.created_at,
      done: true,
      color: "#F59E0B",
    },
  ];

  if (row.updated_at && String(row.updated_at) !== String(row.created_at)) {
    timeline.push({
      id: "updated",
      label: "Mise à jour",
      actor: getResolvedByName(row) || "Chef de parc",
      at: row.updated_at,
      done: true,
      color: "#3B82F6",
    });
  }

  if (row.resolved_at) {
    timeline.push({
      id: "resolved",
      label: row.status === "rejected" ? "Rejetée" : "Traitée",
      actor: getResolvedByName(row) || "MGX",
      at: row.resolved_at,
      done: true,
      color: row.status === "rejected" ? "#EF4444" : "#10B981",
    });
  }

  return timeline;
};

const getStatusMeta = (row) => {
  const status = String(row.status || "new");

  if (status === "converted") {
    return { code: "done", label: "Traitée", color: "#10B981", message: "Demande prise en charge" };
  }
  if (status === "rejected") {
    return { code: "done", label: "Rejetée", color: "#EF4444", message: row.admin_notes || "Demande rejetée" };
  }
  if (status === "reviewed") {
    return { code: "in_progress", label: "En cours", color: "#3B82F6", message: row.admin_notes || "Demande en cours de traitement" };
  }
  return { code: "pending", label: "En attente", color: "#F59E0B", message: "En attente de qualification" };
};

const getReportTypeMeta = (row) => {
  const details = row.details || {};
  const value = cleanString(details.report_type || details.type || row.title).toLowerCase();
  const needType = cleanString(details.need_type).toLowerCase();

  if (needType === "vehicle_assignment" || value.includes("affectation")) {
    return { label: "Affectation", color: "#8B5CF6" };
  }

  if (value.includes("breakdown") || value.includes("panne")) {
    return { label: "Panne", color: "#EF4444" };
  }
  if (value.includes("maintenance") || value.includes("entretien")) {
    return { label: "Entretien", color: "#F59E0B" };
  }
  if (value.includes("location_confirm")) {
    return { label: "Position", color: "#3B82F6" };
  }
  return { label: "Autre besoin", color: "#64748B" };
};

const mapReport = (row) => ({
  id: Number(row.id),
  company_id: Number(row.company_id),
  vehicle_id: Number(row.vehicle_id),
  depot_id: row.depot_id ? Number(row.depot_id) : null,
  submitted_by: row.submitted_by ? Number(row.submitted_by) : null,
  report_kind: row.report_kind,
  priority: row.priority,
  title: row.title,
  description: row.description,
  details: row.details || {},
  status: row.status,
  source: row.source,
  admin_notes: row.admin_notes,
  converted_entity_type: row.converted_entity_type,
  converted_entity_id: row.converted_entity_id ? Number(row.converted_entity_id) : null,
  resolved_at: row.resolved_at,
  resolved_by: row.resolved_by ? Number(row.resolved_by) : null,
  created_at: row.created_at,
  updated_at: row.updated_at,
  vehicle: row.vehicle_ref
    ? {
        id: Number(row.vehicle_id),
        reference: row.vehicle_ref,
        plate_number: row.plate_number,
        brand: row.vehicle_brand,
        model: row.vehicle_model,
      }
    : null,
  depot: row.depot_label
    ? {
        id: Number(row.depot_id),
        code: row.depot_code,
        label: row.depot_label,
      }
    : null,
  submitted_by_user: getSubmittedByName(row)
    ? {
        id: row.submitted_by ? Number(row.submitted_by) : row.details?.submitted_by_snapshot?.mobile_account_id || null,
        full_name: getSubmittedByName(row),
        email: row.submitted_by_email || null,
      }
    : null,
  resolved_by_user: getResolvedByName(row)
    ? {
        id: row.resolved_by ? Number(row.resolved_by) : row.details?.resolved_by_snapshot?.mobile_account_id || null,
        full_name: getResolvedByName(row),
        email: row.resolved_by_email || null,
      }
    : null,
  status_meta: getStatusMeta(row),
  report_type_meta: getReportTypeMeta(row),
  timeline: buildReportTimeline(row),
  comments: Array.isArray(row.details?.comments) ? row.details.comments : [],
  photos: Array.isArray(row.details?.photos) ? row.details.photos : [],
});

const reportSelect = `
  SELECT
    r.*,
    v.reference AS vehicle_ref,
    v.plate_number,
    v.brand AS vehicle_brand,
    v.model AS vehicle_model,
    d.code AS depot_code,
    d.label AS depot_label,
    submitter.full_name AS submitted_by_name,
    submitter.email AS submitted_by_email,
    resolver.full_name AS resolved_by_name,
    resolver.email AS resolved_by_email
  FROM mgx_vehicle_agent_reports r
  JOIN mgx_vehicles v ON v.id = r.vehicle_id
  LEFT JOIN mgx_depots d ON d.id = r.depot_id
  LEFT JOIN users submitter ON submitter.id = r.submitted_by
  LEFT JOIN users resolver ON resolver.id = r.resolved_by
`;

const insertAuditLog = async (db, req, companyId, action, entityId, newData = null, oldData = null) => {
  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,
      "vehicle_agent_report",
      entityId,
      oldData ? JSON.stringify(oldData) : null,
      newData ? JSON.stringify(newData) : null,
      req.ip,
    ],
  );
};

const getReportForConversion = async (db, reportId, companyId) => {
  const { rows } = await db.query(
    `
      ${reportSelect}
      WHERE r.id = $1
        AND r.company_id = $2
      LIMIT 1
    `,
    [reportId, companyId],
  );

  return rows[0] || null;
};

const closeActiveVehicleDriverAssignments = async (db, vehicleId, startDate) => {
  await db.query(
    `
      DELETE FROM mgx_vehicle_driver_assignments
      WHERE vehicle_id = $1
        AND end_date IS NULL
        AND start_date >= $2::date
    `,
    [vehicleId, startDate],
  );

  await db.query(
    `
      UPDATE mgx_vehicle_driver_assignments
      SET
        end_date = ($2::date - INTERVAL '1 day')::date,
        updated_at = NOW()
      WHERE vehicle_id = $1
        AND end_date IS NULL
        AND start_date < $2::date
    `,
    [vehicleId, startDate],
  );
};

const releaseDriverFromOtherVehicles = async (db, driverId, nextVehicleId, startDate) => {
  if (!driverId) return;

  const { rows } = await db.query(
    `
      SELECT id
      FROM mgx_vehicles
      WHERE driver_id = $1
        AND id <> $2
    `,
    [driverId, nextVehicleId],
  );

  if (!rows.length) return;

  await db.query(
    `
      UPDATE mgx_vehicles
      SET
        driver_id = NULL,
        assigned_driver = NULL,
        updated_at = NOW()
      WHERE driver_id = $1
        AND id <> $2
    `,
    [driverId, nextVehicleId],
  );

  for (const row of rows) {
    await closeActiveVehicleDriverAssignments(db, row.id, startDate);
  }
};

export const mgxAgentController = {
  listVehicleReports: async (req, res) => {
    const companyId = getCompanyId(req);
    const filters = [];
    const values = [companyId];
    let index = values.length + 1;

    filters.push(`r.company_id = $1`);

    if (cleanString(req.query.status)) {
      values.push(cleanString(req.query.status));
      filters.push(`r.status = $${index++}`);
    }

    if (cleanString(req.query.report_kind)) {
      values.push(cleanString(req.query.report_kind));
      filters.push(`r.report_kind = $${index++}`);
    }

    if (toNumber(req.query.vehicle_id)) {
      values.push(toNumber(req.query.vehicle_id));
      filters.push(`r.vehicle_id = $${index++}`);
    }

    if (toNumber(req.query.depot_id)) {
      values.push(toNumber(req.query.depot_id));
      filters.push(`r.depot_id = $${index++}`);
    }

    if (getDriverIdFilter(req)) {
      values.push(getDriverIdFilter(req));
      filters.push(`v.driver_id = $${index++}`);
    }

    const { rows } = await pool.query(
      `
        ${reportSelect}
        WHERE ${filters.join(" AND ")}
        ORDER BY r.created_at DESC, r.id DESC
      `,
      values,
    );

    res.json({ items: rows.map(mapReport) });
  },

  getVehicleReport: async (req, res) => {
    const companyId = getCompanyId(req);
    const reportId = toNumber(req.params.id);
    if (!reportId) {
      throw new ApiError(400, "Identifiant invalide.");
    }

    const values = [reportId, companyId];
    const filters = ["r.id = $1", "r.company_id = $2"];
    const driverId = getDriverIdFilter(req);
    if (driverId) {
      values.push(driverId);
      filters.push(`v.driver_id = $${values.length}`);
    }

    const { rows } = await pool.query(
      `
        ${reportSelect}
        WHERE ${filters.join(" AND ")}
        LIMIT 1
      `,
      values,
    );

    if (!rows[0]) {
      throw new ApiError(404, "Remontée introuvable.");
    }

    res.json({ item: mapReport(rows[0]) });
  },

  addVehicleReportComment: async (req, res) => {
    const companyId = getCompanyId(req);
    const reportId = toNumber(req.params.id);
    const comment = cleanString(req.body.comment);

    if (!reportId || !comment) {
      throw new ApiError(400, "Commentaire requis.");
    }

    const client = await pool.connect();
    try {
      await client.query("BEGIN");
      const { rows } = await client.query(
        `
          SELECT *
          FROM mgx_vehicle_agent_reports
          WHERE id = $1 AND company_id = $2
          LIMIT 1
        `,
        [reportId, companyId],
      );
      const report = rows[0];
      if (!report) {
        throw new ApiError(404, "Remontée introuvable.");
      }

      const details = normalizeDetails(report.details);
      const comments = Array.isArray(details.comments) ? [...details.comments] : [];
      comments.push({
        id: Date.now(),
        author: req.user?.fullName || req.user?.full_name || req.user?.email || "Mobile MGX",
        role: req.user?.role_type || req.user?.role || "chauffeur",
        text: comment,
        at: new Date().toISOString(),
      });

      await client.query(
        `
          UPDATE mgx_vehicle_agent_reports
          SET details = $3::jsonb, updated_at = CURRENT_TIMESTAMP
          WHERE id = $1 AND company_id = $2
        `,
        [reportId, companyId, JSON.stringify({ ...details, comments })],
      );

      await insertAuditLog(client, req, companyId, "mgx.vehicle_agent_report.comment", reportId, {
        comment,
      });
      await client.query("COMMIT");

      const refreshed = await pool.query(
        `
          ${reportSelect}
          WHERE r.id = $1
          LIMIT 1
        `,
        [reportId],
      );

      res.json({ item: mapReport(refreshed.rows[0]) });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  confirmVehicleLocation: async (req, res) => {
    const companyId = getCompanyId(req);
    const vehicleId = toNumber(req.body.vehicle_id);
    const depotId = toNumber(req.body.depot_id);
    const customLocation = nullableString(req.body.custom_location);
    const comment = nullableString(req.body.comment);
    const latitude = toNumber(req.body.latitude);
    const longitude = toNumber(req.body.longitude);
    const confirmedAt = nullableString(req.body.confirmed_at) || new Date().toISOString();

    if (!vehicleId) {
      throw new ApiError(400, "Véhicule requis.");
    }

    const client = await pool.connect();
    try {
      await client.query("BEGIN");

      const vehicleResult = await client.query(
        `
          SELECT id, company_id, depot_id
          FROM mgx_vehicles
          WHERE id = $1 AND company_id = $2
          LIMIT 1
        `,
        [vehicleId, companyId],
      );
      const vehicleRow = vehicleResult.rows[0];
      if (!vehicleRow) {
        throw new ApiError(404, "Véhicule introuvable.");
      }

      const title = "Confirmation emplacement";
      const description = customLocation || comment || "Position confirmée depuis mobile";
      const details = addDetailSnapshot({
        type: "location_confirm",
        depot_id: depotId || toNumber(vehicleRow.depot_id),
        custom_location: customLocation,
        comment,
        latitude,
        longitude,
        confirmed_at: confirmedAt,
      }, "submitted_by_snapshot", getActorSnapshot(req, "Mobile MGX"));

      const insertResult = await client.query(
        `
          INSERT INTO mgx_vehicle_agent_reports (
            company_id, vehicle_id, depot_id, submitted_by, report_kind, priority, title, description, details
          )
          VALUES ($1, $2, $3, $4, 'report', 'normal', $5, $6, $7::jsonb)
          RETURNING id
        `,
        [
          companyId,
          vehicleId,
          depotId || toNumber(vehicleRow.depot_id),
          resolveActorUserId(req),
          title,
          description,
          JSON.stringify(details),
        ],
      );

      await insertAuditLog(client, req, companyId, "mgx.vehicle_location.confirm", Number(insertResult.rows[0].id), details);
      await client.query("COMMIT");

      res.status(201).json({
        message: "Confirmation d'emplacement enregistrée.",
        item: { id: Number(insertResult.rows[0].id) },
      });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  createVehicleReport: async (req, res) => {
    const companyId = getCompanyId(req);
    const vehicleId = toNumber(req.body.vehicle_id);
    const depotId = toNumber(req.body.depot_id);
    const reportKind = cleanString(req.body.report_kind).toLowerCase();
    const priority = cleanString(req.body.priority || "normal").toLowerCase();
    const title = cleanString(req.body.title);
    const description = cleanString(req.body.description);
    const details = addDetailSnapshot(req.body.details, "submitted_by_snapshot", getActorSnapshot(req, "Mobile MGX"));

    if (!vehicleId) {
      throw new ApiError(400, "Véhicule requis.");
    }

    if (!["request", "report"].includes(reportKind)) {
      throw new ApiError(400, "Type de remontée invalide.");
    }

    if (!["low", "normal", "high", "critical"].includes(priority)) {
      throw new ApiError(400, "Priorité invalide.");
    }

    if (!title) {
      throw new ApiError(400, "Titre requis.");
    }

    if (!description) {
      throw new ApiError(400, "Description requise.");
    }

    const client = await pool.connect();

    try {
      await client.query("BEGIN");

      const vehicleResult = await client.query(
        `
          SELECT id, company_id, depot_id
          FROM mgx_vehicles
          WHERE id = $1
            AND company_id = $2
          LIMIT 1
        `,
        [vehicleId, companyId],
      );

      const vehicleRow = vehicleResult.rows[0];

      if (!vehicleRow) {
        throw new ApiError(404, "Véhicule introuvable pour cette société.");
      }

      const insertResult = await client.query(
        `
          INSERT INTO mgx_vehicle_agent_reports (
            company_id,
            vehicle_id,
            depot_id,
            submitted_by,
            report_kind,
            priority,
            title,
            description,
            details
          )
          VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb)
          RETURNING id
        `,
        [
          companyId,
          vehicleId,
          depotId || toNumber(vehicleRow.depot_id),
          resolveActorUserId(req),
          reportKind,
          priority,
          title,
          description,
          JSON.stringify(details),
        ],
      );

      const reportId = Number(insertResult.rows[0].id);
      const reportResult = await client.query(
        `
          ${reportSelect}
          WHERE r.id = $1
          LIMIT 1
        `,
        [reportId],
      );

      await insertAuditLog(
        client,
        req,
        companyId,
        "mgx.vehicle_agent_report.create",
        reportId,
        {
          vehicle_id: vehicleId,
          report_kind: reportKind,
          priority,
          title,
          details,
        },
      );

      await client.query("COMMIT");

      req.app.get("io")?.to(`company:${companyId}`).emit("report:new", {
        report_id: reportId,
        urgency: priority,
        vehicle_plate: reportResult.rows[0]?.plate_number || null,
        depot_id: depotId || toNumber(vehicleRow.depot_id),
        title,
      });

      res.status(201).json({
        message: "Remontée terrain enregistrée.",
        item: mapReport(reportResult.rows[0]),
      });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  updateVehicleReport: async (req, res) => {
    const companyId = getCompanyId(req);
    const reportId = toNumber(req.params.id);
    const nextStatus = nullableString(req.body.status)?.toLowerCase();
    const adminNotes = nullableString(req.body.admin_notes);

    if (!reportId) {
      throw new ApiError(400, "Identifiant invalide.");
    }

    if (!nextStatus && adminNotes === null) {
      throw new ApiError(400, "Aucune mise à jour reçue.");
    }

    if (nextStatus && !["new", "reviewed", "converted", "rejected"].includes(nextStatus)) {
      throw new ApiError(400, "Statut invalide.");
    }

    const client = await pool.connect();

    try {
      await client.query("BEGIN");

      const currentResult = await client.query(
        `
          ${reportSelect}
          WHERE r.id = $1
            AND r.company_id = $2
          LIMIT 1
        `,
        [reportId, companyId],
      );

      const currentRow = currentResult.rows[0];

      if (!currentRow) {
        throw new ApiError(404, "Remontée introuvable.");
      }

      const finalStatus = nextStatus || currentRow.status;
      const nextDetails = addDetailSnapshot(currentRow.details, "updated_by_snapshot", getActorSnapshot(req, "Chef de parc"));
      if (["converted", "rejected"].includes(finalStatus)) {
        nextDetails.resolved_by_snapshot = getActorSnapshot(req, "Chef de parc");
      }
      const updateResult = await client.query(
        `
          UPDATE mgx_vehicle_agent_reports
        SET
            status = $3,
            admin_notes = $4,
            details = $6::jsonb,
            resolved_at = CASE WHEN $3 IN ('converted', 'rejected') THEN CURRENT_TIMESTAMP ELSE NULL END,
            resolved_by = CASE WHEN $3 IN ('converted', 'rejected') THEN $5 ELSE NULL END,
            updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
          RETURNING id
        `,
        [reportId, companyId, finalStatus, adminNotes, resolveActorUserId(req), JSON.stringify(nextDetails)],
      );

      const updatedId = Number(updateResult.rows[0].id);
      const refreshedResult = await client.query(
        `
          ${reportSelect}
          WHERE r.id = $1
          LIMIT 1
        `,
        [updatedId],
      );

      await insertAuditLog(
        client,
        req,
        companyId,
        "mgx.vehicle_agent_report.update",
        updatedId,
        {
          status: finalStatus,
          admin_notes: adminNotes,
        },
        {
          status: currentRow.status,
          admin_notes: currentRow.admin_notes,
        },
      );

      await client.query("COMMIT");

      req.app.get("io")?.to(`company:${companyId}`).emit("report:updated", {
        report_id: updatedId,
        status: finalStatus,
        reason: adminNotes,
        vehicle_plate: refreshedResult.rows[0]?.plate_number || null,
      });

      res.json({
        message: "Remontée mise à jour.",
        item: mapReport(refreshedResult.rows[0]),
      });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  approveDriverAssignment: async (req, res) => {
    const companyId = getCompanyId(req);
    const reportId = toNumber(req.params.id);
    const startDate = nullableString(req.body.start_date)?.slice(0, 10) || new Date().toISOString().slice(0, 10);
    const adminNotes = nullableString(req.body.admin_notes);

    console.log("[mgx] approveDriverAssignment:start", {
      companyId,
      reportId,
      startDate,
      adminNotes,
      user: {
        sub: req.user?.sub || null,
        role: req.user?.role || null,
        role_type: req.user?.role_type || null,
        accountType: req.user?.accountType || null,
        linkedEntityId: req.user?.linkedEntityId ?? req.user?.linked_entity_id ?? null,
        linkedEntityType: req.user?.linkedEntityType ?? req.user?.linked_entity_type ?? null,
      },
      body: req.body || {},
    });

    if (!reportId) {
      throw new ApiError(400, "Remontée terrain invalide.");
    }

    const client = await pool.connect();
    try {
      await client.query("BEGIN");

      const report = await getReportForConversion(client, reportId, companyId);
      if (!report) {
        throw new ApiError(404, "Demande introuvable.");
      }

      console.log("[mgx] approveDriverAssignment:report", {
        reportId,
        vehicle_id: report.vehicle_id,
        status: report.status,
        details: report.details || {},
      });

      const details = report.details || {};
      if (cleanString(details.need_type).toLowerCase() !== "vehicle_assignment") {
        throw new ApiError(400, "Cette demande ne correspond pas à une affectation de véhicule.");
      }

      const requestedDriverId = toNumber(details.requested_driver_id);
      if (!requestedDriverId) {
        throw new ApiError(400, "Chauffeur demandé introuvable.");
      }

      const { rows: driverRows } = await client.query(
        `
          SELECT id, full_name
          FROM mgx_drivers
          WHERE id = $1
            AND company_id = $2
            AND is_active = TRUE
          LIMIT 1
        `,
        [requestedDriverId, companyId],
      );
      const driver = driverRows[0];
      if (!driver) {
        throw new ApiError(400, "Le chauffeur demandé est introuvable ou inactif.");
      }

      console.log("[mgx] approveDriverAssignment:driver", {
        requestedDriverId,
        driver,
      });

      const { rows: vehicleRows } = await client.query(
        `
          SELECT id, driver_id, assigned_driver
          FROM mgx_vehicles
          WHERE id = $1
            AND company_id = $2
          LIMIT 1
        `,
        [report.vehicle_id, companyId],
      );
      const vehicle = vehicleRows[0];
      if (!vehicle) {
        throw new ApiError(404, "Véhicule introuvable.");
      }

      console.log("[mgx] approveDriverAssignment:vehicle_before", vehicle);

      await releaseDriverFromOtherVehicles(client, Number(driver.id), report.vehicle_id, startDate);
      await closeActiveVehicleDriverAssignments(client, report.vehicle_id, startDate);

      const assignmentNotes = [
        "Affectation validée depuis une demande terrain.",
        adminNotes,
      ]
        .filter(Boolean)
        .join("\n");

      const assignmentResult = await client.query(
        `
          INSERT INTO mgx_vehicle_driver_assignments (
            company_id, vehicle_id, driver_id, driver_name, start_date, end_date, notes, created_by
          )
          VALUES ($1, $2, $3, $4, $5, NULL, $6, $7)
          RETURNING id
        `,
        [
          companyId,
          report.vehicle_id,
          Number(driver.id),
          driver.full_name,
          startDate,
          assignmentNotes || null,
          resolveActorUserId(req),
        ],
      );

      await client.query(
        `
          UPDATE mgx_vehicles
          SET
            driver_id = $1,
            assigned_driver = $2,
            updated_at = NOW()
          WHERE id = $3
            AND company_id = $4
        `,
        [Number(driver.id), driver.full_name, report.vehicle_id, companyId],
      );

      const mergedNotes = [report.admin_notes, adminNotes].filter(Boolean).join("\n");
      const nextDetails = addDetailSnapshot(report.details, "resolved_by_snapshot", getActorSnapshot(req, "Chef de parc"));
      await client.query(
        `
          UPDATE mgx_vehicle_agent_reports
          SET
            status = 'converted',
            admin_notes = $1,
            details = $5::jsonb,
            converted_entity_type = 'driver_assignment',
            converted_entity_id = $2,
            resolved_at = NOW(),
            resolved_by = $3,
            updated_at = NOW()
          WHERE id = $4
        `,
        [
          mergedNotes || null,
          Number(assignmentResult.rows[0].id),
          resolveActorUserId(req),
          reportId,
          JSON.stringify(nextDetails),
        ],
      );

      await insertAuditLog(
        client,
        req,
        companyId,
        "mgx.vehicle_assignment.approve",
        reportId,
        {
          vehicle_id: report.vehicle_id,
          requested_driver_id: requestedDriverId,
          assignment_id: Number(assignmentResult.rows[0].id),
          start_date: startDate,
        },
        {
          old_driver_id: vehicle.driver_id ? Number(vehicle.driver_id) : null,
          old_assigned_driver: vehicle.assigned_driver || null,
        },
      );

      await client.query("COMMIT");

      console.log("[mgx] approveDriverAssignment:success", {
        reportId,
        vehicle_id: report.vehicle_id,
        new_driver_id: Number(driver.id),
        assignment_id: Number(assignmentResult.rows[0].id),
      });

      const refreshed = await pool.query(
        `
          ${reportSelect}
          WHERE r.id = $1
          LIMIT 1
        `,
        [reportId],
      );

      res.json({
        message: "Affectation validée.",
        item: mapReport(refreshed.rows[0]),
      });
    } catch (error) {
      await client.query("ROLLBACK");
      console.error("[mgx] approveDriverAssignment:error", {
        message: error?.message,
        code: error?.code,
        detail: error?.detail,
        hint: error?.hint,
        stack: error?.stack,
      });
      throw error;
    } finally {
      client.release();
    }
  },

  convertToPurchaseRequest: async (req, res) => {
    const companyId = getCompanyId(req);
    const reportId = toNumber(req.params.id);

    if (!reportId) {
      throw new ApiError(400, "Identifiant invalide.");
    }

    const client = await pool.connect();

    try {
      await client.query("BEGIN");

      const report = await getReportForConversion(client, reportId, companyId);

      if (!report) {
        throw new ApiError(404, "Remontée introuvable.");
      }

      if (report.converted_entity_id) {
        throw new ApiError(400, "Cette remontée est déjà convertie.");
      }

      const details = normalizeDetails(report.details);
      const itemId = toNumber(req.body.item_id || details.item_id);
      const quantity = toNumber(req.body.quantity || details.quantity) || 1;
      const depotId = toNumber(req.body.depot_id || report.depot_id);

      if (!depotId) {
        throw new ApiError(400, "Dépôt requis pour générer une demande d'achat.");
      }

      if (!itemId) {
        throw new ApiError(400, "Article requis pour générer une demande d'achat.");
      }

      const { rows: itemRows } = await client.query(
        `
          SELECT id, code, label, unit_price, category_ref_id, unit_ref_id
          FROM mgx_consumable_items
          WHERE id = $1
            AND company_id = $2
          LIMIT 1
        `,
        [itemId, companyId],
      );

      const item = itemRows[0];

      if (!item) {
        throw new ApiError(400, "Article introuvable pour cette société.");
      }

      const { rows: requestRows } = await client.query(
        `
          INSERT INTO mgx_purchase_requests (
            company_id,
            depot_id,
            requested_by,
            request_date,
            category_ref_id,
            urgency,
            justification,
            status
          )
          VALUES ($1, $2, $3, CURRENT_DATE, $4, $5, $6, 'draft')
          RETURNING id
        `,
        [
          companyId,
          depotId,
          resolveActorUserId(req) || report.submitted_by || null,
          toNumber(req.body.category_ref_id || item.category_ref_id),
          report.priority === "critical" ? "urgent" : report.priority || "normal",
          `${report.title}\n\n${report.description}`,
        ],
      );

      const requestId = Number(requestRows[0].id);
      const unitPrice = toNumber(req.body.estimated_unit_price || details.estimated_unit_price || item.unit_price) || 0;

      await client.query(
        `
          INSERT INTO mgx_purchase_request_lines (
            request_id,
            item_id,
            description,
            quantity,
            estimated_unit_price,
            estimated_total,
            unit_ref_id,
            notes
          )
          VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
        `,
        [
          requestId,
          item.id,
          item.label,
          quantity,
          unitPrice,
          quantity * unitPrice,
          toNumber(item.unit_ref_id),
          report.description,
        ],
      );

      const purchaseDetails = addDetailSnapshot(details, "resolved_by_snapshot", getActorSnapshot(req, "Chef de parc"));
      await client.query(
        `
          UPDATE mgx_vehicle_agent_reports
          SET status = 'converted',
              details = $5::jsonb,
              converted_entity_type = 'purchase_request',
              converted_entity_id = $3,
              resolved_at = CURRENT_TIMESTAMP,
              resolved_by = $4,
              updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
        `,
        [reportId, companyId, requestId, resolveActorUserId(req), JSON.stringify(purchaseDetails)],
      );

      await insertAuditLog(client, req, companyId, "mgx.vehicle_agent_report.convert_purchase_request", reportId, {
        purchase_request_id: requestId,
        item_id: item.id,
        quantity,
      });

      await client.query("COMMIT");

      res.status(201).json({
        message: "Demande d'achat générée.",
        purchase_request_id: requestId,
        edit_url: `/mgx/purchases/requests/${requestId}/edit`,
      });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },

  convertToIntervention: async (req, res) => {
    const companyId = getCompanyId(req);
    const reportId = toNumber(req.params.id);
    await ensureInterventionMobileColumns();

    if (!reportId) {
      throw new ApiError(400, "Identifiant invalide.");
    }

    const client = await pool.connect();

    try {
      await client.query("BEGIN");

      const report = await getReportForConversion(client, reportId, companyId);

      if (!report) {
        throw new ApiError(404, "Remontée introuvable.");
      }

      if (report.converted_entity_id) {
        throw new ApiError(400, "Cette remontée est déjà convertie.");
      }

      const details = normalizeDetails(report.details);
      const maintenanceTypeRefId = toNumber(req.body.maintenance_type_ref_id || details.maintenance_type_ref_id);

      if (!maintenanceTypeRefId) {
        throw new ApiError(400, "Type d'entretien requis pour générer une intervention.");
      }

      const interventionType = cleanString(req.body.intervention_type || details.intervention_type || "corrective").toLowerCase();
      const label = nullableString(req.body.label || report.title) || "Intervention terrain";
      const depotId = toNumber(req.body.depot_id || report.depot_id);
      const supplierId = toNumber(req.body.supplier_id || details.supplier_id);
      const assignedToStaffProfileId = toNumber(req.body.assigned_to_staff_profile_id || req.body.assigned_to || details.assigned_to);
      const itemId = toNumber(req.body.item_id || details.item_id);
      const quantity = toNumber(req.body.quantity || details.quantity) || 0;

      const { rows: interventionRows } = await client.query(
        `
          INSERT INTO mgx_interventions (
            company_id,
            vehicle_id,
            depot_id,
            intervention_type,
            maintenance_type_ref_id,
            label,
            description,
            start_date,
            supplier_id,
            parts_cost,
            labor_cost,
            total_cost,
            notes,
            assigned_to_staff_profile_id,
            created_by
          )
          VALUES ($1, $2, $3, $4, $5, $6, $7, CURRENT_DATE, $8, 0, 0, 0, $9, $10, $11)
          RETURNING id
        `,
        [
          companyId,
          report.vehicle_id,
          depotId,
          interventionType,
          maintenanceTypeRefId,
          label,
          report.description,
          supplierId,
          details.maintenance_plan_id ? `Plan maintenance #${details.maintenance_plan_id}` : null,
          assignedToStaffProfileId,
          resolveActorUserId(req) || report.submitted_by || null,
        ],
      );

      const interventionId = Number(interventionRows[0].id);

      if (itemId && quantity > 0) {
        const { rows: itemRows } = await client.query(
          `
            SELECT id, label, unit_ref_id, unit_price
            FROM mgx_consumable_items
            WHERE id = $1
              AND company_id = $2
            LIMIT 1
          `,
          [itemId, companyId],
        );
        const item = itemRows[0];
        if (item) {
          const unitPrice = toNumber(req.body.unit_price || details.unit_price || item.unit_price) || 0;
          await client.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, 'part')
            `,
            [
              interventionId,
              item.id,
              item.label,
              quantity,
              toNumber(item.unit_ref_id),
              unitPrice,
              quantity * unitPrice,
            ],
          );
        }
      }

      const interventionDetails = addDetailSnapshot(details, "resolved_by_snapshot", getActorSnapshot(req, "Chef de parc"));
      await client.query(
        `
          UPDATE mgx_vehicle_agent_reports
          SET status = 'converted',
              details = $5::jsonb,
              converted_entity_type = 'intervention',
              converted_entity_id = $3,
              resolved_at = CURRENT_TIMESTAMP,
              resolved_by = $4,
              updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
        `,
        [reportId, companyId, interventionId, resolveActorUserId(req), JSON.stringify(interventionDetails)],
      );

      await insertAuditLog(client, req, companyId, "mgx.vehicle_agent_report.convert_intervention", reportId, {
        intervention_id: interventionId,
        maintenance_type_ref_id: maintenanceTypeRefId,
      });

      await client.query("COMMIT");

      res.status(201).json({
        message: "Intervention générée.",
        intervention_id: interventionId,
        edit_url: `/mgx/maintenance/interventions/${interventionId}/edit`,
      });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },
};
