import { pool } from "../db/pool.js";
import { ApiError } from "../utils/ApiError.js";
import { getMgxAlertSummary, runMgxAlertEngine } from "../services/mgxAlertEngine.js";

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 toNumber = (value, fallback = 0) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
};

const parseDate = (value) => {
  if (!value) {
    return null;
  }

  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? null : date;
};

const formatEntityLabel = (row) => {
  if (row.document_id) {
    return row.plate_number
      ? `${row.plate_number} · ${row.document_type_label || "Document"}`
      : row.document_type_label || "Document";
  }

  if (row.rental_id) {
    return row.plate_number
      ? `${row.plate_number}${row.supplier_name ? ` · ${row.supplier_name}` : ""}`
      : row.supplier_name || "Location";
  }

  if (row.consumable_item_id) {
    return row.item_label || "Consommable";
  }

  return row.plate_number || row.vehicle_reference || "Véhicule";
};

const buildLinkPath = (row) => {
  if (row.document_id || row.vehicle_id) {
    return row.vehicle_id ? `/mgx/vehicles/${row.vehicle_id}` : null;
  }

  if (row.rental_id) {
    return `/mgx/rentals/${row.rental_id}/edit`;
  }

  if (row.consumable_item_id) {
    return "/mgx/consumables/items";
  }

  return null;
};

const mapAlert = (row) => {
  const dueDate = parseDate(row.due_date);
  const remainingValue = row.days_remaining === null ? null : Number(row.days_remaining);
  const triggerField = row.trigger_field || null;
  let remainingUnit = "days";
  let remainingLabel = "—";

  if (row.consumable_item_id) {
    remainingUnit = "qty";
    remainingLabel = remainingValue === null
      ? "Sous seuil"
      : remainingValue < 0
        ? `Sous seuil de ${Math.abs(remainingValue)}`.replace(".", ",")
        : `+${remainingValue}`.replace(".", ",");
  } else if (triggerField === "next_due_km") {
    remainingUnit = "km";
    remainingLabel = remainingValue === null
      ? "—"
      : remainingValue < 0
        ? `En retard ${Math.abs(remainingValue)} km`
        : `${remainingValue} km`;
  } else if (remainingValue !== null) {
    remainingLabel = remainingValue < 0
      ? `EN RETARD · ${Math.abs(remainingValue)} j`
      : `${remainingValue} j`;
  }

  return {
    id: Number(row.id),
    company_id: Number(row.company_id),
    alert_config_id: row.alert_config_id ? Number(row.alert_config_id) : null,
    alert_type_ref_id: row.alert_type_ref_id ? Number(row.alert_type_ref_id) : null,
    alert_type: row.alert_type_ref_id
      ? {
          id: Number(row.alert_type_ref_id),
          label: row.alert_type_label,
          color: row.alert_type_color,
          icon: row.alert_type_icon,
        }
      : null,
    trigger_entity: row.trigger_entity,
    trigger_field: row.trigger_field,
    vehicle_id: row.vehicle_id ? Number(row.vehicle_id) : null,
    document_id: row.document_id ? Number(row.document_id) : null,
    consumable_item_id: row.consumable_item_id ? Number(row.consumable_item_id) : null,
    rental_id: row.rental_id ? Number(row.rental_id) : null,
    title: row.title,
    message: row.message,
    due_date: dueDate ? dueDate.toISOString().slice(0, 10) : null,
    days_remaining: remainingValue,
    remaining_unit: remainingUnit,
    remaining_label: remainingLabel,
    criticality: row.criticality,
    status: row.status,
    treated_by: row.treated_by ? Number(row.treated_by) : null,
    treated_by_name: row.treated_by_name || null,
    treated_at: row.treated_at,
    treatment_notes: row.treatment_notes,
    created_at: row.created_at,
    updated_at: row.updated_at || null,
    vehicle: row.vehicle_id
      ? {
          id: Number(row.vehicle_id),
          plate_number: row.plate_number,
          reference: row.vehicle_reference,
          brand: row.brand,
          model: row.model,
        }
      : null,
    depot: row.depot_id
      ? {
          id: Number(row.depot_id),
          label: row.depot_label,
          city: row.depot_city,
        }
      : null,
    entity_label: formatEntityLabel(row),
    link_path: buildLinkPath(row),
  };
};

