import { pool } from "../db/pool.js";
import { ApiError } from "../utils/ApiError.js";
import { formatDzdAmount, toNumber } from "../utils/formatters.js";

const getCompanyId = (req) => {
  const companyId = Number(req.query.company_id || req.user?.companyId || 0);

  if (!companyId) {
    throw new ApiError(400, "company_id is required.");
  }

  return companyId;
};

const getDepotId = (req) => {
  const depotId = Number(req.query.depot_id || 0);
  return depotId || null;
};

const clampLimit = (value, fallback, max = 50) => {
  const parsed = Number(value || fallback);
  return Math.max(1, Math.min(Number.isFinite(parsed) ? parsed : fallback, max));
};

const getPeriodRange = (req, fallbackMonths = 1) => {
  if (req.query.period_from && req.query.period_to) {
    return {
      from: req.query.period_from,
      to: req.query.period_to,
    };
  }

  const months = clampLimit(req.query.months, fallbackMonths, 24);

  return {
    from: new Date(new Date().getFullYear(), new Date().getMonth() - months + 1, 1)
      .toISOString()
      .slice(0, 10),
    to: new Date().toISOString().slice(0, 10),
  };
};

const mapMoney = (value) => ({
  amount: toNumber(value),
  formatted: formatDzdAmount(value),
});

