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 nullableNumber = (value) => {
  if (value === "" || value === null || value === undefined) return null;
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : null;
};
const isoDateOnly = (value) => {
  const cleaned = nullableString(value);
  return cleaned ? cleaned.slice(0, 10) : null;
};

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;
};

export const ensureMgxDriversTables = async (db = pool) => {
  await db.query(`
    CREATE TABLE IF NOT EXISTS mgx_drivers (
      id BIGSERIAL PRIMARY KEY,
      company_id BIGINT NOT NULL REFERENCES companies (id) ON DELETE CASCADE,
      depot_id BIGINT REFERENCES mgx_depots (id) ON DELETE SET NULL,
      code VARCHAR(50) NOT NULL,
      full_name VARCHAR(200) NOT NULL,
      phone VARCHAR(50),
      license_number VARCHAR(100),
      license_category VARCHAR(50),
      license_expiry DATE,
      status VARCHAR(30) NOT NULL DEFAULT 'active',
      notes TEXT,
      is_active BOOLEAN NOT NULL DEFAULT TRUE,
      created_by BIGINT REFERENCES users (id) ON DELETE SET NULL,
      created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
      updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
      CONSTRAINT uq_mgx_drivers_company_code UNIQUE (company_id, code)
    )
  `);
  await db.query("ALTER TABLE mgx_vehicles ADD COLUMN IF NOT EXISTS driver_id BIGINT");
  await db.query(`
    DO $$
    BEGIN
      IF to_regclass('public.mgx_vehicle_driver_assignments') IS NOT NULL THEN
        ALTER TABLE mgx_vehicle_driver_assignments ADD COLUMN IF NOT EXISTS driver_id BIGINT;
      END IF;
    END $$;
  `);
  await db.query(`
    DO $$
    BEGIN
      IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_mgx_vehicles_driver') THEN
        ALTER TABLE mgx_vehicles
          ADD CONSTRAINT fk_mgx_vehicles_driver
          FOREIGN KEY (driver_id) REFERENCES mgx_drivers (id) ON DELETE SET NULL;
      END IF;
    END $$;
  `);
  await db.query(`
    DO $$
    BEGIN
      IF to_regclass('public.mgx_vehicle_driver_assignments') IS NOT NULL
        AND NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_mgx_vehicle_driver_assignments_driver') THEN
        ALTER TABLE mgx_vehicle_driver_assignments
          ADD CONSTRAINT fk_mgx_vehicle_driver_assignments_driver
          FOREIGN KEY (driver_id) REFERENCES mgx_drivers (id) ON DELETE SET NULL;
      END IF;
    END $$;
  `);
  await db.query("CREATE INDEX IF NOT EXISTS idx_mgx_drivers_company_active ON mgx_drivers (company_id, is_active)");
  await db.query("CREATE INDEX IF NOT EXISTS idx_mgx_drivers_depot_id ON mgx_drivers (depot_id)");
  await db.query("CREATE INDEX IF NOT EXISTS idx_mgx_vehicles_driver_id ON mgx_vehicles (driver_id)");
  await db.query(`
    DO $$
    BEGIN
      IF to_regclass('public.mgx_vehicle_driver_assignments') IS NOT NULL THEN
        EXECUTE 'CREATE INDEX IF NOT EXISTS idx_mgx_vehicle_driver_assignments_driver_id ON mgx_vehicle_driver_assignments (driver_id)';
      END IF;
    END $$;
  `);
};

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 mapDriver = (row) => ({
  id: Number(row.id),
  company_id: Number(row.company_id),
  company: row.company_name ? { id: Number(row.company_id), name: row.company_name } : null,
  depot_id: row.depot_id ? Number(row.depot_id) : null,
  depot: row.depot_id ? {
    id: Number(row.depot_id),
    code: row.depot_code,
    label: row.depot_label,
    city: row.depot_city,
  } : null,
  code: row.code,
  full_name: row.full_name,
  phone: row.phone,
  license_number: row.license_number,
  license_category: row.license_category,
  license_expiry: row.license_expiry,
  status: row.status,
  notes: row.notes,
  is_active: row.is_active,
  active_vehicle_id: row.active_vehicle_id ? Number(row.active_vehicle_id) : null,
  active_vehicle: row.active_vehicle_id ? {
    id: Number(row.active_vehicle_id),
    reference: row.active_vehicle_reference,
    plate_number: row.active_vehicle_plate,
  } : null,
  created_at: row.created_at,
  updated_at: row.updated_at,
});