const alertsSelect = `
  SELECT
    a.*,
    ac.alert_type_ref_id,
    ac.trigger_entity,
    ac.trigger_field,
    rt.label AS alert_type_label,
    rt.color AS alert_type_color,
    rt.icon AS alert_type_icon,
    vehicle.id AS vehicle_id,
    vehicle.reference AS vehicle_reference,
    vehicle.plate_number,
    vehicle.brand,
    vehicle.model,
    depot.id AS depot_id,
    depot.label AS depot_label,
    depot.city AS depot_city,
    document.id AS document_id,
    document.reference_number AS document_reference_number,
    document_type.label AS document_type_label,
    item.id AS consumable_item_id,
    item.label AS item_label,
    rental.id AS rental_id,
    supplier.name AS supplier_name,
    treated_user.full_name AS treated_by_name
  FROM mgx_alerts a
  LEFT JOIN mgx_alert_configs ac ON ac.id = a.alert_config_id
  LEFT JOIN mgx_referentiels rt ON rt.id = ac.alert_type_ref_id
  LEFT JOIN mgx_vehicle_documents document ON document.id = a.document_id
  LEFT JOIN mgx_referentiels document_type ON document_type.id = document.document_type_ref_id
  LEFT JOIN mgx_rentals rental ON rental.id = a.rental_id
  LEFT JOIN mgx_consumable_items item ON item.id = a.consumable_item_id
  LEFT JOIN mgx_vehicles vehicle
    ON vehicle.id = COALESCE(a.vehicle_id, document.vehicle_id, rental.vehicle_id)
  LEFT JOIN mgx_depots depot ON depot.id = vehicle.depot_id
  LEFT JOIN mgx_suppliers supplier ON supplier.id = rental.supplier_id
  LEFT JOIN users treated_user ON treated_user.id = a.treated_by
`;

const buildWhereClause = (req, companyId, includeDateRange = false) => {
  const filters = ["a.company_id = $1"];
  const values = [companyId];

  if (req.query.criticality) {
    values.push(req.query.criticality);
    filters.push(`a.criticality = $${values.length}`);
  }

  if (req.query.status) {
    values.push(req.query.status);
    filters.push(`a.status = $${values.length}`);
  }

  if (req.query.type) {
    values.push(Number(req.query.type));
    filters.push(`ac.alert_type_ref_id = $${values.length}`);
  }

  if (req.query.vehicle_id) {
    values.push(Number(req.query.vehicle_id));
    filters.push(`COALESCE(a.vehicle_id, document.vehicle_id, rental.vehicle_id) = $${values.length}`);
  }

  if (req.query.depot_id) {
    values.push(Number(req.query.depot_id));
    filters.push(`vehicle.depot_id = $${values.length}`);
  }

  if (includeDateRange && req.query.from) {
    values.push(req.query.from);
    filters.push(`a.created_at::date >= $${values.length}`);
  }

  if (includeDateRange && req.query.to) {
    values.push(req.query.to);
    filters.push(`a.created_at::date <= $${values.length}`);
  }

  return { filters, values };
};

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,
      req.user?.sub || null,
      action,
      entityType,
      entityId,
      oldData ? JSON.stringify(oldData) : null,
      newData ? JSON.stringify(newData) : null,
      req.ip,
    ],
  );
};

const fetchAlertById = async (db, companyId, alertId) => {
  const { rows } = await db.query(
    `
      ${alertsSelect}
      WHERE a.company_id = $1
        AND a.id = $2
      LIMIT 1
    `,
    [companyId, alertId],
  );

  return rows[0] ? mapAlert(rows[0]) : null;
};