export const mgxDashboardController = {
  getKpis: async (req, res) => {
    const companyId = getCompanyId(req);
    const depotId = getDepotId(req);
    const { from, to } = getPeriodRange(req, 1);
    const { rows } = await pool.query(
      `
        WITH vehicle_stats AS (
          SELECT
            COUNT(*)::int AS total_vehicles,
            COUNT(*) FILTER (
              WHERE LOWER(COALESCE(status.label, '')) IN ('actif', 'active')
                 OR LOWER(COALESCE(status.code, '')) IN ('active', 'actif')
            )::int AS active_vehicles,
            COUNT(*) FILTER (
              WHERE LOWER(COALESCE(status.label, '')) LIKE '%immobil%'
                 OR LOWER(COALESCE(status.code, '')) LIKE '%immobil%'
            )::int AS immobilized_vehicles
          FROM mgx_vehicles v
          LEFT JOIN mgx_referentiels status
            ON status.id = v.status_ref_id
          WHERE v.company_id = $1
            AND ($2::bigint IS NULL OR v.depot_id = $2)
        ),
        alert_stats AS (
          SELECT
            COUNT(*) FILTER (WHERE status = 'active')::int AS active_alerts,
            COUNT(*) FILTER (WHERE status = 'active' AND criticality = 'critical')::int AS critical_alerts,
            COUNT(*) FILTER (WHERE status = 'active' AND criticality = 'warning')::int AS warning_alerts,
            COUNT(*) FILTER (WHERE status = 'active' AND criticality = 'info')::int AS info_alerts
          FROM mgx_alerts
          WHERE company_id = $1
        ),
        fuel_stats AS (
          SELECT COALESCE(SUM(total_amount), 0)::numeric AS fuel_amount
          FROM mgx_fuel_entries
          WHERE company_id = $1
            AND entry_date BETWEEN $3::date AND $4::date
            AND ($2::bigint IS NULL OR depot_id = $2)
        ),
        maintenance_stats AS (
          SELECT COALESCE(SUM(total_cost), 0)::numeric AS maintenance_amount
          FROM mgx_interventions
          WHERE company_id = $1
            AND start_date BETWEEN $3::date AND $4::date
            AND ($2::bigint IS NULL OR depot_id = $2)
        ),
        treasury_stats AS (
          SELECT
            COALESCE(SUM(CASE WHEN direction = 'in' THEN amount ELSE -amount END), 0)::numeric AS treasury_balance
          FROM mgx_treasury_movements
          WHERE company_id = $1
            AND ($2::bigint IS NULL OR depot_id = $2)
        )
        SELECT *
        FROM vehicle_stats, alert_stats, fuel_stats, maintenance_stats, treasury_stats
      `,
      [companyId, depotId, from, to],
    );

    const row = rows[0] || {};

    res.json({
      period_from: from,
      period_to: to,
      items: [
        {
          key: "vehicles",
          value: Number(row.total_vehicles || 0),
          formatted: String(row.total_vehicles || 0),
          sub_labels: [
            `${Number(row.active_vehicles || 0)} actifs`,
            `${Number(row.immobilized_vehicles || 0)} immobilisés`,
          ],
        },
        {
          key: "alerts",
          value: Number(row.active_alerts || 0),
          formatted: String(row.active_alerts || 0),
          critical: Number(row.critical_alerts || 0),
          warnings: Number(row.warning_alerts || 0),
          info: Number(row.info_alerts || 0),
          sub_labels: [
            `${Number(row.critical_alerts || 0)} critiques`,
            `${Number(row.warning_alerts || 0)} warnings`,
          ],
        },
        {
          key: "fuel",
          ...mapMoney(row.fuel_amount),
        },
        {
          key: "maintenance",
          ...mapMoney(row.maintenance_amount),
        },
        {
          key: "treasury",
          ...mapMoney(row.treasury_balance),
        },
      ],
    });
  },

  getDepotDistribution: async (req, res) => {
    const companyId = getCompanyId(req);
    const { from, to } = getPeriodRange(req, 1);
    const { rows } = await pool.query(
      `
        SELECT
          d.id,
          d.label AS depot_name,
          COUNT(DISTINCT v.id)::int AS vehicle_count,
          (
            COALESCE(SUM(DISTINCT f.total_amount), 0)
            + COALESCE(SUM(DISTINCT i.total_cost), 0)
            + COALESCE(SUM(DISTINCT po.total_ttc), 0)
          )::numeric AS monthly_cost
        FROM mgx_depots d
        LEFT JOIN mgx_vehicles v
          ON v.depot_id = d.id
        LEFT JOIN mgx_fuel_entries f
          ON f.depot_id = d.id
         AND f.entry_date BETWEEN $2::date AND $3::date
        LEFT JOIN mgx_interventions i
          ON i.depot_id = d.id
         AND i.start_date BETWEEN $2::date AND $3::date
        LEFT JOIN mgx_purchase_orders po
          ON po.depot_id = d.id
         AND po.order_date BETWEEN $2::date AND $3::date
        WHERE d.company_id = $1
          AND d.is_active = TRUE
        GROUP BY d.id, d.label
        ORDER BY vehicle_count DESC, d.label ASC
      `,
      [companyId, from, to],
    );

    res.json({
      items: rows.map((row) => ({
        id: Number(row.id),
        name: row.depot_name,
        value: Number(row.vehicle_count || 0),
        vehicle_count: Number(row.vehicle_count || 0),
        monthly_cost: Number(row.monthly_cost || 0),
        monthly_cost_formatted: formatDzdAmount(row.monthly_cost),
      })),
    });
  },

  getVehicleStatuses: async (req, res) => {
    const companyId = getCompanyId(req);
    const depotId = getDepotId(req);
    const { rows } = await pool.query(
      `
        SELECT
          COALESCE(status.id, 0) AS status_id,
          COALESCE(status.label, 'Non défini') AS label,
          COALESCE(status.color, '#64748B') AS color,
          COUNT(v.id)::int AS count
        FROM mgx_vehicles v
        LEFT JOIN mgx_referentiels status
          ON status.id = v.status_ref_id
        WHERE v.company_id = $1
          AND ($2::bigint IS NULL OR v.depot_id = $2)
        GROUP BY status.id, status.label, status.color
        ORDER BY count DESC, label ASC
      `,
      [companyId, depotId],
    );

    res.json({
      items: rows.map((row) => ({
        status_id: Number(row.status_id),
        label: row.label,
        color: row.color,
        count: Number(row.count || 0),
      })),
    });
  },

  getAlertsSummary: async (req, res) => {
    const companyId = getCompanyId(req);
    const { rows } = await pool.query(
      `
        SELECT
          criticality,
          COUNT(*)::int AS count
        FROM mgx_alerts
        WHERE company_id = $1
          AND status = 'active'
        GROUP BY criticality
      `,
      [companyId],
    );
    const summary = { critical: 0, warning: 0, info: 0 };

    for (const row of rows) {
      summary[row.criticality || "info"] = Number(row.count || 0);
    }

    res.json({ summary });
  },

  getMonthlyExpenses: async (req, res) => {
    const companyId = getCompanyId(req);
    const depotId = getDepotId(req);
    const months = clampLimit(req.query.months, 6, 12);
    const { rows } = await pool.query(
      `
        WITH months AS (
          SELECT
            generate_series(
              date_trunc('month', CURRENT_DATE) - (($3::int - 1) * INTERVAL '1 month'),
              date_trunc('month', CURRENT_DATE),
              INTERVAL '1 month'
            )::date AS month_start
        ),
        fuel AS (
          SELECT date_trunc('month', entry_date)::date AS month_start, SUM(total_amount)::numeric AS amount
          FROM mgx_fuel_entries
          WHERE company_id = $1
            AND entry_date >= (SELECT MIN(month_start) FROM months)
            AND ($2::bigint IS NULL OR depot_id = $2)
          GROUP BY date_trunc('month', entry_date)::date
        ),
        maintenance AS (
          SELECT date_trunc('month', start_date)::date AS month_start, SUM(total_cost)::numeric AS amount
          FROM mgx_interventions
          WHERE company_id = $1
            AND start_date >= (SELECT MIN(month_start) FROM months)
            AND ($2::bigint IS NULL OR depot_id = $2)
          GROUP BY date_trunc('month', start_date)::date
        ),
        purchases AS (
          SELECT date_trunc('month', order_date)::date AS month_start, SUM(total_ttc)::numeric AS amount
          FROM mgx_purchase_orders
          WHERE company_id = $1
            AND order_date >= (SELECT MIN(month_start) FROM months)
            AND ($2::bigint IS NULL OR depot_id = $2)
          GROUP BY date_trunc('month', order_date)::date
        )
        SELECT
          m.month_start,
          COALESCE(f.amount, 0)::numeric AS fuel,
          COALESCE(mt.amount, 0)::numeric AS maintenance,
          COALESCE(p.amount, 0)::numeric AS purchases
        FROM months m
        LEFT JOIN fuel f ON f.month_start = m.month_start
        LEFT JOIN maintenance mt ON mt.month_start = m.month_start
        LEFT JOIN purchases p ON p.month_start = m.month_start
        ORDER BY m.month_start ASC
      `,
      [companyId, depotId, months],
    );

    res.json({
      items: rows.map((row) => ({
        month: row.month_start,
        label: new Intl.DateTimeFormat("fr-DZ", { month: "short" }).format(row.month_start),
        fuel: Number(row.fuel || 0),
        maintenance: Number(row.maintenance || 0),
        purchases: Number(row.purchases || 0),
        fuel_formatted: formatDzdAmount(row.fuel),
        maintenance_formatted: formatDzdAmount(row.maintenance),
        purchases_formatted: formatDzdAmount(row.purchases),
      })),
    });
  },

  getUpcomingDeadlines: async (req, res) => {
    const companyId = getCompanyId(req);
    const limit = clampLimit(req.query.limit, 8, 30);
    const { rows } = await pool.query(
      `
        SELECT
          a.id,
          a.title,
          a.message,
          a.due_date,
          a.days_remaining,
          a.criticality,
          v.reference AS vehicle_reference,
          v.plate_number
        FROM mgx_alerts a
        LEFT JOIN mgx_vehicles v
          ON v.id = a.vehicle_id
        WHERE a.company_id = $1
          AND a.status = 'active'
        ORDER BY
          CASE a.criticality
            WHEN 'critical' THEN 0
            WHEN 'warning' THEN 1
            ELSE 2
          END,
          a.due_date ASC NULLS LAST,
          a.created_at DESC
        LIMIT $2
      `,
      [companyId, limit],
    );

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

  getFuelByVehicle: async (req, res) => {
    const companyId = getCompanyId(req);
    const depotId = getDepotId(req);
    const { from, to } = getPeriodRange(req, 1);
    const { rows } = await pool.query(
      `
        SELECT
          v.id,
          v.reference,
          v.plate_number,
          COALESCE(cat.label, 'Non classé') AS category_label,
          COALESCE(cat.color, '#F59E0B') AS category_color,
          COALESCE(SUM(f.total_amount), 0)::numeric AS total_amount,
          BOOL_OR(f.is_anomaly)::boolean AS has_anomaly
        FROM mgx_vehicles v
        JOIN mgx_fuel_entries f
          ON f.vehicle_id = v.id
         AND f.entry_date BETWEEN $3::date AND $4::date
        LEFT JOIN mgx_referentiels cat
          ON cat.id = v.category_ref_id
        WHERE v.company_id = $1
          AND ($2::bigint IS NULL OR v.depot_id = $2)
        GROUP BY v.id, v.reference, v.plate_number, cat.label, cat.color
        ORDER BY total_amount DESC
        LIMIT 8
      `,
      [companyId, depotId, from, to],
    );

    res.json({
      items: rows.map((row) => ({
        id: Number(row.id),
        reference: row.reference,
        plate_number: row.plate_number,
        label: `${row.reference} · ${row.plate_number}`,
        category_label: row.category_label,
        color: row.category_color,
        total_amount: Number(row.total_amount || 0),
        total_amount_formatted: formatDzdAmount(row.total_amount),
        has_anomaly: row.has_anomaly,
      })),
    });
  },

  getRecentActivity: async (req, res) => {
    const companyId = getCompanyId(req);
    const limit = clampLimit(req.query.limit, 12, 30);
    const { rows } = await pool.query(
      `
        SELECT *
        FROM (
          SELECT
            i.created_at,
            'maintenance' AS type,
            COALESCE(mt.label, 'Intervention') AS category_label,
            COALESCE(v.reference, i.label) AS reference,
            CONCAT('Intervention ', COALESCE(status.label, 'créée')) AS label,
            i.total_cost AS amount
          FROM mgx_interventions i
          LEFT JOIN mgx_vehicles v ON v.id = i.vehicle_id
          LEFT JOIN mgx_referentiels mt ON mt.id = i.maintenance_type_ref_id
          LEFT JOIN mgx_referentiels status ON status.id = i.status_ref_id
          WHERE i.company_id = $1

          UNION ALL

          SELECT
            f.created_at,
            'fuel' AS type,
            COALESCE(ft.label, 'Carburant') AS category_label,
            COALESCE(v.reference, f.receipt_number) AS reference,
            CONCAT('Plein carburant - ', f.quantity::text, ' unités') AS label,
            f.total_amount AS amount
          FROM mgx_fuel_entries f
          LEFT JOIN mgx_vehicles v ON v.id = f.vehicle_id
          LEFT JOIN mgx_referentiels ft ON ft.id = f.fuel_type_ref_id
          WHERE f.company_id = $1

          UNION ALL

          SELECT
            r.created_at,
            'purchase' AS type,
            'Réception commande' AS category_label,
            r.reception_number AS reference,
            COALESCE(r.invoice_number, 'Réception fournisseur') AS label,
            r.invoice_amount AS amount
          FROM mgx_receptions r
          WHERE r.company_id = $1

          UNION ALL

          SELECT
            a.created_at,
            'alert' AS type,
            a.criticality AS category_label,
            COALESCE(v.reference, a.title) AS reference,
            CONCAT('Alerte créée - ', a.title) AS label,
            NULL::numeric AS amount
          FROM mgx_alerts a
          LEFT JOIN mgx_vehicles v ON v.id = a.vehicle_id
          WHERE a.company_id = $1
        ) activity
        ORDER BY created_at DESC
        LIMIT $2
      `,
      [companyId, limit],
    );

    res.json({
      items: rows.map((row) => ({
        ...row,
        amount: row.amount === null ? null : Number(row.amount),
        amount_formatted: row.amount === null ? null : formatDzdAmount(row.amount),
      })),
    });
  },
};