const driverSelect = `
  SELECT
    dr.*,
    c.name AS company_name,
    d.code AS depot_code,
    d.label AS depot_label,
    d.city AS depot_city,
    active_vehicle.id AS active_vehicle_id,
    active_vehicle.reference AS active_vehicle_reference,
    active_vehicle.plate_number AS active_vehicle_plate
  FROM mgx_drivers dr
  LEFT JOIN companies c ON c.id = dr.company_id
  LEFT JOIN mgx_depots d ON d.id = dr.depot_id
  LEFT JOIN LATERAL (
    SELECT id, reference, plate_number
    FROM mgx_vehicles v
    WHERE v.driver_id = dr.id
      AND v.is_active = TRUE
    ORDER BY v.updated_at DESC, v.id DESC
    LIMIT 1
  ) active_vehicle ON true
`;

const myVehiclesSelect = `
  SELECT
    v.id,
    v.reference,
    v.plate_number,
    v.brand,
    v.model,
    v.photo_url,
    v.km_current,
    d.id AS depot_id,
    d.label AS depot_label,
    cat.label AS category_label,
    fuel.label AS fuel_label,
    status.code AS status_code,
    status.label AS status_label,
    status.color AS status_color
  FROM mgx_vehicles v
  LEFT JOIN mgx_depots d ON d.id = v.depot_id
  LEFT JOIN mgx_referentiels cat ON cat.id = v.category_ref_id
  LEFT JOIN mgx_referentiels fuel ON fuel.id = v.fuel_type_ref_id
  LEFT JOIN mgx_referentiels status ON status.id = v.status_ref_id
`;

const mapMobileVehicle = (row) => ({
  id: Number(row.id),
  reference: row.reference,
  plate_number: row.plate_number,
  brand: row.brand,
  model: row.model,
  photo_url: row.photo_url,
  km_current: Number(row.km_current || 0),
  depot_id: row.depot_id ? Number(row.depot_id) : null,
  depot: row.depot_id ? { id: Number(row.depot_id), label: row.depot_label } : null,
  category: row.category_label ? { label: row.category_label } : null,
  fuel_type: row.fuel_label ? { label: row.fuel_label } : null,
  status: row.status_label
    ? { code: row.status_code, label: row.status_label, color: row.status_color }
    : null,
});

const buildDriverPayload = (body) => ({
  company_id: Number(body.company_id),
  depot_id: nullableNumber(body.depot_id),
  code: cleanString(body.code).toUpperCase(),
  full_name: cleanString(body.full_name),
  phone: nullableString(body.phone),
  license_number: nullableString(body.license_number),
  license_category: nullableString(body.license_category),
  license_expiry: isoDateOnly(body.license_expiry),
  status: nullableString(body.status) || "active",
  notes: nullableString(body.notes),
  is_active: body.is_active !== false,
});

