const MONTH_LABELS = [
  "Janvier",
  "Fevrier",
  "Mars",
  "Avril",
  "Mai",
  "Juin",
  "Juillet",
  "Aout",
  "Septembre",
  "Octobre",
  "Novembre",
  "Decembre",
];

const TLS_MONTHLY_RATES = {
  canalisation: 3,
  minier_sans_refaction: 1.5,
  minier_refaction: 1.5,
};

export const G50_TIMBRE_TEMPLATE_ROWS = [
  {
    code: "E2E00",
    label: "Droit de timbre sur etat",
    rate: 0,
  },
];

export const G50_OTHER_TAX_PRESETS = [
  {
    code: "TFP",
    label: "Taxe formation professionnelle",
    rate: 1,
  },
  {
    code: "TA",
    label: "Taxe d'apprentissage",
    rate: 1,
  },
  {
    code: "TH",
    label: "Taxe habitat",
    rate: 0,
  },
  {
    code: "TVEH",
    label: "TVA vehicules",
    rate: 19,
  },
  {
    code: "TPNEU",
    label: "Taxe sur pneus",
    rate: 0,
  },
];

export const G50_TVA_9_TEMPLATE_ROWS = [
  { code: "E3B11", label: "Biens, produits et denrees (art. 23 CTCA)", rate: 9 },
  { code: "E3B12", label: "Prestations de services (art. 23)", rate: 9 },
  { code: "E3B13", label: "Operations immobilieres (art. 23)", rate: 9 },
  { code: "E3B14", label: "Actes medicaux", rate: 9 },
  { code: "E3B15", label: "Commissionnaires et courtiers", rate: 9 },
  { code: "E3B16", label: "Fourniture d'energie", rate: 9 },
];

export const G50_TVA_19_TEMPLATE_ROWS = [
  { code: "E3B21", label: "Productions: biens, produits (art. 21)", rate: 19 },
  { code: "E3B22", label: "Revente en l'etat (art. 21)", rate: 19 },
  { code: "E3B23", label: "Travaux immobiliers (autres que 9%)", rate: 19 },
  { code: "E3B24", label: "Professions liberales", rate: 19 },
  { code: "E3B25", label: "Operations de banques et assurances", rate: 19 },
  { code: "E3B26", label: "Prestations telephones et telex", rate: 19 },
  { code: "E3B28", label: "Autres prestations de services", rate: 19 },
  { code: "E3B31", label: "Debits de boissons", rate: 19 },
  { code: "E3B34", label: "Tabacs et allumettes", rate: 19 },
  { code: "E3B35", label: "Spectacles, jeux et divertissements", rate: 19 },
  { code: "E3B36", label: "Autres prestations (art. 21)", rate: 19 },
  { code: "E3B37", label: "Consommations sur place", rate: 19 },
  { code: "AUTRE19", label: "Autres", rate: 19 },
];

const roundAmount = (value) => Number(Number(value || 0).toFixed(2));

const toAmount = (value) => roundAmount(Number(value || 0));

const normalizeCode = (value, fallback = "") => String(value ?? fallback).trim();
const normalizeLabel = (value, fallback = "") => String(value ?? fallback).trim();

export const roundTvaCaToZero = (value) => {
  const amount = toAmount(value);

  if (amount <= 0) {
    return 0;
  }

  return roundAmount(Math.floor(amount / 10) * 10);
};

