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

const cleanString = (value) => String(value ?? "").trim();
const resolveStringField = (payload, keys, fallback = "") => {
  for (const key of keys) {
    if (Object.prototype.hasOwnProperty.call(payload, key)) {
      return cleanString(payload[key]);
    }
  }

  return cleanString(fallback);
};

const parseOptionalIsoDate = (value, fallback = null) => {
  const normalized = cleanString(value);

  if (!normalized) {
    return fallback;
  }

  if (!/^\d{4}-\d{2}-\d{2}$/.test(normalized)) {
    throw new ApiError(400, "La date d'inscription est invalide.");
  }

  const parsedDate = new Date(`${normalized}T00:00:00Z`);
  const [year, month, day] = normalized.split("-").map(Number);

  if (
    Number.isNaN(parsedDate.getTime()) ||
    parsedDate.getUTCFullYear() !== year ||
    parsedDate.getUTCMonth() + 1 !== month ||
    parsedDate.getUTCDate() !== day
  ) {
    throw new ApiError(400, "La date d'inscription est invalide.");
  }

  return normalized;
};

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

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

  return companyId;
};

const sanitizeCompany = (company) => ({
  id: company.id,
  name: company.name,
  nif: company.nif,
  rc: company.rc,
  nis: company.nis,
  ai: company.ai,
  industry_sector: company.industry_sector,
  registration_date:
    typeof company.registration_date === "string"
      ? company.registration_date
      : company.registration_date?.toISOString?.().slice(0, 10) || null,
  address: company.address,
  city: company.city,
  phone: company.phone,
  email: company.email,
  logo_url: company.logo_url,
  currency: company.currency,
  fiscal_year_start:
    typeof company.fiscal_year_start === "string"
      ? company.fiscal_year_start
      : company.fiscal_year_start?.toISOString?.().slice(0, 10) || null,
});

const fetchSectorOptions = async (db) => {
  const { rows } = await db.query(
    `
      SELECT DISTINCT industry_sector
      FROM companies
      WHERE COALESCE(BTRIM(industry_sector), '') <> ''
      ORDER BY industry_sector ASC
    `,
  );

  return rows.map((row) => row.industry_sector).filter(Boolean);
};

export const companySettingsController = {
  get: async (req, res) => {
    const companyId = getCompanyId(req);
    const [{ rows }, sectorOptions] = await Promise.all([
      pool.query(
        `
        SELECT
          id,
          name,
          nif,
          rc,
          nis,
          ai,
          industry_sector,
          TO_CHAR(registration_date, 'YYYY-MM-DD') AS registration_date,
          address,
          city,
          phone,
          email,
          logo_url,
          currency,
          TO_CHAR(fiscal_year_start, 'YYYY-MM-DD') AS fiscal_year_start,
          created_at
        FROM companies
        WHERE id = $1
        LIMIT 1
      `,
        [companyId],
      ),
      fetchSectorOptions(pool),
    ]);

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

    const company = rows[0];

    res.json({
      item: {
        ...company,
        fiscal_year_start_month: Number(company.fiscal_year_start.slice(5, 7)),
      },
      sector_options: sectorOptions,
    });
  },

  update: async (req, res) => {
    const companyId = getCompanyId(req, req.body);
    const client = await pool.connect();

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

      const { rows: currentRows } = await client.query(
        `
          SELECT *
          FROM companies
          WHERE id = $1
          LIMIT 1
        `,
        [companyId],
      );

      if (!currentRows[0]) {
        throw new ApiError(404, "Société introuvable.");
      }

      const currentCompany = currentRows[0];
      const fiscalYearStartMonth = Number(
        req.body.fiscal_year_start_month ||
          req.body.fiscalYearStartMonth ||
          new Date(currentCompany.fiscal_year_start).getUTCMonth() + 1,
      );

      if (fiscalYearStartMonth < 1 || fiscalYearStartMonth > 12) {
        throw new ApiError(400, "Le mois de démarrage de l'exercice est invalide.");
      }

      const currentYear = new Date().getUTCFullYear();
      const fiscalYearStart = `${currentYear}-${String(fiscalYearStartMonth).padStart(2, "0")}-01`;
      const payload = {
        name: resolveStringField(req.body, ["name"], currentCompany.name),
        nif: resolveStringField(req.body, ["nif"], currentCompany.nif),
        rc: resolveStringField(req.body, ["rc"], currentCompany.rc),
        nis: resolveStringField(req.body, ["nis"], currentCompany.nis),
        ai: resolveStringField(req.body, ["ai"], currentCompany.ai),
        industry_sector: resolveStringField(
          req.body,
          ["industry_sector", "industrySector"],
          currentCompany.industry_sector,
        ),
        registration_date:
          req.body.registration_date === undefined &&
          req.body.registrationDate === undefined
            ? currentCompany.registration_date?.toISOString?.().slice(0, 10) || null
            : parseOptionalIsoDate(
                req.body.registration_date ?? req.body.registrationDate,
                null,
              ),
        address: resolveStringField(req.body, ["address"], currentCompany.address),
        city: resolveStringField(req.body, ["city"], currentCompany.city),
        phone: resolveStringField(req.body, ["phone"], currentCompany.phone),
        email: resolveStringField(req.body, ["email"], currentCompany.email),
        logo_url:
          req.body.logo_url === undefined ? currentCompany.logo_url : req.body.logo_url || null,
        currency: resolveStringField(
          req.body,
          ["currency"],
          currentCompany.currency || "DZD",
        ),
        fiscal_year_start: fiscalYearStart,
      };

      if (!payload.name) {
        throw new ApiError(400, "Le nom de la société est obligatoire.");
      }

      const { rows } = await client.query(
        `
          UPDATE companies
          SET
            name = $2,
            nif = $3,
            rc = $4,
            nis = $5,
            ai = $6,
            industry_sector = $7,
            registration_date = $8,
            address = $9,
            city = $10,
            phone = $11,
            email = $12,
            logo_url = $13,
            currency = $14,
            fiscal_year_start = $15
          WHERE id = $1
          RETURNING
            id,
            name,
            nif,
            rc,
            nis,
            ai,
            industry_sector,
            TO_CHAR(registration_date, 'YYYY-MM-DD') AS registration_date,
            address,
            city,
            phone,
            email,
            logo_url,
            currency,
            TO_CHAR(fiscal_year_start, 'YYYY-MM-DD') AS fiscal_year_start,
            created_at
        `,
        [
          companyId,
          payload.name,
          payload.nif,
          payload.rc,
          payload.nis || null,
          payload.ai || null,
          payload.industry_sector || null,
          payload.registration_date,
          payload.address,
          payload.city,
          payload.phone,
          payload.email,
          payload.logo_url,
          payload.currency,
          payload.fiscal_year_start,
        ],
      );

      await logAudit(client, {
        companyId,
        userId: req.user?.sub || null,
        action: "company_settings.update",
        entityType: "company",
        entityId: companyId,
        oldData: sanitizeCompany(currentCompany),
        newData: sanitizeCompany(rows[0]),
        ipAddress: req.ip,
      });

      await client.query("COMMIT");

      const sectorOptions = await fetchSectorOptions(pool);

      res.json({
        message: "Paramètres société mis à jour.",
        item: {
          ...rows[0],
          fiscal_year_start_month: fiscalYearStartMonth,
        },
        sector_options: sectorOptions,
      });
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  },
};
