import { pool } from "../db/pool.js";
import { ApiError } from "../utils/ApiError.js";
import { logAudit } from "../utils/auditLog.js";
import { markFiscalDeadlineAsFiled } from "../utils/fiscalDeadlines.js";
import { formatDzdAmount } from "../utils/formatters.js";
import { FISCAL_EXONERATION_OPTIONS } from "../utils/fiscalConstants.js";
import { buildGn12PdfBuffer } from "../utils/gn12Pdf.js";

const TODAY = new Date().toISOString().slice(0, 10);
const EXONERATION_SET = new Set(FISCAL_EXONERATION_OPTIONS);

const PAYMENT_MODE_LABELS = {
  integral: "Paiement intégral",
  fractionne: "Paiement fractionné",
  minimum: "Minimum d'imposition",
};

const STATUS_LABELS = {
  brouillon: "Brouillon",
  deposee: "Déposée",
  validee: "Validée",
};

const cleanString = (value) => String(value ?? "").trim();
const roundAmount = (value) => Number(Number(value || 0).toFixed(2));

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

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

  return companyId;
};

const parseInteger = (value, label, fallback = 0) => {
  if (value === undefined || value === null || value === "") {
    return fallback;
  }

  const parsed = Number.parseInt(value, 10);

  if (!Number.isFinite(parsed)) {
    throw new ApiError(400, `${label} est invalide.`);
  }

  return parsed;
};