export const mgxDriversController = {
  async listMyVehicles(req, res) {
    await ensureMgxDriversTables();
    const companyId = getCompanyId(req);
    const requestedDriverId = Number(
      req.query.driver_id ||
      req.query.linked_entity_id ||
      req.user?.linkedEntityId ||
      req.user?.linked_entity_id ||
      0,
    );

    if (!requestedDriverId) {
      res.json({ items: [] });
      return;
    }

    const { rows } = await pool.query(
      `
        ${myVehiclesSelect}
        WHERE v.company_id = $1
          AND v.driver_id = $2
          AND v.is_active = TRUE
        ORDER BY v.updated_at DESC, v.id DESC
      `,
      [companyId, requestedDriverId],
    );

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

  async listDrivers(req, res) {
    await ensureMgxDriversTables();
    const allCompanies = req.query.all_companies === "true" || req.query.company_id === "all";
    const companyId = allCompanies ? null : getCompanyId(req);
    const onlyActive = req.query.active === "true";
    const values = [];
    const conditions = [];

    if (!allCompanies) {
      values.push(companyId);
      conditions.push(`dr.company_id = $${values.length}`);
    }
    if (onlyActive) {
      conditions.push("dr.is_active = TRUE");
    }

    const { rows } = await pool.query(
      `
        ${driverSelect}
        ${conditions.length ? `WHERE ${conditions.join(" AND ")}` : ""}
        ORDER BY c.name ASC, dr.is_active DESC, dr.full_name ASC
      `,
      values,
    );

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

  async getDriver(req, res) {
    await ensureMgxDriversTables();
    const companyId = getCompanyId(req);
    const { rows } = await pool.query(
      `
        ${driverSelect}
        WHERE dr.id = $1 AND dr.company_id = $2
        LIMIT 1
      `,
      [req.params.id, companyId],
    );

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

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

  async saveDriver(req, res) {
    await ensureMgxDriversTables();
    const payload = buildDriverPayload(req.body);

    if (!payload.company_id || !payload.code || !payload.full_name) {
      throw new ApiError(400, "Société, code et nom chauffeur sont obligatoires.");
    }

    const client = await pool.connect();
    try {
      await client.query("BEGIN");
      let previous = null;
      let rows;

      if (req.params.id) {
        const previousResult = await client.query(
          "SELECT * FROM mgx_drivers WHERE id = $1 AND company_id = $2",
          [req.params.id, payload.company_id],
        );
        previous = previousResult.rows[0];
        if (!previous) {
          throw new ApiError(404, "Chauffeur introuvable.");
        }

        ({ rows } = await client.query(
          `
            UPDATE mgx_drivers
            SET
              depot_id = $3,
              code = $4,
              full_name = $5,
              phone = $6,
              license_number = $7,
              license_category = $8,
              license_expiry = $9,
              status = $10,
              notes = $11,
              is_active = $12,
              updated_at = NOW()
            WHERE id = $1 AND company_id = $2
            RETURNING *
          `,
          [
            req.params.id,
            payload.company_id,
            payload.depot_id,
            payload.code,
            payload.full_name,
            payload.phone,
            payload.license_number,
            payload.license_category,
            payload.license_expiry,
            payload.status,
            payload.notes,
            payload.is_active,
          ],
        ));
      } else {
        ({ rows } = await client.query(
          `
            INSERT INTO mgx_drivers (
              company_id, depot_id, code, full_name, phone, license_number,
              license_category, license_expiry, status, notes, is_active, created_by
            )
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
            RETURNING *
          `,
          [
            payload.company_id,
            payload.depot_id,
            payload.code,
            payload.full_name,
            payload.phone,
            payload.license_number,
            payload.license_category,
            payload.license_expiry,
            payload.status,
            payload.notes,
            payload.is_active,
            req.user?.sub || null,
          ],
        ));
      }

      await logMgxAudit(client, req, {
        companyId: payload.company_id,
        action: req.params.id ? "drivers.update" : "drivers.create",
        entityType: "mgx_drivers",
        entityId: rows[0].id,
        oldData: previous,
        newData: rows[0],
      });

      await client.query("COMMIT");

      const { rows: mappedRows } = await pool.query(
        `
          ${driverSelect}
          WHERE dr.id = $1 AND dr.company_id = $2
          LIMIT 1
        `,
        [rows[0].id, payload.company_id],
      );
      res.status(req.params.id ? 200 : 201).json({ item: mapDriver(mappedRows[0]) });
    } catch (error) {
      await client.query("ROLLBACK");
      if (error.code === "23505") {
        throw new ApiError(409, "Ce code chauffeur existe déjà.");
      }
      throw error;
    } finally {
      client.release();
    }
  },

  async deleteDriver(req, res) {
    await ensureMgxDriversTables();
    const companyId = getCompanyId(req);
    const client = await pool.connect();
    try {
      await client.query("BEGIN");
      const { rows: previousRows } = await client.query(
        "SELECT * FROM mgx_drivers WHERE id = $1 AND company_id = $2",
        [req.params.id, companyId],
      );
      if (!previousRows[0]) {
        throw new ApiError(404, "Chauffeur introuvable.");
      }

      await client.query(
        `
          UPDATE mgx_drivers
          SET is_active = FALSE, status = 'inactive', updated_at = NOW()
          WHERE id = $1 AND company_id = $2
        `,
        [req.params.id, companyId],
      );

      await logMgxAudit(client, req, {
        companyId,
        action: "drivers.disable",
        entityType: "mgx_drivers",
        entityId: req.params.id,
        oldData: previousRows[0],
        newData: { is_active: false, status: "inactive" },
      });
      await client.query("COMMIT");
      res.status(204).send();
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },
};