const normalizeTaxRows = (rows = [], templateRows = []) => {
  const sourceRows = Array.isArray(rows) ? rows : [];
  const sourceByCode = new Map(
    sourceRows.map((row, index) => [normalizeCode(row.code, `row-${index}`), row]),
  );
  const normalizedRows = templateRows.map((template, index) => {
    const code = normalizeCode(template.code, `template-${index}`);
    const source = sourceByCode.get(code) || {};

    return {
      code: normalizeCode(source.code, template.code),
      label: normalizeLabel(source.label ?? source.operation_label, template.label),
      base: toAmount(source.base ?? source.base_imposition ?? source.ca_imposable),
      rate: toAmount(source.rate ?? template.rate),
    };
  });

  const templateKeys = new Set(
    templateRows.map((row, index) => normalizeCode(row.code, `template-${index}`)),
  );
  const additionalRows = sourceRows
    .filter((row, index) => !templateKeys.has(normalizeCode(row.code, `row-${index}`)))
    .map((row) => ({
      code: normalizeCode(row.code),
      label: normalizeLabel(row.label ?? row.operation_label),
      base: toAmount(row.base ?? row.base_imposition ?? row.ca_imposable),
      rate: toAmount(row.rate),
    }));

  return [...normalizedRows, ...additionalRows].map((row) => ({
    ...row,
    amount: roundAmount(row.base * (row.rate / 100)),
  }));
};

const normalizeTvaRows = (rows = [], templateRows = []) => {
  const sourceRows = Array.isArray(rows) ? rows : [];
  const sourceByCode = new Map(
    sourceRows.map((row, index) => [normalizeCode(row.code, `row-${index}`), row]),
  );

  return templateRows.map((template, index) => {
    const key = normalizeCode(template.code, `template-${index}`);
    const source = sourceByCode.get(key) || {};
    const caTotal = roundTvaCaToZero(source.ca_total);
    const caExonere = roundTvaCaToZero(source.ca_exonere);
    const caImposable = roundAmount(Math.max(caTotal - caExonere, 0));
    const rate = toAmount(source.rate ?? template.rate);

    return {
      code: normalizeCode(source.code, template.code),
      label: normalizeLabel(source.label ?? source.operation_label, template.label),
      ca_total: caTotal,
      ca_exonere: caExonere,
      ca_imposable: caImposable,
      rate,
      montant_droits: roundAmount(caImposable * (rate / 100)),
    };
  });
};