const parseDecimalField = (value, label, fallback = 0) => {
  if (value === undefined || value === null || value === "") {
    return fallback;
  }

  const normalized = cleanString(value).replace(/\s+/g, "").replace(",", ".");
  const parsed = Number(normalized);

  if (!Number.isFinite(parsed)) {
    throw new ApiError(400, `${label} est invalide.`);
  }

  if (parsed < 0) {
    throw new ApiError(400, `${label} ne peut pas être négatif.`);
  }

  return roundAmount(parsed);
};

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

  if (!normalized) {
    return null;
  }

  if (!/^\d{4}-\d{2}-\d{2}$/.test(normalized)) {
    throw new ApiError(400, `${label} 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, `${label} invalide.`);
  }

  return normalized;
};

const sanitizeExonerationFlags = (payload, fallback = []) => {
  const source =
    payload.exoneration_flags ?? payload.exonerationFlags ?? payload.exonerations ?? fallback;

  const rawValues = Array.isArray(source) ? source : [source];
  const normalizedValues = [
    ...new Set(rawValues.map((value) => cleanString(value)).filter(Boolean)),
  ];

  const invalidValue = normalizedValues.find((value) => !EXONERATION_SET.has(value));

  if (invalidValue) {
    throw new ApiError(400, "Une exonération sélectionnée est invalide.");
  }

  return normalizedValues;
};

const toNullableText = (value) => {
  const normalized = cleanString(value);
  return normalized || null;
};

const computeActivityRow = ({
  globalValue,
  imposableValue,
  rate,
  globalLabel,
  imposableLabel,
}) => {
  const globalAmount = parseDecimalField(globalValue, globalLabel, 0);
  const imposableAmount = parseDecimalField(
    imposableValue,
    imposableLabel,
    globalAmount,
  );

  if (imposableAmount > globalAmount) {
    throw new ApiError(400, `${imposableLabel} ne peut pas dépasser le CA global.`);
  }

  const exonereAmount = roundAmount(globalAmount - imposableAmount);
  const ifuAmount = roundAmount((imposableAmount * rate) / 100);

  return {
    global: globalAmount,
    imposable: imposableAmount,
    exonere: exonereAmount,
    ifu: ifuAmount,
  };
};

const computeMargeRow = ({
  enabled,
  globalValue,
  imposableValue,
  margeValue,
  rate,
}) => {
  if (!enabled) {
    return {
      global: 0,
      imposable: 0,
      exonere: 0,
      marge: 0,
      ifu: 0,
    };
  }

  const globalAmount = parseDecimalField(
    globalValue,
    "Le CA global marge réglementée",
    0,
  );
  const imposableAmount = parseDecimalField(
    imposableValue,
    "Le CA imposable marge réglementée",
    globalAmount,
  );
  const margeImposable = parseDecimalField(
    margeValue,
    "La marge imposable prévisionnelle",
    0,
  );

  if (imposableAmount > globalAmount) {
    throw new ApiError(
      400,
      "Le CA imposable marge réglementée ne peut pas dépasser le CA global.",
    );
  }

  const exonereAmount = roundAmount(globalAmount - imposableAmount);
  const ifuAmount = roundAmount((margeImposable * rate) / 100);

  return {
    global: globalAmount,
    imposable: imposableAmount,
    exonere: exonereAmount,
    marge: margeImposable,
    ifu: ifuAmount,
  };
};

const buildInstallments = (amount) => {
  const tranche1 = roundAmount(amount * 0.5);
  const tranche2 = roundAmount(amount * 0.25);
  const tranche3 = roundAmount(amount - tranche1 - tranche2);

  return {
    tranche1,
    tranche2,
    tranche3,
  };
};

const resolveDeclarationYear = (fiscalYear, bodyYear) => {
  const explicitYear = parseInteger(bodyYear, "L'année de déclaration", 0);

  if (explicitYear > 0) {
    return explicitYear;
  }

  if (fiscalYear?.label && /^\d{4}$/.test(String(fiscalYear.label))) {
    return Number(fiscalYear.label);
  }

  if (fiscalYear?.end_date) {
    return Number(String(fiscalYear.end_date).slice(0, 4));
  }

  return new Date().getUTCFullYear();
};

const normalizeModePaiement = (value, fallback = "integral") => {
  const normalized = cleanString(value || fallback) || fallback;

  if (!["integral", "fractionne", "minimum"].includes(normalized)) {
    throw new ApiError(400, "Le mode de paiement est invalide.");
  }

  return normalized;
};

const fetchCompany = async (db, companyId) => {
  const { rows } = await db.query(
    `
      SELECT
        id,
        name,
        nif,
        rc,
        ai,
        address,
        city,
        logo_url,
        TO_CHAR(registration_date, 'YYYY-MM-DD') AS registration_date
      FROM companies
      WHERE id = $1
      LIMIT 1
    `,
    [companyId],
  );

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

  return rows[0];
};

const fetchFiscalProfile = async (db, companyId) => {
  const { rows } = await db.query(
    `
      SELECT
        id,
        nif,
        nin,
        article_imposition,
        forme_juridique,
        code_activite,
        activity_description,
        statut_contribuable,
        exoneration_flags,
        taux_ifu_marchandises,
        taux_ifu_services,
        taux_ifu_auto_entrepreneur,
        minimum_imposition_standard,
        minimum_imposition_auto_entrepreneur,
        obligation_gn12_previsionnelle
      FROM fiscal_profiles
      WHERE company_id = $1
      LIMIT 1
    `,
    [companyId],
  );

  return rows[0] || null;
};

const fetchFiscalYears = async (db, companyId) => {
  const { rows } = await db.query(
    `
      SELECT
        id,
        label,
        TO_CHAR(start_date, 'YYYY-MM-DD') AS start_date,
        TO_CHAR(end_date, 'YYYY-MM-DD') AS end_date,
        is_current
      FROM fiscal_years
      WHERE company_id = $1
      ORDER BY start_date ASC
    `,
    [companyId],
  );

  return rows.map((row) => ({
    id: Number(row.id),
    label: row.label,
    start_date: row.start_date,
    end_date: row.end_date,
    is_current: row.is_current,
  }));
};

const resolveFiscalYearContext = async (db, companyId, fiscalYearId = 0) => {
  const fiscalYears = await fetchFiscalYears(db, companyId);
  const currentFiscalYear = fiscalYearId
    ? fiscalYears.find((item) => item.id === Number(fiscalYearId))
    : fiscalYears.find((item) => item.is_current) || fiscalYears[fiscalYears.length - 1];

  if (!currentFiscalYear) {
    throw new ApiError(404, "Aucun exercice comptable disponible.");
  }

  return currentFiscalYear;
};

const GN12_SELECT = `
  SELECT
    id,
    company_id,
    fiscal_year_id,
    annee,
    direction_wilaya,
    diw,
    recette,
    commune,
    nom_raison_sociale,
    activites_exercees,
    TO_CHAR(date_debut_activite, 'YYYY-MM-DD') AS date_debut_activite,
    adresse_lieu_exercice,
    adresse_domicile,
    exoneration_flags,
    ca_marchandises_global,
    ca_marchandises_imposable,
    ca_marchandises_exonere,
    ifu_marchandises,
    ca_services_global,
    ca_services_imposable,
    ca_services_exonere,
    ifu_services,
    ca_auto_entrepreneur_global,
    ca_auto_entrepreneur_imposable,
    ifu_auto_entrepreneur,
    ca_marge_global,
    ca_marge_imposable,
    ca_marge_exonere,
    marge_imposable,
    ifu_marge,
    is_marge_reglementee,
    ifu_total_ca,
    ifu_total_marge,
    total_ifu_du,
    montant_effectif_a_payer,
    mode_paiement,
    paiement_integral_montant,
    paiement_integral_quittance,
    TO_CHAR(paiement_integral_date, 'YYYY-MM-DD') AS paiement_integral_date,
    tranche1_montant,
    TO_CHAR(tranche1_date, 'YYYY-MM-DD') AS tranche1_date,
    tranche1_quittance,
    tranche2_montant,
    TO_CHAR(tranche2_date, 'YYYY-MM-DD') AS tranche2_date,
    tranche2_quittance,
    tranche3_montant,
    TO_CHAR(tranche3_date, 'YYYY-MM-DD') AS tranche3_date,
    tranche3_quittance,
    minimum_imposition,
    lieu_declaration,
    TO_CHAR(date_signature, 'YYYY-MM-DD') AS date_signature,
    statut,
    TO_CHAR(date_depot, 'YYYY-MM-DD') AS date_depot,
    submitted_at,
    validated_at,
    created_by,
    validated_by,
    created_at,
    updated_at
  FROM ifu_declarations_previsionnelles
`;

const normalizeGn12Declaration = (row) => {
  if (!row) {
    return null;
  }

  const caGlobalPrevisionnel = roundAmount(
    Number(row.ca_marchandises_global || 0) +
      Number(row.ca_services_global || 0) +
      Number(row.ca_auto_entrepreneur_global || 0) +
      Number(row.ca_marge_global || 0),
  );

  return {
    id: Number(row.id),
    company_id: Number(row.company_id),
    fiscal_year_id: Number(row.fiscal_year_id),
    annee: Number(row.annee),
    direction_wilaya: row.direction_wilaya || "",
    diw: row.diw || "",
    recette: row.recette || "",
    commune: row.commune || "",
    nom_raison_sociale: row.nom_raison_sociale || "",
    activites_exercees: row.activites_exercees || "",
    date_debut_activite: row.date_debut_activite || "",
    adresse_lieu_exercice: row.adresse_lieu_exercice || "",
    adresse_domicile: row.adresse_domicile || "",
    exoneration_flags: Array.isArray(row.exoneration_flags) ? row.exoneration_flags : [],
    ca_marchandises_global: Number(row.ca_marchandises_global || 0),
    ca_marchandises_imposable: Number(row.ca_marchandises_imposable || 0),
    ca_marchandises_exonere: Number(row.ca_marchandises_exonere || 0),
    ifu_marchandises: Number(row.ifu_marchandises || 0),
    ca_services_global: Number(row.ca_services_global || 0),
    ca_services_imposable: Number(row.ca_services_imposable || 0),
    ca_services_exonere: Number(row.ca_services_exonere || 0),
    ifu_services: Number(row.ifu_services || 0),
    ca_auto_entrepreneur_global: Number(row.ca_auto_entrepreneur_global || 0),
    ca_auto_entrepreneur_imposable: Number(row.ca_auto_entrepreneur_imposable || 0),
    ifu_auto_entrepreneur: Number(row.ifu_auto_entrepreneur || 0),
    ca_marge_global: Number(row.ca_marge_global || 0),
    ca_marge_imposable: Number(row.ca_marge_imposable || 0),
    ca_marge_exonere: Number(row.ca_marge_exonere || 0),
    marge_imposable: Number(row.marge_imposable || 0),
    ifu_marge: Number(row.ifu_marge || 0),
    is_marge_reglementee: Boolean(row.is_marge_reglementee),
    ifu_total_ca: Number(row.ifu_total_ca || 0),
    ifu_total_marge: Number(row.ifu_total_marge || 0),
    total_ifu_du: Number(row.total_ifu_du || 0),
    montant_effectif_a_payer: Number(row.montant_effectif_a_payer || 0),
    minimum_imposition: Number(row.minimum_imposition || 0),
    mode_paiement: row.mode_paiement,
    mode_paiement_label: PAYMENT_MODE_LABELS[row.mode_paiement] || row.mode_paiement,
    paiement_integral_montant: Number(row.paiement_integral_montant || 0),
    paiement_integral_quittance: row.paiement_integral_quittance || "",
    paiement_integral_date: row.paiement_integral_date || "",
    tranche1_montant: Number(row.tranche1_montant || 0),
    tranche1_date: row.tranche1_date || "",
    tranche1_quittance: row.tranche1_quittance || "",
    tranche2_montant: Number(row.tranche2_montant || 0),
    tranche2_date: row.tranche2_date || "",
    tranche2_quittance: row.tranche2_quittance || "",
    tranche3_montant: Number(row.tranche3_montant || 0),
    tranche3_date: row.tranche3_date || "",
    tranche3_quittance: row.tranche3_quittance || "",
    lieu_declaration: row.lieu_declaration || "",
    date_signature: row.date_signature || "",
    statut: row.statut,
    statut_label: STATUS_LABELS[row.statut] || row.statut,
    date_depot: row.date_depot || "",
    submitted_at: row.submitted_at,
    validated_at: row.validated_at,
    created_by: row.created_by ? Number(row.created_by) : null,
    validated_by: row.validated_by ? Number(row.validated_by) : null,
    created_at: row.created_at,
    updated_at: row.updated_at,
    ca_global_previsionnel: caGlobalPrevisionnel,
    formatted_ca_global_previsionnel: formatDzdAmount(caGlobalPrevisionnel),
    formatted_total_ifu_du: formatDzdAmount(row.total_ifu_du),
    formatted_montant_effectif_a_payer: formatDzdAmount(row.montant_effectif_a_payer),
    formatted_ifu_total_ca: formatDzdAmount(row.ifu_total_ca),
    formatted_ifu_total_marge: formatDzdAmount(row.ifu_total_marge),
    formatted_minimum_imposition: formatDzdAmount(row.minimum_imposition),
  };
};

const fetchDeclarationById = async (db, declarationId, companyId) => {
  const { rows } = await db.query(
    `
      ${GN12_SELECT}
      WHERE id = $1
        AND company_id = $2
      LIMIT 1
    `,
    [declarationId, companyId],
  );

  return normalizeGn12Declaration(rows[0]);
};

const buildGn12Payload = ({
  body,
  company,
  profile,
  fiscalYear,
}) => {
  const rates = {
    marchandises: Number(profile?.taux_ifu_marchandises ?? 5),
    services: Number(profile?.taux_ifu_services ?? 12),
    autoEntrepreneur: Number(profile?.taux_ifu_auto_entrepreneur ?? 0.5),
  };

  const marchandises = computeActivityRow({
    globalValue: body.ca_marchandises_global,
    imposableValue: body.ca_marchandises_imposable,
    rate: rates.marchandises,
    globalLabel: "Le CA global marchandises",
    imposableLabel: "Le CA imposable marchandises",
  });

  const services = computeActivityRow({
    globalValue: body.ca_services_global,
    imposableValue: body.ca_services_imposable,
    rate: rates.services,
    globalLabel: "Le CA global services",
    imposableLabel: "Le CA imposable services",
  });

  const autoEntrepreneur = computeActivityRow({
    globalValue: body.ca_auto_entrepreneur_global,
    imposableValue: body.ca_auto_entrepreneur_imposable,
    rate: rates.autoEntrepreneur,
    globalLabel: "Le CA global auto-entrepreneur",
    imposableLabel: "Le CA imposable auto-entrepreneur",
  });

  const marge = computeMargeRow({
    enabled: Boolean(body.is_marge_reglementee),
    globalValue: body.ca_marge_global,
    imposableValue: body.ca_marge_imposable,
    margeValue: body.marge_imposable,
    rate: rates.marchandises,
  });

  const ifuTotalCa = roundAmount(
    marchandises.ifu + services.ifu + autoEntrepreneur.ifu,
  );
  const ifuTotalMarge = roundAmount(marge.ifu);
  const totalIfuDu = roundAmount(ifuTotalCa + ifuTotalMarge);

  const minimumImposition =
    profile?.statut_contribuable === "auto_entrepreneur"
      ? Number(profile.minimum_imposition_auto_entrepreneur || 10000)
      : Number(profile?.minimum_imposition_standard || 30000);

  const montantEffectifAPayer = roundAmount(
    Math.max(totalIfuDu, minimumImposition),
  );
  const modePaiement = normalizeModePaiement(body.mode_paiement, "integral");
  const exonerationFlags = sanitizeExonerationFlags(
    body,
    profile?.exoneration_flags || [],
  );

  if (modePaiement === "minimum" && totalIfuDu > minimumImposition + 0.01) {
    throw new ApiError(
      400,
      "Le paiement minimum d'imposition n'est autorisé que lorsque l'IFU calculé est inférieur au minimum.",
    );
  }

  const installments =
    modePaiement === "fractionne"
      ? buildInstallments(montantEffectifAPayer)
      : { tranche1: 0, tranche2: 0, tranche3: 0 };

  const paiementIntegralMontant =
    modePaiement === "integral"
      ? montantEffectifAPayer
      : modePaiement === "minimum"
        ? minimumImposition
        : 0;

  return {
    company_id: company.id,
    fiscal_year_id: fiscalYear.id,
    annee: resolveDeclarationYear(fiscalYear, body.annee),
    direction_wilaya: toNullableText(body.direction_wilaya || company.city),
    diw: toNullableText(body.diw),
    recette: toNullableText(body.recette),
    commune: toNullableText(body.commune || company.city),
    nom_raison_sociale: toNullableText(body.nom_raison_sociale || company.name),
    activites_exercees: toNullableText(
      body.activites_exercees || profile?.activity_description,
    ),
    date_debut_activite: parseOptionalIsoDate(
      body.date_debut_activite || company.registration_date,
      "La date début d'activité",
    ),
    adresse_lieu_exercice: toNullableText(body.adresse_lieu_exercice || company.address),
    adresse_domicile: toNullableText(body.adresse_domicile || company.address),
    exoneration_flags: exonerationFlags,
    ca_marchandises_global: marchandises.global,
    ca_marchandises_imposable: marchandises.imposable,
    ca_marchandises_exonere: marchandises.exonere,
    ifu_marchandises: marchandises.ifu,
    ca_services_global: services.global,
    ca_services_imposable: services.imposable,
    ca_services_exonere: services.exonere,
    ifu_services: services.ifu,
    ca_auto_entrepreneur_global: autoEntrepreneur.global,
    ca_auto_entrepreneur_imposable: autoEntrepreneur.imposable,
    ifu_auto_entrepreneur: autoEntrepreneur.ifu,
    ca_marge_global: marge.global,
    ca_marge_imposable: marge.imposable,
    ca_marge_exonere: marge.exonere,
    marge_imposable: marge.marge,
    ifu_marge: marge.ifu,
    is_marge_reglementee: Boolean(body.is_marge_reglementee),
    ifu_total_ca: ifuTotalCa,
    ifu_total_marge: ifuTotalMarge,
    total_ifu_du: totalIfuDu,
    montant_effectif_a_payer: montantEffectifAPayer,
    mode_paiement: modePaiement,
    paiement_integral_montant: paiementIntegralMontant,
    paiement_integral_quittance: toNullableText(body.paiement_integral_quittance),
    paiement_integral_date: parseOptionalIsoDate(
      body.paiement_integral_date,
      "La date de quittance",
    ),
    tranche1_montant: installments.tranche1,
    tranche1_date: parseOptionalIsoDate(
      body.tranche1_date,
      "La date de la 1re tranche",
    ),
    tranche1_quittance: toNullableText(body.tranche1_quittance),
    tranche2_montant: installments.tranche2,
    tranche2_date: parseOptionalIsoDate(
      body.tranche2_date,
      "La date de la 2e tranche",
    ),
    tranche2_quittance: toNullableText(body.tranche2_quittance),
    tranche3_montant: installments.tranche3,
    tranche3_date: parseOptionalIsoDate(
      body.tranche3_date,
      "La date de la 3e tranche",
    ),
    tranche3_quittance: toNullableText(body.tranche3_quittance),
    minimum_imposition: roundAmount(minimumImposition),
    lieu_declaration: toNullableText(body.lieu_declaration),
    date_signature: parseOptionalIsoDate(body.date_signature, "La date de déclaration"),
  };
};

const validateFractionedPaymentDeadline = (declaration, candidateDate) => {
  if (declaration.mode_paiement !== "fractionne") {
    return;
  }

  const effectiveDate = candidateDate || declaration.date_signature || TODAY;
  const limitDate = `${declaration.annee}-06-30`;

  if (effectiveDate > limitDate) {
    throw new ApiError(
      400,
      "Dépôt hors délai — paiement fractionné non autorisé",
    );
  }
};

const serializeGn12Values = (payload) => [
  payload.company_id,
  payload.fiscal_year_id,
  payload.annee,
  payload.direction_wilaya,
  payload.diw,
  payload.recette,
  payload.commune,
  payload.nom_raison_sociale,
  payload.activites_exercees,
  payload.date_debut_activite,
  payload.adresse_lieu_exercice,
  payload.adresse_domicile,
  JSON.stringify(payload.exoneration_flags),
  payload.ca_marchandises_global,
  payload.ca_marchandises_imposable,
  payload.ca_marchandises_exonere,
  payload.ifu_marchandises,
  payload.ca_services_global,
  payload.ca_services_imposable,
  payload.ca_services_exonere,
  payload.ifu_services,
  payload.ca_auto_entrepreneur_global,
  payload.ca_auto_entrepreneur_imposable,
  payload.ifu_auto_entrepreneur,
  payload.ca_marge_global,
  payload.ca_marge_imposable,
  payload.ca_marge_exonere,
  payload.marge_imposable,
  payload.ifu_marge,
  payload.is_marge_reglementee,
  payload.ifu_total_ca,
  payload.ifu_total_marge,
  payload.total_ifu_du,
  payload.montant_effectif_a_payer,
  payload.mode_paiement,
  payload.paiement_integral_montant,
  payload.paiement_integral_quittance,
  payload.paiement_integral_date,
  payload.tranche1_montant,
  payload.tranche1_date,
  payload.tranche1_quittance,
  payload.tranche2_montant,
  payload.tranche2_date,
  payload.tranche2_quittance,
  payload.tranche3_montant,
  payload.tranche3_date,
  payload.tranche3_quittance,
  payload.minimum_imposition,
  payload.lieu_declaration,
  payload.date_signature,
];

const INSERT_COLUMNS = `
  company_id,
  fiscal_year_id,
  annee,
  direction_wilaya,
  diw,
  recette,
  commune,
  nom_raison_sociale,
  activites_exercees,
  date_debut_activite,
  adresse_lieu_exercice,
  adresse_domicile,
  exoneration_flags,
  ca_marchandises_global,
  ca_marchandises_imposable,
  ca_marchandises_exonere,
  ifu_marchandises,
  ca_services_global,
  ca_services_imposable,
  ca_services_exonere,
  ifu_services,
  ca_auto_entrepreneur_global,
  ca_auto_entrepreneur_imposable,
  ifu_auto_entrepreneur,
  ca_marge_global,
  ca_marge_imposable,
  ca_marge_exonere,
  marge_imposable,
  ifu_marge,
  is_marge_reglementee,
  ifu_total_ca,
  ifu_total_marge,
  total_ifu_du,
  montant_effectif_a_payer,
  mode_paiement,
  paiement_integral_montant,
  paiement_integral_quittance,
  paiement_integral_date,
  tranche1_montant,
  tranche1_date,
  tranche1_quittance,
  tranche2_montant,
  tranche2_date,
  tranche2_quittance,
  tranche3_montant,
  tranche3_date,
  tranche3_quittance,
  minimum_imposition,
  lieu_declaration,
  date_signature,
  created_by
`;

const UPDATE_COLUMNS = `
  fiscal_year_id = $3,
  annee = $4,
  direction_wilaya = $5,
  diw = $6,
  recette = $7,
  commune = $8,
  nom_raison_sociale = $9,
  activites_exercees = $10,
  date_debut_activite = $11,
  adresse_lieu_exercice = $12,
  adresse_domicile = $13,
  exoneration_flags = $14::jsonb,
  ca_marchandises_global = $15,
  ca_marchandises_imposable = $16,
  ca_marchandises_exonere = $17,
  ifu_marchandises = $18,
  ca_services_global = $19,
  ca_services_imposable = $20,
  ca_services_exonere = $21,
  ifu_services = $22,
  ca_auto_entrepreneur_global = $23,
  ca_auto_entrepreneur_imposable = $24,
  ifu_auto_entrepreneur = $25,
  ca_marge_global = $26,
  ca_marge_imposable = $27,
  ca_marge_exonere = $28,
  marge_imposable = $29,
  ifu_marge = $30,
  is_marge_reglementee = $31,
  ifu_total_ca = $32,
  ifu_total_marge = $33,
  total_ifu_du = $34,
  montant_effectif_a_payer = $35,
  mode_paiement = $36,
  paiement_integral_montant = $37,
  paiement_integral_quittance = $38,
  paiement_integral_date = $39,
  tranche1_montant = $40,
  tranche1_date = $41,
  tranche1_quittance = $42,
  tranche2_montant = $43,
  tranche2_date = $44,
  tranche2_quittance = $45,
  tranche3_montant = $46,
  tranche3_date = $47,
  tranche3_quittance = $48,
  minimum_imposition = $49,
  lieu_declaration = $50,
  date_signature = $51,
  updated_at = CURRENT_TIMESTAMP
`;

export const fiscalGn12Controller = {
  list: async (req, res) => {
    const companyId = getCompanyId(req);
    const fiscalYearId = Number(req.query.fiscal_year_id || 0);
    const currentYear = new Date().getUTCFullYear();
    const company = await fetchCompany(pool, companyId);
    const profile = await fetchFiscalProfile(pool, companyId);
    const params = [companyId];
    let whereClause = "WHERE company_id = $1";

    if (fiscalYearId) {
      params.push(fiscalYearId);
      whereClause += ` AND fiscal_year_id = $${params.length}`;
    }

    const { rows } = await pool.query(
      `
        ${GN12_SELECT}
        ${whereClause}
        ORDER BY annee DESC, created_at DESC
      `,
      params,
    );

    const items = rows.map(normalizeGn12Declaration);
    const showDeadlineAlert =
      profile?.obligation_gn12_previsionnelle !== false &&
      TODAY > `${currentYear}-05-15` &&
      !items.some((item) => item.annee === currentYear);

    res.json({
      items,
      meta: {
        company: {
          id: Number(company.id),
          name: company.name,
        },
        deadline_alert: {
          show: showDeadlineAlert,
          message: "⚠️ Dépôt GN°12 obligatoire avant le 30 juin",
          year: currentYear,
        },
      },
    });
  },

  getById: async (req, res) => {
    const companyId = getCompanyId(req);
    const declarationId = Number(req.params.id || 0);

    if (!declarationId) {
      throw new ApiError(400, "Identifiant de déclaration invalide.");
    }

    const item = await fetchDeclarationById(pool, declarationId, companyId);

    if (!item) {
      throw new ApiError(404, "Déclaration GN°12 introuvable.");
    }

    res.json({ item });
  },

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

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

      const company = await fetchCompany(client, companyId);
      const profile = await fetchFiscalProfile(client, companyId);
      const fiscalYear = await resolveFiscalYearContext(
        client,
        companyId,
        req.body.fiscal_year_id,
      );
      const payload = buildGn12Payload({
        body: req.body,
        company,
        profile,
        fiscalYear,
      });

      const existing = await client.query(
        `
          SELECT id
          FROM ifu_declarations_previsionnelles
          WHERE company_id = $1
            AND fiscal_year_id = $2
          LIMIT 1
        `,
        [companyId, fiscalYear.id],
      );

      if (existing.rows[0]) {
        throw new ApiError(
          409,
          "Une déclaration GN°12 existe déjà pour cet exercice.",
        );
      }

      const values = [...serializeGn12Values(payload), req.user?.id || null];
      const placeholders = values.map((_, index) => `$${index + 1}`).join(", ");

      const { rows } = await client.query(
        `
          INSERT INTO ifu_declarations_previsionnelles (
            ${INSERT_COLUMNS}
          )
          VALUES (${placeholders})
          RETURNING id
        `,
        values,
      );

      const saved = await fetchDeclarationById(client, rows[0].id, companyId);

      await logAudit(client, {
        companyId,
        userId: req.user?.id || null,
        action: "CREATE",
        entityType: "ifu_gn12_previsionnelle",
        entityId: saved.id,
        oldData: null,
        newData: saved,
        ipAddress: req.ip,
      });

      await client.query("COMMIT");
      res.status(201).json({ item: saved });
    } catch (error) {
      await client.query("ROLLBACK").catch(() => {});
      throw error;
    } finally {
      client.release();
    }
  },

  update: async (req, res) => {
    const companyId = getCompanyId(req, req.body);
    const declarationId = Number(req.params.id || 0);
    const client = await pool.connect();

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

      const current = await fetchDeclarationById(client, declarationId, companyId);

      if (!current) {
        throw new ApiError(404, "Déclaration GN°12 introuvable.");
      }

      if (current.statut === "validee") {
        throw new ApiError(409, "Une déclaration validée ne peut plus être modifiée.");
      }

      const company = await fetchCompany(client, companyId);
      const profile = await fetchFiscalProfile(client, companyId);
      const fiscalYear = await resolveFiscalYearContext(
        client,
        companyId,
        req.body.fiscal_year_id || current.fiscal_year_id,
      );

      const existingConflict = await client.query(
        `
          SELECT id
          FROM ifu_declarations_previsionnelles
          WHERE company_id = $1
            AND fiscal_year_id = $2
            AND id <> $3
          LIMIT 1
        `,
        [companyId, fiscalYear.id, declarationId],
      );

      if (existingConflict.rows[0]) {
        throw new ApiError(
          409,
          "Une autre déclaration GN°12 existe déjà pour cet exercice.",
        );
      }

      const payload = buildGn12Payload({
        body: {
          ...current,
          ...req.body,
        },
        company,
        profile,
        fiscalYear,
      });

      const values = [declarationId, companyId, ...serializeGn12Values(payload).slice(1)];
      values[13] = JSON.stringify(payload.exoneration_flags);

      await client.query(
        `
          UPDATE ifu_declarations_previsionnelles
          SET
            ${UPDATE_COLUMNS}
          WHERE id = $1
            AND company_id = $2
        `,
        values,
      );

      const saved = await fetchDeclarationById(client, declarationId, companyId);
      await markFiscalDeadlineAsFiled(client, {
        companyId,
        declarationType: "GN12",
        fiscalYearId: saved.fiscal_year_id,
        periodeLabel: `Exercice ${saved.annee}`,
        declarationId: saved.id,
      });

      await logAudit(client, {
        companyId,
        userId: req.user?.id || null,
        action: "UPDATE",
        entityType: "ifu_gn12_previsionnelle",
        entityId: saved.id,
        oldData: current,
        newData: saved,
        ipAddress: req.ip,
      });

      await client.query("COMMIT");
      res.json({ item: saved });
    } catch (error) {
      await client.query("ROLLBACK").catch(() => {});
      throw error;
    } finally {
      client.release();
    }
  },

  submit: async (req, res) => {
    const companyId = getCompanyId(req, req.body);
    const declarationId = Number(req.params.id || 0);
    const client = await pool.connect();

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

      const current = await fetchDeclarationById(client, declarationId, companyId);

      if (!current) {
        throw new ApiError(404, "Déclaration GN°12 introuvable.");
      }

      if (current.statut === "validee") {
        throw new ApiError(409, "La déclaration est déjà validée.");
      }

      const dateDepot =
        parseOptionalIsoDate(req.body.date_depot ?? req.body.dateDepot, "La date de dépôt") ||
        current.date_signature ||
        TODAY;

      validateFractionedPaymentDeadline(current, dateDepot);

      await client.query(
        `
          UPDATE ifu_declarations_previsionnelles
          SET
            statut = 'deposee',
            date_depot = $3,
            submitted_at = CURRENT_TIMESTAMP,
            updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
        `,
        [declarationId, companyId, dateDepot],
      );

      const saved = await fetchDeclarationById(client, declarationId, companyId);

      await logAudit(client, {
        companyId,
        userId: req.user?.id || null,
        action: "SUBMIT",
        entityType: "ifu_gn12_previsionnelle",
        entityId: saved.id,
        oldData: current,
        newData: saved,
        ipAddress: req.ip,
      });

      await client.query("COMMIT");
      res.json({ item: saved });
    } catch (error) {
      await client.query("ROLLBACK").catch(() => {});
      throw error;
    } finally {
      client.release();
    }
  },

  validate: async (req, res) => {
    const companyId = getCompanyId(req, req.body);
    const declarationId = Number(req.params.id || 0);
    const client = await pool.connect();

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

      const current = await fetchDeclarationById(client, declarationId, companyId);

      if (!current) {
        throw new ApiError(404, "Déclaration GN°12 introuvable.");
      }

      if (current.statut === "brouillon") {
        throw new ApiError(409, "La déclaration doit être marquée comme déposée avant validation.");
      }

      if (current.statut === "validee") {
        throw new ApiError(409, "La déclaration est déjà validée.");
      }

      validateFractionedPaymentDeadline(current, current.date_depot || current.date_signature);

      await client.query(
        `
          UPDATE ifu_declarations_previsionnelles
          SET
            statut = 'validee',
            validated_at = CURRENT_TIMESTAMP,
            validated_by = $3,
            updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
        `,
        [declarationId, companyId, req.user?.id || null],
      );

      const saved = await fetchDeclarationById(client, declarationId, companyId);

      await logAudit(client, {
        companyId,
        userId: req.user?.id || null,
        action: "VALIDATE",
        entityType: "ifu_gn12_previsionnelle",
        entityId: saved.id,
        oldData: current,
        newData: saved,
        ipAddress: req.ip,
      });

      await client.query("COMMIT");
      res.json({ item: saved });
    } catch (error) {
      await client.query("ROLLBACK").catch(() => {});
      throw error;
    } finally {
      client.release();
    }
  },

  pdf: async (req, res) => {
    const companyId = getCompanyId(req);
    const declarationId = Number(req.params.id || 0);

    const [company, profile, declaration] = await Promise.all([
      fetchCompany(pool, companyId),
      fetchFiscalProfile(pool, companyId),
      fetchDeclarationById(pool, declarationId, companyId),
    ]);

    if (!declaration) {
      throw new ApiError(404, "Déclaration GN°12 introuvable.");
    }

    const buffer = await buildGn12PdfBuffer({
      company,
      profile,
      declaration,
      generatedBy: req.user?.fullName || req.user?.email || "Système",
    });

    const fileName = `gn12-previsionnelle-${declaration.annee}.pdf`;

    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", `inline; filename="${fileName}"`);
    res.send(Buffer.from(buffer));
  },
};
