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 { buildGn12BisPdfBuffer } from "../utils/gn12BisPdf.js";

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

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 computeDefinitiveActivityRow = ({
  realImposableValue,
  realExonereValue,
  prevImposableValue,
  prevGlobalValue,
  rate,
  labels,
}) => {
  const realImposable = parseDecimalField(realImposableValue, labels.realImposable, 0);
  const realExonere = parseDecimalField(realExonereValue, labels.realExonere, 0);
  const realGlobal = roundAmount(realImposable + realExonere);
  const prevImposable = parseDecimalField(prevImposableValue, labels.prevImposable, 0);
  const prevGlobal = parseDecimalField(prevGlobalValue, labels.prevGlobal, prevImposable);

  if (prevImposable > prevGlobal) {
    throw new ApiError(400, `${labels.prevImposable} ne peut pas dépasser le CA prévisionnel global.`);
  }

  const prevExonere = roundAmount(prevGlobal - prevImposable);
  const complementaire = roundAmount(realImposable - prevImposable);
  const ifuComplementaire = roundAmount((Math.max(complementaire, 0) * rate) / 100);
  const actualIfu = roundAmount((realImposable * rate) / 100);

  return {
    realImposable,
    realExonere,
    realGlobal,
    prevImposable,
    prevExonere,
    prevGlobal,
    complementaire,
    ifuComplementaire,
    actualIfu,
  };
};