export const deriveFiscalDeclarationYear = (fiscalYear) => {
  if (!fiscalYear) {
    return new Date().getUTCFullYear();
  }

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

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

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

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

export const buildG50PeriodMeta = ({ periodicite, annee, mois = null, trimestre = null }) => {
  if (periodicite === "mensuel") {
    const monthIndex = Number(mois) - 1;
    const deadlineMonth = Number(mois) === 12 ? 1 : Number(mois) + 1;
    const deadlineYear = Number(mois) === 12 ? Number(annee) + 1 : Number(annee);

    return {
      periodicite,
      annee: Number(annee),
      mois: Number(mois),
      trimestre: null,
      period_label: `${MONTH_LABELS[monthIndex]} ${annee}`,
      deadline_date: `${String(deadlineYear)}-${String(deadlineMonth).padStart(2, "0")}-20`,
      deadline_label: `20 ${MONTH_LABELS[deadlineMonth - 1]} ${deadlineYear}`,
    };
  }

  const quarterMonths = {
    1: [1, 2, 3],
    2: [4, 5, 6],
    3: [7, 8, 9],
    4: [10, 11, 12],
  }[Number(trimestre)] || [1, 2, 3];
  const deadlineMonth = Number(trimestre) === 4 ? 1 : Number(trimestre) * 3 + 1;
  const deadlineYear = Number(trimestre) === 4 ? Number(annee) + 1 : Number(annee);

  return {
    periodicite,
    annee: Number(annee),
    mois: null,
    trimestre: Number(trimestre),
    period_label: `T${trimestre} ${annee} (${quarterMonths
      .map((month) => MONTH_LABELS[month - 1])
      .join(", ")})`,
    deadline_date: `${String(deadlineYear)}-${String(deadlineMonth).padStart(2, "0")}-20`,
    deadline_label: `20 ${MONTH_LABELS[deadlineMonth - 1]} ${deadlineYear}`,
  };
};

export const buildExpectedG50Periods = ({ annee, periodicite }) => {
  if (periodicite === "trimestriel") {
    return [1, 2, 3, 4].map((trimestre) =>
      buildG50PeriodMeta({
        periodicite,
        annee,
        trimestre,
      }),
    );
  }

  return Array.from({ length: 12 }, (_, index) =>
    buildG50PeriodMeta({
      periodicite: "mensuel",
      annee,
      mois: index + 1,
    }),
  );
};

export const getCurrentG50Period = (periodicite, value = new Date()) => {
  const date = value instanceof Date ? value : new Date(value);
  const annee = date.getUTCFullYear();
  const mois = date.getUTCMonth() + 1;

  if (periodicite === "trimestriel") {
    return {
      annee,
      trimestre: Math.floor((mois - 1) / 3) + 1,
      mois: null,
    };
  }

  return {
    annee,
    mois,
    trimestre: null,
  };
};

export const getPreviousG50Period = ({ periodicite, annee, mois = null, trimestre = null }) => {
  if (periodicite === "trimestriel") {
    if (Number(trimestre) === 1) {
      return {
        periodicite,
        annee: Number(annee) - 1,
        trimestre: 4,
        mois: null,
      };
    }

    return {
      periodicite,
      annee: Number(annee),
      trimestre: Number(trimestre) - 1,
      mois: null,
    };
  }

  if (Number(mois) === 1) {
    return {
      periodicite,
      annee: Number(annee) - 1,
      mois: 12,
      trimestre: null,
    };
  }

  return {
    periodicite,
    annee: Number(annee),
    mois: Number(mois) - 1,
    trimestre: null,
  };
};

export const getG50DateRange = ({ periodicite, annee, mois = null, trimestre = null }) => {
  if (periodicite === "trimestriel") {
    const monthStart = (Number(trimestre) - 1) * 3 + 1;
    const start = new Date(Date.UTC(Number(annee), monthStart - 1, 1));
    const end = new Date(Date.UTC(Number(annee), monthStart + 2, 0));

    return {
      from: start.toISOString().slice(0, 10),
      to: end.toISOString().slice(0, 10),
    };
  }

  const start = new Date(Date.UTC(Number(annee), Number(mois) - 1, 1));
  const end = new Date(Date.UTC(Number(annee), Number(mois), 0));

  return {
    from: start.toISOString().slice(0, 10),
    to: end.toISOString().slice(0, 10),
  };
};

export const ensureG50Declarations = async ({
  db,
  companyId,
  fiscalYears,
  periodicite = "mensuel",
  userId = null,
}) => {
  for (const fiscalYear of fiscalYears) {
    const annee = deriveFiscalDeclarationYear(fiscalYear);
    const periods = buildExpectedG50Periods({
      annee,
      periodicite,
    });

    for (const period of periods) {
      await db.query(
        `
          INSERT INTO g50_declarations (
            company_id,
            mois,
            trimestre,
            annee,
            periodicite,
            created_by
          )
          VALUES ($1, $2, $3, $4, $5, $6)
          ON CONFLICT DO NOTHING
        `,
        [
          companyId,
          period.mois,
          period.trimestre,
          period.annee,
          periodicite,
          userId,
        ],
      );
    }
  }
};

export const computeCadre1Totals = (payload) => {
  const total = roundAmount(
    toAmount(payload.e1m10_acompte_ibs) +
      toAmount(payload.solde_liquidation_ibs) +
      toAmount(payload.acompte_icbs) +
      toAmount(payload.solde_liquidation_icbs),
  );

  return {
    total_cadre1: total,
  };
};

export const computeCadre2Totals = (payload) => {
  const e1m30 = roundAmount(
    toAmount(payload.e1m30_base_imposable) * (toAmount(payload.taux_e1m30) / 100),
  );
  const e1m40 = roundAmount(
    toAmount(payload.e1m40_base_imposable) * (toAmount(payload.taux_e1m40) / 100),
  );

  return {
    e1m30_ibs_entreprises_etrangeres: e1m30,
    e1m40_ibs_autres_retenues: e1m40,
    total_cadre2: roundAmount(e1m30 + e1m40),
  };
};

export const computeCadre3Totals = (payload) => ({
  total_cadre3: roundAmount(
    toAmount(payload.acompte_irg_bic_bnc) + toAmount(payload.solde_liquidation_irg),
  ),
});

export const computeCadre4Totals = (detail = {}) => {
  const nextDetail = {
    e1l20: {
      base: toAmount(detail.e1l20?.base),
      rate_label: detail.e1l20?.rate_label || "Bareme",
      amount: toAmount(detail.e1l20?.amount),
    },
    e1l30: {
      base: toAmount(detail.e1l30?.base),
      rate: detail.e1l30?.rate ?? 10,
      amount: roundAmount(toAmount(detail.e1l30?.base) * (toAmount(detail.e1l30?.rate ?? 10) / 100)),
    },
    e1l40: {
      base: toAmount(detail.e1l40?.base),
      rate: detail.e1l40?.rate ?? 15,
      amount: roundAmount(toAmount(detail.e1l40?.base) * (toAmount(detail.e1l40?.rate ?? 15) / 100)),
    },
    e1l60: {
      base: toAmount(detail.e1l60?.base),
      rate: detail.e1l60?.rate ?? 50,
      amount: roundAmount(toAmount(detail.e1l60?.base) * (toAmount(detail.e1l60?.rate ?? 50) / 100)),
    },
    etrangeres: {
      base: toAmount(detail.etrangeres?.base),
      rate: toAmount(detail.etrangeres?.rate),
      amount: roundAmount(
        toAmount(detail.etrangeres?.base) * (toAmount(detail.etrangeres?.rate) / 100),
      ),
    },
    e1l80: {
      base: toAmount(detail.e1l80?.base),
      rate: toAmount(detail.e1l80?.rate),
      amount: roundAmount(toAmount(detail.e1l80?.base) * (toAmount(detail.e1l80?.rate) / 100)),
    },
  };

  const total = roundAmount(
    Object.values(nextDetail).reduce((sum, row) => sum + toAmount(row.amount), 0),
  );

  return {
    irg_retenues_detail: nextDetail,
    total_cadre4: total,
  };
};

export const computeCadre5Totals = (detail = {}) => {
  const monthly = {
    canalisation: {
      ca: toAmount(detail.monthly?.canalisation?.ca),
      base: toAmount(detail.monthly?.canalisation?.base),
      rate: TLS_MONTHLY_RATES.canalisation,
    },
    minier_sans_refaction: {
      ca: toAmount(detail.monthly?.minier_sans_refaction?.ca),
      base: toAmount(detail.monthly?.minier_sans_refaction?.base),
      rate: TLS_MONTHLY_RATES.minier_sans_refaction,
    },
    minier_refaction: {
      ca: toAmount(detail.monthly?.minier_refaction?.ca),
      base: toAmount(detail.monthly?.minier_refaction?.base),
      rate: TLS_MONTHLY_RATES.minier_refaction,
    },
  };

  monthly.canalisation.amount = roundAmount(monthly.canalisation.base * (monthly.canalisation.rate / 100));
  monthly.minier_sans_refaction.amount = roundAmount(
    monthly.minier_sans_refaction.base * (monthly.minier_sans_refaction.rate / 100),
  );
  monthly.minier_refaction.amount = roundAmount(
    monthly.minier_refaction.base * (monthly.minier_refaction.rate / 100),
  );

  const accomptes = {
    enabled: Boolean(detail.accomptes?.enabled),
    canalisation: {
      acompte: toAmount(detail.accomptes?.canalisation?.acompte),
      solde: toAmount(detail.accomptes?.canalisation?.solde),
    },
    minier_sans_refaction: {
      acompte: toAmount(detail.accomptes?.minier_sans_refaction?.acompte),
      solde: toAmount(detail.accomptes?.minier_sans_refaction?.solde),
    },
    minier_refaction: {
      acompte: toAmount(detail.accomptes?.minier_refaction?.acompte),
      solde: toAmount(detail.accomptes?.minier_refaction?.solde),
    },
  };

  const totalMonthly = roundAmount(
    monthly.canalisation.amount +
      monthly.minier_sans_refaction.amount +
      monthly.minier_refaction.amount,
  );
  const totalAccomptes = accomptes.enabled
    ? roundAmount(
        accomptes.canalisation.acompte +
          accomptes.canalisation.solde +
          accomptes.minier_sans_refaction.acompte +
          accomptes.minier_sans_refaction.solde +
          accomptes.minier_refaction.acompte +
          accomptes.minier_refaction.solde,
      )
    : 0;

  return {
    tls_detail: {
      monthly,
      exemptions: Array.isArray(detail.exemptions) ? detail.exemptions : [],
      accomptes,
    },
    total_cadre5: roundAmount(totalMonthly + totalAccomptes),
  };
};

export const computeCadre6Totals = (detail = {}) => {
  const rows = normalizeTaxRows(detail.rows, G50_TIMBRE_TEMPLATE_ROWS);

  return {
    droits_timbre_detail: { rows },
    total_cadre6: roundAmount(rows.reduce((sum, row) => sum + toAmount(row.amount), 0)),
  };
};

export const computeCadre7Totals = (detail = {}) => {
  const rows = normalizeTaxRows(detail.rows, []);

  return {
    autres_taxes_detail: { rows },
    total_cadre7: roundAmount(rows.reduce((sum, row) => sum + toAmount(row.amount), 0)),
  };
};

export const computeCadre8Totals = ({ caDetail = {}, deductionsDetail = {} } = {}) => {
  const rows9 = normalizeTvaRows(caDetail.rows9, G50_TVA_9_TEMPLATE_ROWS);
  const rows19 = normalizeTvaRows(caDetail.rows19, G50_TVA_19_TEMPLATE_ROWS);
  const e3b91 = toAmount(deductionsDetail.e3b91);
  const e3b92 = toAmount(deductionsDetail.e3b92);
  const e3b93 = toAmount(deductionsDetail.e3b93);
  const e3b94 = toAmount(deductionsDetail.e3b94);
  const e3b95 = toAmount(deductionsDetail.e3b95);
  const e3b96 = toAmount(deductionsDetail.e3b96);
  const e3b97 = toAmount(deductionsDetail.e3b97);
  const e3b98 = toAmount(deductionsDetail.e3b98);

  const totalDroits = roundAmount(
    [...rows9, ...rows19].reduce((sum, row) => sum + toAmount(row.montant_droits), 0),
  );
  const totalDeductions = roundAmount(e3b91 + e3b92 + e3b93 + e3b94 + e3b95 + e3b96);
  const totalARappeler = roundAmount(totalDroits + e3b97 + e3b98);
  const tvaAPayer = roundAmount(Math.max(totalARappeler - totalDeductions, 0));
  const precompteReporter = roundAmount(Math.max(totalDeductions - totalARappeler, 0));

  return {
    tva_ca_imposable_detail: {
      rows9,
      rows19,
    },
    tva_deductions_detail: {
      e3b91,
      e3b92,
      e3b93,
      e3b94,
      e3b95,
      e3b96,
      e3b97,
      e3b98,
    },
    tva_total_droits: totalDroits,
    tva_total_deductions: totalDeductions,
    tva_a_payer: tvaAPayer,
    tva_precompte_reporter: precompteReporter,
    total_cadre8: tvaAPayer,
    tva_total_a_rappeler: totalARappeler,
  };
};