export const mgxAlertsController = {
  async listAlerts(req, res) {
    const companyId = getCompanyId(req);
    const { filters, values } = buildWhereClause(req, companyId);

    const { rows } = await pool.query(
      `
        ${alertsSelect}
        WHERE ${filters.join(" AND ")}
        ORDER BY
          CASE a.criticality
            WHEN 'critical' THEN 1
            WHEN 'warning' THEN 2
            ELSE 3
          END,
          a.due_date ASC NULLS LAST,
          a.created_at DESC
      `,
      values,
    );

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

  async treatAlert(req, res) {
    const companyId = getCompanyId(req);
    const alertId = Number(req.params.id);
    const actionNotes = String(req.body.action_notes || "").trim();

    if (!actionNotes) {
      throw new ApiError(400, "Le détail du traitement est obligatoire.");
    }

    const db = await pool.connect();

    try {
      await db.query("BEGIN");
      const previous = await fetchAlertById(db, companyId, alertId);

      if (!previous) {
        throw new ApiError(404, "Alerte introuvable.");
      }

      const { rows } = await db.query(
        `
          UPDATE mgx_alerts
          SET
            status = 'treated',
            treated_by = $3,
            treated_at = CURRENT_TIMESTAMP,
            treatment_notes = $4
          WHERE id = $1
            AND company_id = $2
          RETURNING id
        `,
        [alertId, companyId, req.user?.sub || null, actionNotes],
      );

      const item = await fetchAlertById(db, companyId, rows[0].id);
      await logMgxAudit(db, req, {
        companyId,
        action: "ALERT_TREAT",
        entityType: "mgx_alert",
        entityId: alertId,
        oldData: previous,
        newData: item,
      });
      await db.query("COMMIT");

      const summary = await getMgxAlertSummary(pool, companyId);
      req.app.get("io")?.to(`company:${companyId}`).emit("alerts:updated", {
        company_id: companyId,
        ...summary,
        created: 0,
        updated: 0,
        resolved: 0,
        new_critical: 0,
      });

      res.json({ message: "Alerte marquée comme traitée.", item, summary });
    } catch (error) {
      await db.query("ROLLBACK");
      throw error;
    } finally {
      db.release();
    }
  },

  async cancelAlert(req, res) {
    const companyId = getCompanyId(req);
    const alertId = Number(req.params.id);
    const reason = String(req.body.reason || "").trim();

    if (!reason) {
      throw new ApiError(400, "Le motif d'annulation est obligatoire.");
    }

    const db = await pool.connect();

    try {
      await db.query("BEGIN");
      const previous = await fetchAlertById(db, companyId, alertId);

      if (!previous) {
        throw new ApiError(404, "Alerte introuvable.");
      }

      const { rows } = await db.query(
        `
          UPDATE mgx_alerts
          SET
            status = 'cancelled',
            treated_by = $3,
            treated_at = CURRENT_TIMESTAMP,
            treatment_notes = $4
          WHERE id = $1
            AND company_id = $2
          RETURNING id
        `,
        [alertId, companyId, req.user?.sub || null, reason],
      );

      const item = await fetchAlertById(db, companyId, rows[0].id);
      await logMgxAudit(db, req, {
        companyId,
        action: "ALERT_CANCEL",
        entityType: "mgx_alert",
        entityId: alertId,
        oldData: previous,
        newData: item,
      });
      await db.query("COMMIT");

      const summary = await getMgxAlertSummary(pool, companyId);
      req.app.get("io")?.to(`company:${companyId}`).emit("alerts:updated", {
        company_id: companyId,
        ...summary,
        created: 0,
        updated: 0,
        resolved: 0,
        new_critical: 0,
      });

      res.json({ message: "Alerte annulée.", item, summary });
    } catch (error) {
      await db.query("ROLLBACK");
      throw error;
    } finally {
      db.release();
    }
  },

  async runEngine(req, res) {
    const companyId = getCompanyId(req);
    const summary = await runMgxAlertEngine({
      companyId,
      io: req.app.get("io"),
    });

    res.json({
      message: "Moteur d'alertes exécuté.",
      summary,
    });
  },

  async getHistory(req, res) {
    const companyId = getCompanyId(req);
    const { filters, values } = buildWhereClause(req, companyId, true);

    const [historyResult, statsResult] = await Promise.all([
      pool.query(
        `
          ${alertsSelect}
          WHERE ${filters.join(" AND ")}
          ORDER BY a.created_at DESC, a.id DESC
        `,
        values,
      ),
      pool.query(
        `
          SELECT
            COUNT(*)::int AS total_created,
            COUNT(*) FILTER (WHERE a.status = 'treated' AND (a.days_remaining IS NULL OR a.days_remaining >= 0))::int AS treated_on_time,
            COUNT(*) FILTER (WHERE a.status = 'treated' AND a.days_remaining < 0)::int AS treated_late,
            COUNT(*) FILTER (WHERE a.status = 'cancelled')::int AS cancelled
          FROM mgx_alerts a
          LEFT JOIN mgx_alert_configs ac ON ac.id = a.alert_config_id
          LEFT JOIN mgx_vehicle_documents document ON document.id = a.document_id
          LEFT JOIN mgx_rentals rental ON rental.id = a.rental_id
          LEFT JOIN mgx_vehicles vehicle
            ON vehicle.id = COALESCE(a.vehicle_id, document.vehicle_id, rental.vehicle_id)
          WHERE ${filters.join(" AND ")}
        `,
        values,
      ),
    ]);

    res.json({
      items: historyResult.rows.map(mapAlert),
      stats: {
        total_created: Number(statsResult.rows[0]?.total_created || 0),
        treated_on_time: Number(statsResult.rows[0]?.treated_on_time || 0),
        treated_late: Number(statsResult.rows[0]?.treated_late || 0),
        cancelled: Number(statsResult.rows[0]?.cancelled || 0),
      },
    });
  },

  async getSummary(req, res) {
    const companyId = getCompanyId(req);
    const summary = await getMgxAlertSummary(pool, companyId);
    res.json({ summary });
  },
};