const computeDefinitiveMargeRow = ({
  enabled,
  realImposableValue,
  realExonereValue,
  margeRealiseeValue,
  prevImposableValue,
  prevGlobalValue,
  margePrevisionnelleValue,
  rate,
}) => {
  if (!enabled) {
    return {
      realImposable: 0,
      realExonere: 0,
      realGlobal: 0,
      prevImposable: 0,
      prevExonere: 0,
      prevGlobal: 0,
      margeRealisee: 0,
      margePrevisionnelle: 0,
      margeComplementaire: 0,
      ifuComplementaire: 0,
      actualIfu: 0,
    };
  }

  const realImposable = parseDecimalField(
    realImposableValue,
    "Le CA réalisé imposable marge réglementée",
    0,
  );
  const realExonere = parseDecimalField(
    realExonereValue,
    "Le CA réalisé exonéré marge réglementée",
    0,
  );
  const realGlobal = roundAmount(realImposable + realExonere);
  const prevImposable = parseDecimalField(
    prevImposableValue,
    "Le CA prévisionnel imposable marge réglementée",
    0,
  );
  const prevGlobal = parseDecimalField(
    prevGlobalValue,
    "Le CA prévisionnel global marge réglementée",
    prevImposable,
  );
  const margeRealisee = parseDecimalField(
    margeRealiseeValue,
    "La marge bénéficiaire réalisée",
    0,
  );
  const margePrevisionnelle = parseDecimalField(
    margePrevisionnelleValue,
    "La marge bénéficiaire prévisionnelle",
    0,
  );

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

  const prevExonere = roundAmount(prevGlobal - prevImposable);
  const margeComplementaire = roundAmount(margeRealisee - margePrevisionnelle);
  const ifuComplementaire = roundAmount((Math.max(margeComplementaire, 0) * rate) / 100);
  const actualIfu = roundAmount((margeRealisee * rate) / 100);

  return {
    realImposable,
    realExonere,
    realGlobal,
    prevImposable,
    prevExonere,
    prevGlobal,
    margeRealisee,
    margePrevisionnelle,
    margeComplementaire,
    ifuComplementaire,
    actualIfu,
  };
};

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 fetchCompany = async (db, companyId) => {
  const { rows } = await db.query(
    `
      SELECT
        id,
        name,
        nif,
        rc,
        ai,
        address,
        city,
        phone,
        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_bis
      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_BIS_SELECT = `
  SELECT
    id,
    company_id,
    fiscal_year_id,
    annee,
    direction_wilaya,
    diw,
    recette,
    commune,
    nom_raison_sociale,
    activites_exercees,
    TO_CHAR(periode_du, 'YYYY-MM-DD') AS periode_du,
    TO_CHAR(periode_au, 'YYYY-MM-DD') AS periode_au,
    TO_CHAR(date_debut_activite, 'YYYY-MM-DD') AS date_debut_activite,
    adresse_lieu_exercice,
    adresse_domicile,
    telephone,
    exoneration_flags,
    is_nouveau_contribuable,
    nb_salaries,
    total_salaires_bruts,
    total_charges_sociales,
    total_irg_salaires_acquitte,
    ca_marchandises_reel_imposable,
    ca_marchandises_reel_exonere,
    ca_marchandises_reel_global,
    ca_marchandises_prev_imposable,
    ca_marchandises_prev_exonere,
    ca_marchandises_prev_global,
    ca_marchandises_complementaire,
    ifu_complementaire_marchandises,
    ca_services_reel_imposable,
    ca_services_reel_exonere,
    ca_services_reel_global,
    ca_services_prev_imposable,
    ca_services_prev_exonere,
    ca_services_prev_global,
    ca_services_complementaire,
    ifu_complementaire_services,
    ca_ae_reel_imposable,
    ca_ae_reel_exonere,
    ca_ae_reel_global,
    ca_ae_prev_imposable,
    ca_ae_prev_exonere,
    ca_ae_prev_global,
    ca_ae_complementaire,
    ifu_complementaire_ae,
    is_marge_reglementee,
    ca_marge_reel_global,
    ca_marge_reel_imposable,
    ca_marge_reel_exonere,
    marge_realisee,
    ca_marge_prev_global,
    ca_marge_prev_imposable,
    ca_marge_prev_exonere,
    marge_previsionnelle,
    marge_complementaire,
    ifu_complementaire_marge,
    ifu_previsionnel_acquitte,
    total_ifu_complementaire,
    total_ifu_exercice,
    minimum_imposition,
    revenu_net_declare,
    montant_acquitte,
    quittance_numero,
    TO_CHAR(quittance_date, 'YYYY-MM-DD') AS quittance_date,
    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,
    created_by,
    created_at,
    updated_at
  FROM ifu_declarations_definitives
`;

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

  const caGlobalRealise = roundAmount(
    Number(row.ca_marchandises_reel_global || 0) +
      Number(row.ca_services_reel_global || 0) +
      Number(row.ca_ae_reel_global || 0) +
      Number(row.ca_marge_reel_global || 0),
  );
  const totalIfuComplementaire = Number(row.total_ifu_complementaire || 0);
  const ifuPrevisionnelAcquitte = Number(row.ifu_previsionnel_acquitte || 0);
  const minimumImposition = Number(row.minimum_imposition || 0);
  const rawComplementSum = roundAmount(
    Number(row.ifu_complementaire_marchandises || 0) +
      Number(row.ifu_complementaire_services || 0) +
      Number(row.ifu_complementaire_ae || 0) +
      Number(row.ifu_complementaire_marge || 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 || "",
    periode_du: row.periode_du || "",
    periode_au: row.periode_au || "",
    date_debut_activite: row.date_debut_activite || "",
    adresse_lieu_exercice: row.adresse_lieu_exercice || "",
    adresse_domicile: row.adresse_domicile || "",
    telephone: row.telephone || "",
    exoneration_flags: Array.isArray(row.exoneration_flags) ? row.exoneration_flags : [],
    is_nouveau_contribuable: Boolean(row.is_nouveau_contribuable),
    nb_salaries: Number(row.nb_salaries || 0),
    total_salaires_bruts: Number(row.total_salaires_bruts || 0),
    total_charges_sociales: Number(row.total_charges_sociales || 0),
    total_irg_salaires_acquitte: Number(row.total_irg_salaires_acquitte || 0),
    ca_marchandises_reel_imposable: Number(row.ca_marchandises_reel_imposable || 0),
    ca_marchandises_reel_exonere: Number(row.ca_marchandises_reel_exonere || 0),
    ca_marchandises_reel_global: Number(row.ca_marchandises_reel_global || 0),
    ca_marchandises_prev_imposable: Number(row.ca_marchandises_prev_imposable || 0),
    ca_marchandises_prev_exonere: Number(row.ca_marchandises_prev_exonere || 0),
    ca_marchandises_prev_global: Number(row.ca_marchandises_prev_global || 0),
    ca_marchandises_complementaire: Number(row.ca_marchandises_complementaire || 0),
    ifu_complementaire_marchandises: Number(row.ifu_complementaire_marchandises || 0),
    ca_services_reel_imposable: Number(row.ca_services_reel_imposable || 0),
    ca_services_reel_exonere: Number(row.ca_services_reel_exonere || 0),
    ca_services_reel_global: Number(row.ca_services_reel_global || 0),
    ca_services_prev_imposable: Number(row.ca_services_prev_imposable || 0),
    ca_services_prev_exonere: Number(row.ca_services_prev_exonere || 0),
    ca_services_prev_global: Number(row.ca_services_prev_global || 0),
    ca_services_complementaire: Number(row.ca_services_complementaire || 0),
    ifu_complementaire_services: Number(row.ifu_complementaire_services || 0),
    ca_ae_reel_imposable: Number(row.ca_ae_reel_imposable || 0),
    ca_ae_reel_exonere: Number(row.ca_ae_reel_exonere || 0),
    ca_ae_reel_global: Number(row.ca_ae_reel_global || 0),
    ca_ae_prev_imposable: Number(row.ca_ae_prev_imposable || 0),
    ca_ae_prev_exonere: Number(row.ca_ae_prev_exonere || 0),
    ca_ae_prev_global: Number(row.ca_ae_prev_global || 0),
    ca_ae_complementaire: Number(row.ca_ae_complementaire || 0),
    ifu_complementaire_ae: Number(row.ifu_complementaire_ae || 0),
    is_marge_reglementee: Boolean(row.is_marge_reglementee),
    ca_marge_reel_global: Number(row.ca_marge_reel_global || 0),
    ca_marge_reel_imposable: Number(row.ca_marge_reel_imposable || 0),
    ca_marge_reel_exonere: Number(row.ca_marge_reel_exonere || 0),
    marge_realisee: Number(row.marge_realisee || 0),
    ca_marge_prev_global: Number(row.ca_marge_prev_global || 0),
    ca_marge_prev_imposable: Number(row.ca_marge_prev_imposable || 0),
    ca_marge_prev_exonere: Number(row.ca_marge_prev_exonere || 0),
    marge_previsionnelle: Number(row.marge_previsionnelle || 0),
    marge_complementaire: Number(row.marge_complementaire || 0),
    ifu_complementaire_marge: Number(row.ifu_complementaire_marge || 0),
    ifu_previsionnel_acquitte: ifuPrevisionnelAcquitte,
    total_ifu_complementaire: totalIfuComplementaire,
    total_ifu_exercice: Number(row.total_ifu_exercice || 0),
    minimum_imposition: minimumImposition,
    revenu_net_declare: Number(row.revenu_net_declare || 0),
    montant_acquitte: Number(row.montant_acquitte || 0),
    quittance_numero: row.quittance_numero || "",
    quittance_date: row.quittance_date || "",
    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,
    created_by: row.created_by ? Number(row.created_by) : null,
    created_at: row.created_at,
    updated_at: row.updated_at,
    ca_global_realise: caGlobalRealise,
    formatted_ca_global_realise: formatDzdAmount(caGlobalRealise),
    formatted_total_ifu_complementaire: formatDzdAmount(totalIfuComplementaire),
    formatted_total_ifu_exercice: formatDzdAmount(row.total_ifu_exercice),
    formatted_ifu_previsionnel_acquitte: formatDzdAmount(ifuPrevisionnelAcquitte),
    formatted_minimum_imposition: formatDzdAmount(minimumImposition),
    minimum_gap_due: roundAmount(Math.max(totalIfuComplementaire - rawComplementSum, 0)),
    raw_complement_sum: rawComplementSum,
  };
};

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

  return normalizeGn12BisDeclaration(rows[0]);
};

const fetchGn12ByFiscalYear = async (db, companyId, fiscalYearId, year) => {
  const params = [companyId];
  let whereClause = "WHERE company_id = $1";

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

  const { rows } = await db.query(
    `
      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_imposable,
        ca_marchandises_exonere,
        ca_marchandises_global,
        ca_services_imposable,
        ca_services_exonere,
        ca_services_global,
        ca_auto_entrepreneur_imposable,
        ca_auto_entrepreneur_global,
        ca_marge_imposable,
        ca_marge_exonere,
        ca_marge_global,
        marge_imposable,
        is_marge_reglementee,
        montant_effectif_a_payer,
        minimum_imposition,
        lieu_declaration,
        TO_CHAR(date_signature, 'YYYY-MM-DD') AS date_signature
      FROM ifu_declarations_previsionnelles
      ${whereClause}
      ORDER BY updated_at DESC, created_at DESC
      LIMIT 1
    `,
    params,
  );

  if (!rows[0]) {
    return null;
  }

  return {
    ...rows[0],
    exoneration_flags: Array.isArray(rows[0].exoneration_flags)
      ? rows[0].exoneration_flags
      : [],
    ca_marchandises_imposable: Number(rows[0].ca_marchandises_imposable || 0),
    ca_marchandises_exonere: Number(rows[0].ca_marchandises_exonere || 0),
    ca_marchandises_global: Number(rows[0].ca_marchandises_global || 0),
    ca_services_imposable: Number(rows[0].ca_services_imposable || 0),
    ca_services_exonere: Number(rows[0].ca_services_exonere || 0),
    ca_services_global: Number(rows[0].ca_services_global || 0),
    ca_auto_entrepreneur_imposable: Number(rows[0].ca_auto_entrepreneur_imposable || 0),
    ca_auto_entrepreneur_global: Number(rows[0].ca_auto_entrepreneur_global || 0),
    ca_marge_imposable: Number(rows[0].ca_marge_imposable || 0),
    ca_marge_exonere: Number(rows[0].ca_marge_exonere || 0),
    ca_marge_global: Number(rows[0].ca_marge_global || 0),
    marge_imposable: Number(rows[0].marge_imposable || 0),
    montant_effectif_a_payer: Number(rows[0].montant_effectif_a_payer || 0),
    minimum_imposition: Number(rows[0].minimum_imposition || 0),
  };
};

const fetchG50TerSummary = async (db, companyId, fiscalYearId, year) => {
  const params = [companyId];
  let whereClause = "WHERE company_id = $1";

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

  const { rows } = await db.query(
    `
      SELECT
        COUNT(*)::int AS declarations_count,
        COALESCE(SUM(salaires_imposables_t), 0) AS total_salaires_bruts,
        COALESCE(SUM(montant_irg), 0) AS total_irg_salaires
      FROM g50ter_declarations
      ${whereClause}
    `,
    params,
  );

  return {
    declarations_count: Number(rows[0]?.declarations_count || 0),
    total_salaires_bruts: Number(rows[0]?.total_salaires_bruts || 0),
    total_irg_salaires: Number(rows[0]?.total_irg_salaires || 0),
  };
};

const buildGn12BisPayload = ({
  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 = computeDefinitiveActivityRow({
    realImposableValue: body.ca_marchandises_reel_imposable,
    realExonereValue: body.ca_marchandises_reel_exonere,
    prevImposableValue: body.ca_marchandises_prev_imposable,
    prevGlobalValue: body.ca_marchandises_prev_global,
    rate: rates.marchandises,
    labels: {
      realImposable: "Le CA réalisé imposable marchandises",
      realExonere: "Le CA réalisé exonéré marchandises",
      prevImposable: "Le CA prévisionnel imposable marchandises",
      prevGlobal: "Le CA prévisionnel global marchandises",
    },
  });

  const services = computeDefinitiveActivityRow({
    realImposableValue: body.ca_services_reel_imposable,
    realExonereValue: body.ca_services_reel_exonere,
    prevImposableValue: body.ca_services_prev_imposable,
    prevGlobalValue: body.ca_services_prev_global,
    rate: rates.services,
    labels: {
      realImposable: "Le CA réalisé imposable services",
      realExonere: "Le CA réalisé exonéré services",
      prevImposable: "Le CA prévisionnel imposable services",
      prevGlobal: "Le CA prévisionnel global services",
    },
  });

  const autoEntrepreneur = computeDefinitiveActivityRow({
    realImposableValue: body.ca_ae_reel_imposable,
    realExonereValue: body.ca_ae_reel_exonere,
    prevImposableValue: body.ca_ae_prev_imposable,
    prevGlobalValue: body.ca_ae_prev_global,
    rate: rates.autoEntrepreneur,
    labels: {
      realImposable: "Le CA réalisé imposable auto-entrepreneur",
      realExonere: "Le CA réalisé exonéré auto-entrepreneur",
      prevImposable: "Le CA prévisionnel imposable auto-entrepreneur",
      prevGlobal: "Le CA prévisionnel global auto-entrepreneur",
    },
  });

  const isMargeReglementee = Boolean(body.is_marge_reglementee);
  const marge = computeDefinitiveMargeRow({
    enabled: isMargeReglementee,
    realImposableValue: body.ca_marge_reel_imposable,
    realExonereValue: body.ca_marge_reel_exonere,
    margeRealiseeValue: body.marge_realisee,
    prevImposableValue: body.ca_marge_prev_imposable,
    prevGlobalValue: body.ca_marge_prev_global,
    margePrevisionnelleValue: body.marge_previsionnelle,
    rate: rates.marchandises,
  });

  const rawComplementSum = roundAmount(
    marchandises.ifuComplementaire +
      services.ifuComplementaire +
      autoEntrepreneur.ifuComplementaire +
      marge.ifuComplementaire,
  );
  const actualIfuTotal = roundAmount(
    marchandises.actualIfu + services.actualIfu + autoEntrepreneur.actualIfu + marge.actualIfu,
  );
  const minimumImposition = parseDecimalField(
    body.minimum_imposition,
    "Le minimum d'imposition",
    profile?.statut_contribuable === "auto_entrepreneur"
      ? Number(profile.minimum_imposition_auto_entrepreneur || 10000)
      : Number(profile?.minimum_imposition_standard || 30000),
  );
  const ifuPrevisionnelAcquitte = parseDecimalField(
    body.ifu_previsionnel_acquitte,
    "L'IFU prévisionnel acquitté",
    0,
  );
  const effectiveExerciseIfu = roundAmount(Math.max(actualIfuTotal, minimumImposition));
  const totalIfuComplementaire = roundAmount(
    Math.max(effectiveExerciseIfu - ifuPrevisionnelAcquitte, 0),
  );
  const totalIfuExercice = roundAmount(ifuPrevisionnelAcquitte + totalIfuComplementaire);

  return {
    company_id: Number(company.id),
    fiscal_year_id: Number(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),
    periode_du: parseOptionalIsoDate(
      body.periode_du || fiscalYear.start_date,
      "La date de début de période",
    ),
    periode_au: parseOptionalIsoDate(
      body.periode_au || fiscalYear.end_date,
      "La date de fin de période",
    ),
    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),
    telephone: toNullableText(body.telephone || company.phone),
    exoneration_flags: sanitizeExonerationFlags(body, profile?.exoneration_flags || []),
    is_nouveau_contribuable: Boolean(body.is_nouveau_contribuable),
    nb_salaries: parseInteger(body.nb_salaries, "Le nombre de salariés", 0),
    total_salaires_bruts: parseDecimalField(
      body.total_salaires_bruts,
      "Le total brut des salaires",
      0,
    ),
    total_charges_sociales: parseDecimalField(
      body.total_charges_sociales,
      "Le total des charges sociales",
      0,
    ),
    total_irg_salaires_acquitte: parseDecimalField(
      body.total_irg_salaires_acquitte,
      "Le total IRG salaires acquitté",
      0,
    ),
    ca_marchandises_reel_imposable: marchandises.realImposable,
    ca_marchandises_reel_exonere: marchandises.realExonere,
    ca_marchandises_reel_global: marchandises.realGlobal,
    ca_marchandises_prev_imposable: marchandises.prevImposable,
    ca_marchandises_prev_exonere: marchandises.prevExonere,
    ca_marchandises_prev_global: marchandises.prevGlobal,
    ca_marchandises_complementaire: marchandises.complementaire,
    ifu_complementaire_marchandises: marchandises.ifuComplementaire,
    ca_services_reel_imposable: services.realImposable,
    ca_services_reel_exonere: services.realExonere,
    ca_services_reel_global: services.realGlobal,
    ca_services_prev_imposable: services.prevImposable,
    ca_services_prev_exonere: services.prevExonere,
    ca_services_prev_global: services.prevGlobal,
    ca_services_complementaire: services.complementaire,
    ifu_complementaire_services: services.ifuComplementaire,
    ca_ae_reel_imposable: autoEntrepreneur.realImposable,
    ca_ae_reel_exonere: autoEntrepreneur.realExonere,
    ca_ae_reel_global: autoEntrepreneur.realGlobal,
    ca_ae_prev_imposable: autoEntrepreneur.prevImposable,
    ca_ae_prev_exonere: autoEntrepreneur.prevExonere,
    ca_ae_prev_global: autoEntrepreneur.prevGlobal,
    ca_ae_complementaire: autoEntrepreneur.complementaire,
    ifu_complementaire_ae: autoEntrepreneur.ifuComplementaire,
    is_marge_reglementee: isMargeReglementee,
    ca_marge_reel_global: marge.realGlobal,
    ca_marge_reel_imposable: marge.realImposable,
    ca_marge_reel_exonere: marge.realExonere,
    marge_realisee: marge.margeRealisee,
    ca_marge_prev_global: marge.prevGlobal,
    ca_marge_prev_imposable: marge.prevImposable,
    ca_marge_prev_exonere: marge.prevExonere,
    marge_previsionnelle: marge.margePrevisionnelle,
    marge_complementaire: marge.margeComplementaire,
    ifu_complementaire_marge: marge.ifuComplementaire,
    ifu_previsionnel_acquitte: ifuPrevisionnelAcquitte,
    total_ifu_complementaire: totalIfuComplementaire,
    total_ifu_exercice: totalIfuExercice,
    minimum_imposition: minimumImposition,
    revenu_net_declare: parseDecimalField(
      body.revenu_net_declare,
      "Le revenu net déclaré",
      0,
    ),
    montant_acquitte: totalIfuComplementaire,
    quittance_numero: toNullableText(body.quittance_numero),
    quittance_date: parseOptionalIsoDate(body.quittance_date, "La date de quittance"),
    lieu_declaration: toNullableText(body.lieu_declaration || company.city),
    date_signature: parseOptionalIsoDate(body.date_signature, "La date de déclaration"),
    submitted_at: body.submitted_at || null,
  };
};

const PERSISTED_COLUMNS = [
  "company_id",
  "fiscal_year_id",
  "annee",
  "direction_wilaya",
  "diw",
  "recette",
  "commune",
  "nom_raison_sociale",
  "activites_exercees",
  "periode_du",
  "periode_au",
  "date_debut_activite",
  "adresse_lieu_exercice",
  "adresse_domicile",
  "telephone",
  "exoneration_flags",
  "is_nouveau_contribuable",
  "nb_salaries",
  "total_salaires_bruts",
  "total_charges_sociales",
  "total_irg_salaires_acquitte",
  "ca_marchandises_reel_imposable",
  "ca_marchandises_reel_exonere",
  "ca_marchandises_reel_global",
  "ca_marchandises_prev_imposable",
  "ca_marchandises_prev_exonere",
  "ca_marchandises_prev_global",
  "ca_marchandises_complementaire",
  "ifu_complementaire_marchandises",
  "ca_services_reel_imposable",
  "ca_services_reel_exonere",
  "ca_services_reel_global",
  "ca_services_prev_imposable",
  "ca_services_prev_exonere",
  "ca_services_prev_global",
  "ca_services_complementaire",
  "ifu_complementaire_services",
  "ca_ae_reel_imposable",
  "ca_ae_reel_exonere",
  "ca_ae_reel_global",
  "ca_ae_prev_imposable",
  "ca_ae_prev_exonere",
  "ca_ae_prev_global",
  "ca_ae_complementaire",
  "ifu_complementaire_ae",
  "is_marge_reglementee",
  "ca_marge_reel_global",
  "ca_marge_reel_imposable",
  "ca_marge_reel_exonere",
  "marge_realisee",
  "ca_marge_prev_global",
  "ca_marge_prev_imposable",
  "ca_marge_prev_exonere",
  "marge_previsionnelle",
  "marge_complementaire",
  "ifu_complementaire_marge",
  "ifu_previsionnel_acquitte",
  "total_ifu_complementaire",
  "total_ifu_exercice",
  "minimum_imposition",
  "revenu_net_declare",
  "montant_acquitte",
  "quittance_numero",
  "quittance_date",
  "lieu_declaration",
  "date_signature",
  "submitted_at",
];

const JSONB_COLUMNS = new Set(["exoneration_flags"]);

const serializePayload = (payload) =>
  PERSISTED_COLUMNS.map((column) => {
    if (column === "exoneration_flags") {
      return JSON.stringify(payload.exoneration_flags || []);
    }

    return payload[column] ?? null;
  });

const insertColumnsSql = PERSISTED_COLUMNS.join(",\n            ");
const insertValuesSql = PERSISTED_COLUMNS.map((column, index) =>
  JSONB_COLUMNS.has(column) ? `$${index + 1}::jsonb` : `$${index + 1}`,
).join(", ");
const updateColumnsSql = PERSISTED_COLUMNS.slice(1)
  .map((column, index) => {
    const placeholder = `$${index + 3}`;
    return `${column} = ${JSONB_COLUMNS.has(column) ? `${placeholder}::jsonb` : placeholder}`;
  })
  .join(",\n            ");

const buildGn12ImportBody = ({ current, gn12 }) => ({
  ...current,
  direction_wilaya: gn12.direction_wilaya || current.direction_wilaya,
  diw: gn12.diw || current.diw,
  recette: gn12.recette || current.recette,
  commune: gn12.commune || current.commune,
  nom_raison_sociale: gn12.nom_raison_sociale || current.nom_raison_sociale,
  activites_exercees: gn12.activites_exercees || current.activites_exercees,
  date_debut_activite: gn12.date_debut_activite || current.date_debut_activite,
  adresse_lieu_exercice: gn12.adresse_lieu_exercice || current.adresse_lieu_exercice,
  adresse_domicile: gn12.adresse_domicile || current.adresse_domicile,
  exoneration_flags: gn12.exoneration_flags || current.exoneration_flags,
  is_nouveau_contribuable: false,
  ca_marchandises_prev_imposable: gn12.ca_marchandises_imposable,
  ca_marchandises_prev_global: gn12.ca_marchandises_global,
  ca_services_prev_imposable: gn12.ca_services_imposable,
  ca_services_prev_global: gn12.ca_services_global,
  ca_ae_prev_imposable: gn12.ca_auto_entrepreneur_imposable,
  ca_ae_prev_global: gn12.ca_auto_entrepreneur_global,
  is_marge_reglementee: Boolean(current.is_marge_reglementee || gn12.is_marge_reglementee),
  ca_marge_prev_imposable: gn12.ca_marge_imposable,
  ca_marge_prev_global: gn12.ca_marge_global,
  marge_previsionnelle: gn12.marge_imposable,
  ifu_previsionnel_acquitte: gn12.montant_effectif_a_payer,
  minimum_imposition: gn12.minimum_imposition,
  lieu_declaration: current.lieu_declaration || gn12.lieu_declaration,
  date_signature: current.date_signature || gn12.date_signature || TODAY,
});

export const fiscalGn12BisController = {
  list: async (req, res) => {
    const companyId = getCompanyId(req);
    const yearFilter = parseInteger(req.query.annee, "L'année de déclaration", 0);
    const currentYear = new Date().getUTCFullYear();
    const deadlineYear = currentYear - 1;
    const profile = await fetchFiscalProfile(pool, companyId);
    const params = [companyId];
    let whereClause = "WHERE company_id = $1";

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

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

    const items = rows.map(normalizeGn12BisDeclaration);
    const showDeadlineAlert =
      profile?.obligation_gn12_bis !== false &&
      TODAY > `${currentYear}-01-05` &&
      !items.some((item) => item.annee === deadlineYear);

    res.json({
      items,
      meta: {
        deadline_alert: {
          show: showDeadlineAlert,
          message: `⚠️ Dépôt GN°12 Bis obligatoire avant le 20 janvier ${currentYear}`,
          year: deadlineYear,
        },
      },
    });
  },

  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 Bis 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 existing = await client.query(
        `
          SELECT id
          FROM ifu_declarations_definitives
          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 Bis existe déjà pour cet exercice.");
      }

      const payload = buildGn12BisPayload({
        body: req.body,
        company,
        profile,
        fiscalYear,
      });
      const values = [...serializePayload(payload), req.user?.id || null];

      const { rows } = await client.query(
        `
          INSERT INTO ifu_declarations_definitives (
            ${insertColumnsSql},
            created_by
          )
          VALUES (${insertValuesSql}, $${values.length})
          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_bis_definitive",
        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 Bis introuvable.");
      }

      if (current.statut !== "brouillon") {
        throw new ApiError(409, "Seule une déclaration brouillon peut ê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_definitives
          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 Bis existe déjà pour cet exercice.");
      }

      const payload = buildGn12BisPayload({
        body: {
          ...current,
          ...req.body,
        },
        company,
        profile,
        fiscalYear,
      });
      const values = [declarationId, companyId, ...serializePayload(payload).slice(1)];

      await client.query(
        `
          UPDATE ifu_declarations_definitives
          SET
            ${updateColumnsSql},
            updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
        `,
        values,
      );

      const saved = await fetchDeclarationById(client, declarationId, companyId);
      await markFiscalDeadlineAsFiled(client, {
        companyId,
        declarationType: "GN12BIS",
        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_bis_definitive",
        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();
    }
  },

  importFromGn12: 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 Bis introuvable.");
      }

      if (current.statut !== "brouillon") {
        throw new ApiError(409, "L'import GN°12 est réservé aux déclarations brouillon.");
      }

      const [company, profile, fiscalYear, gn12] = await Promise.all([
        fetchCompany(client, companyId),
        fetchFiscalProfile(client, companyId),
        resolveFiscalYearContext(client, companyId, current.fiscal_year_id),
        fetchGn12ByFiscalYear(client, companyId, current.fiscal_year_id, current.annee),
      ]);

      if (!gn12) {
        throw new ApiError(404, "Aucune déclaration GN°12 prévisionnelle trouvée pour cet exercice.");
      }

      const payload = buildGn12BisPayload({
        body: buildGn12ImportBody({ current, gn12 }),
        company,
        profile,
        fiscalYear,
      });
      const values = [declarationId, companyId, ...serializePayload(payload).slice(1)];

      await client.query(
        `
          UPDATE ifu_declarations_definitives
          SET
            ${updateColumnsSql},
            updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
        `,
        values,
      );

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

      await logAudit(client, {
        companyId,
        userId: req.user?.id || null,
        action: "IMPORT_GN12",
        entityType: "ifu_gn12_bis_definitive",
        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();
    }
  },

  importFromG50ter: 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 Bis introuvable.");
      }

      if (current.statut !== "brouillon") {
        throw new ApiError(409, "L'import G50ter est réservé aux déclarations brouillon.");
      }

      const [company, profile, fiscalYear, g50terSummary] = await Promise.all([
        fetchCompany(client, companyId),
        fetchFiscalProfile(client, companyId),
        resolveFiscalYearContext(client, companyId, current.fiscal_year_id),
        fetchG50TerSummary(client, companyId, current.fiscal_year_id, current.annee),
      ]);

      if (!g50terSummary.declarations_count) {
        throw new ApiError(404, "Aucune déclaration G50ter trouvée pour cet exercice.");
      }

      const payload = buildGn12BisPayload({
        body: {
          ...current,
          total_salaires_bruts: g50terSummary.total_salaires_bruts,
          total_irg_salaires_acquitte: g50terSummary.total_irg_salaires,
        },
        company,
        profile,
        fiscalYear,
      });
      const values = [declarationId, companyId, ...serializePayload(payload).slice(1)];

      await client.query(
        `
          UPDATE ifu_declarations_definitives
          SET
            ${updateColumnsSql},
            updated_at = CURRENT_TIMESTAMP
          WHERE id = $1
            AND company_id = $2
        `,
        values,
      );

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

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

      await client.query("COMMIT");
      res.json({
        item: saved,
        meta: {
          declarations_count: g50terSummary.declarations_count,
        },
      });
    } 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 Bis introuvable.");
      }

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

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

      await client.query(
        `
          UPDATE ifu_declarations_definitives
          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_bis_definitive",
        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 Bis introuvable.");
    }

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

    const fileName = `gn12bis-definitive-${declaration.annee}.pdf`;

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