import { body, param, query } from "express-validator";
import { Router } from "express";

import { fiscalController } from "../controllers/fiscalController.js";
import { fiscalCalendarController } from "../controllers/fiscalCalendarController.js";
import { fiscalGn12Controller } from "../controllers/fiscalGn12Controller.js";
import { fiscalGn12BisController } from "../controllers/fiscalGn12BisController.js";
import { fiscalG50terController } from "../controllers/fiscalG50terController.js";
import { fiscalG50Controller } from "../controllers/fiscalG50Controller.js";
import { fiscalPenaltyController } from "../controllers/fiscalPenaltyController.js";
import { fiscalDashboardController } from "../controllers/fiscalDashboardController.js";
import { fiscalTaxCodesController } from "../controllers/fiscalTaxCodesController.js";
import { fiscalRulesController } from "../controllers/fiscalRulesController.js";
import { fiscalMappingController } from "../controllers/fiscalMappingController.js";
import { fiscalG50EngineController } from "../controllers/fiscalG50EngineController.js";
import { handleValidation } from "../middleware/validation.js";
import { asyncHandler } from "../utils/asyncHandler.js";

const router = Router();

const saveProfileValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("code_activite")
    .optional({ values: "falsy" })
    .matches(/^\d{1,4}$/)
    .withMessage("Le code activité doit contenir 4 chiffres maximum."),
  body("g50_periodicite")
    .optional({ values: "falsy" })
    .isIn(["mensuel", "trimestriel"])
    .withMessage("La périodicité G50 est invalide."),
  body("exoneration_flags")
    .optional()
    .isArray()
    .withMessage("Les exonérations doivent être transmises sous forme de liste."),
  handleValidation,
];

const gn12BaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("fiscal_year_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Identifiant exercice invalide."),
  body("annee")
    .optional({ values: "falsy" })
    .isInt({ min: 2000, max: 2100 })
    .withMessage("L'année de déclaration est invalide."),
  body("mode_paiement")
    .optional({ values: "falsy" })
    .isIn(["integral", "fractionne", "minimum"])
    .withMessage("Le mode de paiement est invalide."),
  body("is_marge_reglementee")
    .optional()
    .isBoolean()
    .withMessage("Le statut marge réglementée est invalide."),
  body("exoneration_flags")
    .optional()
    .isArray()
    .withMessage("Les exonérations doivent être transmises sous forme de liste."),
  body([
    "date_debut_activite",
    "paiement_integral_date",
    "tranche1_date",
    "tranche2_date",
    "tranche3_date",
    "date_signature",
    "date_depot",
  ])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date GN°12 est invalide."),
  handleValidation,
];

const gn12BisBaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("fiscal_year_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Identifiant exercice invalide."),
  body("annee")
    .optional({ values: "falsy" })
    .isInt({ min: 2000, max: 2100 })
    .withMessage("L'année de déclaration est invalide."),
  body("nb_salaries")
    .optional({ values: "falsy" })
    .isInt({ min: 0 })
    .withMessage("Le nombre de salariés est invalide."),
  body("is_nouveau_contribuable")
    .optional()
    .isBoolean()
    .withMessage("Le statut nouveau contribuable est invalide."),
  body("is_marge_reglementee")
    .optional()
    .isBoolean()
    .withMessage("Le statut marge réglementée est invalide."),
  body("exoneration_flags")
    .optional()
    .isArray()
    .withMessage("Les exonérations doivent être transmises sous forme de liste."),
  body([
    "periode_du",
    "periode_au",
    "date_debut_activite",
    "quittance_date",
    "date_signature",
    "date_depot",
  ])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date GN°12 Bis est invalide."),
  handleValidation,
];

const g50terBaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant societe invalide."),
  body("fiscal_year_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Identifiant exercice invalide."),
  body("annee")
    .optional({ values: "falsy" })
    .isInt({ min: 2000, max: 2100 })
    .withMessage("L'annee de declaration est invalide."),
  body("trimestre")
    .optional({ values: "falsy" })
    .isInt({ min: 1, max: 4 })
    .withMessage("Le trimestre doit etre compris entre 1 et 4."),
  body([
    "date_versement",
    "delai_limite",
    "quittance_date",
    "date_signature",
    "receveur_date",
  ])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date G50ter est invalide."),
  handleValidation,
];

const g50BaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant societe invalide."),
  body("annee")
    .optional({ values: "falsy" })
    .isInt({ min: 2000, max: 2100 })
    .withMessage("L'annee de declaration est invalide."),
  body("periodicite")
    .optional({ values: "falsy" })
    .isIn(["mensuel", "trimestriel"])
    .withMessage("La periodicite G50 est invalide."),
  body("mois")
    .optional({ values: "falsy" })
    .isInt({ min: 1, max: 12 })
    .withMessage("Le mois doit etre compris entre 1 et 12."),
  body("trimestre")
    .optional({ values: "falsy" })
    .isInt({ min: 1, max: 4 })
    .withMessage("Le trimestre doit etre compris entre 1 et 4."),
  body(["quittance_date", "date_signature"])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date G50 est invalide."),
  handleValidation,
];

const penaltyBaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant societe invalide."),
  body("declaration_type")
    .notEmpty()
    .isIn(["GN12", "GN12BIS", "G50TER", "G50"])
    .withMessage("Le type de declaration est invalide."),
  body("declaration_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Identifiant de declaration invalide."),
  body("droits_dus")
    .optional({ values: "falsy" })
    .isFloat({ min: 0 })
    .withMessage("Les droits dus sont invalides."),
  body(["date_limite", "date_depot", "date_mise_en_demeure"])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date de penalite est invalide."),
  body(["mise_en_demeure", "is_neant"])
    .optional()
    .isBoolean()
    .withMessage("Un indicateur de penalite est invalide."),
  body("nb_obligations")
    .optional({ values: "falsy" })
    .isInt({ min: 1, max: 50 })
    .withMessage("Le nombre d'obligations est invalide."),
  handleValidation,
];

const companyYearQueryValidation = [
  query("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant societe invalide."),
  query("annee")
    .optional({ values: "falsy" })
    .isInt({ min: 2000, max: 2100 })
    .withMessage("L'annee est invalide."),
  handleValidation,
];

const taxCodeBaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("code")
    .optional()
    .isString()
    .isLength({ min: 2, max: 10 })
    .withMessage("Le code fiscal doit contenir entre 2 et 10 caractères."),
  body("label")
    .optional()
    .isString()
    .isLength({ min: 1, max: 500 })
    .withMessage("Le libellé du code fiscal est invalide."),
  body("cadre")
    .optional({ values: "falsy" })
    .isInt({ min: 1, max: 8 })
    .withMessage("Le cadre fiscal est invalide."),
  body("sub_cadre")
    .optional({ values: "falsy" })
    .isIn(["A", "B", "C", "a", "b", "c"])
    .withMessage("Le sous-cadre fiscal est invalide."),
  body("tax_category")
    .optional({ values: "falsy" })
    .isIn(["TVA", "IRG", "IBS", "TLS", "TIMBRE", "AUTRE"])
    .withMessage("La catégorie fiscale est invalide."),
  body("rate_type")
    .optional({ values: "falsy" })
    .isIn(["fixed", "bareme", "variable"])
    .withMessage("Le type de taux fiscal est invalide."),
  body("rate")
    .optional({ values: "falsy" })
    .isFloat({ min: 0 })
    .withMessage("Le taux fiscal est invalide."),
  body("sort_order")
    .optional({ values: "falsy" })
    .isInt({ min: 0 })
    .withMessage("L'ordre d'affichage est invalide."),
  body(["is_deduction", "is_active"])
    .optional()
    .isBoolean()
    .withMessage("Un indicateur du code fiscal est invalide."),
  handleValidation,
];

const fiscalRuleBaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("tax_code_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Le code G50 cible est invalide."),
  body("rule_name")
    .optional()
    .isString()
    .isLength({ min: 1, max: 200 })
    .withMessage("Le nom de la règle est invalide."),
  body("rule_type")
    .optional({ values: "falsy" })
    .isIn([
      "account_range",
      "account_exact",
      "journal_type",
      "entry_label_pattern",
      "auxiliary_type",
    ])
    .withMessage("Le type de règle fiscale est invalide."),
  body("amount_side")
    .optional({ values: "falsy" })
    .isIn(["debit", "credit", "net", "absolute"])
    .withMessage("Le côté du montant est invalide."),
  body("sign")
    .optional({ values: "falsy" })
    .custom((value) => {
      const numericValue = Number(value);
      return numericValue === -1 || numericValue === 1;
    })
    .withMessage("Le signe fiscal est invalide."),
  body(["effective_from", "effective_to", "period_from", "period_to"])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date de règle fiscale est invalide."),
  body("auxiliary_type")
    .optional({ values: "falsy" })
    .isIn(["client", "supplier"])
    .withMessage("Le type de tiers est invalide."),
  body(["is_active"])
    .optional()
    .isBoolean()
    .withMessage("Le statut de la règle est invalide."),
  body(["account_code_exact_list", "journal_type_list"])
    .optional()
    .isArray()
    .withMessage("Une liste de règle fiscale est invalide."),
  handleValidation,
];

const fiscalAccountMappingBaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("account_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Le compte comptable est invalide."),
  body("tax_code_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Le code G50 est invalide."),
  body("amount_side")
    .optional({ values: "falsy" })
    .isIn(["debit", "credit", "net", "absolute"])
    .withMessage("Le côté du montant est invalide."),
  body("sign")
    .optional({ values: "falsy" })
    .custom((value) => {
      const numericValue = Number(value);
      return numericValue === -1 || numericValue === 1;
    })
    .withMessage("Le signe fiscal est invalide."),
  body(["effective_from", "effective_to"])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date de mapping de compte est invalide."),
  body("is_active")
    .optional()
    .isBoolean()
    .withMessage("Le statut du mapping de compte est invalide."),
  handleValidation,
];

const fiscalJournalMappingBaseValidation = [
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("journal_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Le journal est invalide."),
  body("tax_code_id")
    .optional({ values: "falsy" })
    .isInt({ min: 1 })
    .withMessage("Le code G50 est invalide."),
  body("mapping_type")
    .optional({ values: "falsy" })
    .isIn(["include", "exclude"])
    .withMessage("Le type de mapping de journal est invalide."),
  body("amount_side")
    .optional({ values: "falsy" })
    .isIn(["debit", "credit", "net"])
    .withMessage("Le côté du montant est invalide."),
  body("sign")
    .optional({ values: "falsy" })
    .custom((value) => {
      const numericValue = Number(value);
      return numericValue === -1 || numericValue === 1;
    })
    .withMessage("Le signe fiscal est invalide."),
  body(["effective_from", "effective_to"])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date de mapping de journal est invalide."),
  body("is_active")
    .optional()
    .isBoolean()
    .withMessage("Le statut du mapping de journal est invalide."),
  handleValidation,
];

const fiscalG50EnginePeriodValidation = [
  query("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  query(["period_from", "period_to"])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date de période G50 est invalide."),
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body(["period_from", "period_to"])
    .optional({ values: "falsy" })
    .isISO8601()
    .withMessage("Une date de période G50 est invalide."),
  handleValidation,
];

const fiscalG50EngineHeaderValidation = [
  param("id")
    .isInt({ min: 1 })
    .withMessage("Identifiant de brouillon G50 invalide."),
  query("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  handleValidation,
];

const fiscalG50EngineLineValidation = [
  param("id")
    .isInt({ min: 1 })
    .withMessage("Identifiant de brouillon G50 invalide."),
  param("line_id")
    .isInt({ min: 1 })
    .withMessage("Identifiant de ligne G50 invalide."),
  query("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  body("company_id")
    .optional()
    .isInt({ min: 1 })
    .withMessage("Identifiant société invalide."),
  handleValidation,
];

const fiscalG50EngineAdjustmentValidation = [
  ...fiscalG50EngineLineValidation.slice(0, -1),
  body("new_amount")
    .optional({ values: "falsy" })
    .isFloat({ min: 0 })
    .withMessage("Le nouveau montant est invalide."),
  body("reason_type")
    .optional({ values: "falsy" })
    .isIn([
      "facturation_error",
      "missing_entry",
      "rate_correction",
      "credit_note_missing",
      "other",
    ])
    .withMessage("Le type de correction est invalide."),
  body("justification")
    .optional({ values: "falsy" })
    .isString()
    .isLength({ min: 20 })
    .withMessage("La justification doit contenir au moins 20 caractères."),
  body("supporting_document_url")
    .optional({ values: "falsy" })
    .isString()
    .isLength({ max: 500 })
    .withMessage("Le document justificatif est invalide."),
  handleValidation,
];

router.get(
  "/dashboard",
  companyYearQueryValidation,
  asyncHandler(fiscalDashboardController.getDashboard),
);

router.get(
  "/compliance-rate",
  companyYearQueryValidation,
  asyncHandler(fiscalDashboardController.getComplianceRate),
);

router.get(
  "/ifu-annual-report",
  companyYearQueryValidation,
  asyncHandler(fiscalDashboardController.getIfuAnnualReport),
);

router.get(
  "/ifu-annual-report/pdf",
  companyYearQueryValidation,
  asyncHandler(fiscalDashboardController.exportIfuAnnualReportPdf),
);

router.get(
  "/charts/monthly-payments",
  companyYearQueryValidation,
  asyncHandler(fiscalDashboardController.getMonthlyPaymentsChart),
);

router.get(
  "/charts/tax-breakdown",
  companyYearQueryValidation,
  asyncHandler(fiscalDashboardController.getTaxBreakdownChart),
);

router.get(
  "/calendar",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'annee de calendrier est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalCalendarController.getCalendar),
);

router.post(
  "/calendar/generate-deadlines",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("fiscal_year_id")
      .optional({ values: "falsy" })
      .isInt({ min: 1 })
      .withMessage("Identifiant exercice invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    body("fiscal_year_id")
      .optional({ values: "falsy" })
      .isInt({ min: 1 })
      .withMessage("Identifiant exercice invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalCalendarController.generateDeadlines),
);

router.get(
  "/annual-summary",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'annee du recapitulatif est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalCalendarController.getAnnualSummary),
);

router.get(
  "/annual-summary/export/pdf",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'annee du recapitulatif est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalCalendarController.exportAnnualSummaryPdf),
);

router.get(
  "/annual-summary/export/excel",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'annee du recapitulatif est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalCalendarController.exportAnnualSummaryExcel),
);

router.post(
  "/penalties/calculate",
  penaltyBaseValidation,
  asyncHandler(fiscalPenaltyController.calculate),
);

router.post(
  "/penalties/export-pdf",
  penaltyBaseValidation,
  asyncHandler(fiscalPenaltyController.exportPdf),
);

router.get(
  "/entries-link",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'annee est invalide."),
    query("declaration_type")
      .optional({ values: "falsy" })
      .isIn(["GN12", "GN12BIS", "G50TER", "G50"])
      .withMessage("Le type de declaration est invalide."),
    query("declaration_id")
      .optional({ values: "falsy" })
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalPenaltyController.listEntriesLink),
);

router.post(
  "/declarations/:id/generate-entries",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    body("declaration_type")
      .isIn(["GN12", "GN12BIS", "G50TER", "G50"])
      .withMessage("Le type de declaration est invalide."),
    body("bank_account_id")
      .isInt({ min: 1 })
      .withMessage("Le compte bancaire est obligatoire."),
    body("preview")
      .optional()
      .isBoolean()
      .withMessage("Le mode apercu est invalide."),
    body(["penalty_amount"])
      .optional({ values: "falsy" })
      .isFloat({ min: 0 })
      .withMessage("Le montant de penalite est invalide."),
    body(["penalty_date"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("La date de penalite est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalPenaltyController.generateEntries),
);

router.get(
  "/profile",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalController.getProfile),
);

router.post(
  "/profile",
  saveProfileValidation,
  asyncHandler(fiscalController.saveProfile),
);

router.put(
  "/profile",
  saveProfileValidation,
  asyncHandler(fiscalController.saveProfile),
);

router.get(
  "/activity-codes",
  [
    query("search")
      .optional()
      .isString()
      .isLength({ max: 120 })
      .withMessage("La recherche du code activité est invalide."),
    query("limit")
      .optional()
      .isInt({ min: 1, max: 25 })
      .withMessage("La limite de recherche est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalController.searchActivityCodes),
);

router.get(
  "/tax-codes",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query("search")
      .optional({ values: "falsy" })
      .isString()
      .isLength({ max: 120 })
      .withMessage("La recherche de codes fiscaux est invalide."),
    query("cadre")
      .optional({ values: "falsy" })
      .isInt({ min: 1, max: 8 })
      .withMessage("Le cadre fiscal est invalide."),
    query("category")
      .optional({ values: "falsy" })
      .isIn(["TVA", "IRG", "IBS", "TLS", "TIMBRE", "AUTRE"])
      .withMessage("La catégorie fiscale est invalide."),
    query("active")
      .optional({ values: "falsy" })
      .isBoolean()
      .withMessage("Le filtre d'activité est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalTaxCodesController.list),
);

router.post(
  "/tax-codes",
  taxCodeBaseValidation,
  asyncHandler(fiscalTaxCodesController.create),
);

router.put(
  "/tax-codes/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant du code fiscal invalide."),
    ...taxCodeBaseValidation,
  ],
  asyncHandler(fiscalTaxCodesController.update),
);

router.delete(
  "/tax-codes/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant du code fiscal invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalTaxCodesController.remove),
);

router.post(
  "/tax-codes/reset-official",
  [
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    body("mode")
      .optional({ values: "falsy" })
      .isIn(["import", "reset"])
      .withMessage("Le mode de synchronisation est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalTaxCodesController.resetOfficial),
);

router.get(
  "/rules",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query("tax_code_id")
      .optional({ values: "falsy" })
      .isInt({ min: 1 })
      .withMessage("Le code G50 cible est invalide."),
    query("status")
      .optional({ values: "falsy" })
      .isIn(["active", "expired", "future", "all"])
      .withMessage("Le statut de règle est invalide."),
    query("rule_type")
      .optional({ values: "falsy" })
      .isIn([
        "account_range",
        "account_exact",
        "journal_type",
        "entry_label_pattern",
        "auxiliary_type",
      ])
      .withMessage("Le type de règle fiscale est invalide."),
    query(["date", "effective_from", "effective_to"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("Une date de filtre est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalRulesController.list),
);

router.get(
  "/rules/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de règle invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalRulesController.getById),
);

router.post(
  "/rules",
  fiscalRuleBaseValidation,
  asyncHandler(fiscalRulesController.create),
);

router.put(
  "/rules/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de règle invalide."),
    ...fiscalRuleBaseValidation,
  ],
  asyncHandler(fiscalRulesController.update),
);

router.delete(
  "/rules/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de règle invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalRulesController.remove),
);

router.post(
  "/rules/test-preview",
  fiscalRuleBaseValidation,
  asyncHandler(fiscalRulesController.testPreview),
);

router.post(
  "/rules/:id/new-version",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de règle invalide."),
    ...fiscalRuleBaseValidation,
  ],
  asyncHandler(fiscalRulesController.createNewVersion),
);

router.post(
  "/rules/:id/test",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de règle invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query(["period_from", "period_to"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("Une date de test est invalide."),
    body(["period_from", "period_to"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("Une date de test est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalRulesController.test),
);

router.get(
  "/rules/:id/history",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de règle invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalRulesController.history),
);

router.get(
  "/account-mapping",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query("account_id")
      .optional({ values: "falsy" })
      .isInt({ min: 1 })
      .withMessage("Le compte comptable est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalMappingController.listAccountMappings),
);

router.post(
  "/account-mapping",
  fiscalAccountMappingBaseValidation,
  asyncHandler(fiscalMappingController.createAccountMapping),
);

router.put(
  "/account-mapping/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de mapping invalide."),
    ...fiscalAccountMappingBaseValidation,
  ],
  asyncHandler(fiscalMappingController.updateAccountMapping),
);

router.delete(
  "/account-mapping/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de mapping invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalMappingController.deleteAccountMapping),
);

router.get(
  "/account-mapping/coverage",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query(["period_from", "period_to"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("Une date de couverture est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalMappingController.getAccountMappingCoverage),
);

router.get(
  "/account-mapping/unmapped",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query(["period_from", "period_to"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("Une date de couverture est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalMappingController.getUnmappedAccounts),
);

router.get(
  "/journal-mapping",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalMappingController.listJournalMappings),
);

router.post(
  "/journal-mapping",
  fiscalJournalMappingBaseValidation,
  asyncHandler(fiscalMappingController.createJournalMapping),
);

router.put(
  "/journal-mapping/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de mapping invalide."),
    ...fiscalJournalMappingBaseValidation,
  ],
  asyncHandler(fiscalMappingController.updateJournalMapping),
);

router.delete(
  "/journal-mapping/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de mapping invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalMappingController.deleteJournalMapping),
);

router.post(
  "/journal-mapping/auto-propose",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalMappingController.autoProposeJournalMappings),
);

router.post(
  "/g50-engine/preflight",
  fiscalG50EnginePeriodValidation,
  asyncHandler(fiscalG50EngineController.preflight),
);

router.post(
  "/g50-engine/calculate",
  fiscalG50EnginePeriodValidation,
  asyncHandler(fiscalG50EngineController.calculate),
);

router.get(
  "/g50-engine/status/:g50_header_id",
  [
    param("g50_header_id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de brouillon G50 invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50EngineController.status),
);

router.get(
  "/g50-engine/draft/:id",
  fiscalG50EngineHeaderValidation,
  asyncHandler(fiscalG50EngineController.getDraft),
);

router.get(
  "/g50-engine/draft/:id/line/:line_id/entries",
  fiscalG50EngineLineValidation,
  asyncHandler(fiscalG50EngineController.getLineEntries),
);

router.put(
  "/g50-engine/draft/:id/line/:line_id/adjust",
  fiscalG50EngineAdjustmentValidation,
  asyncHandler(fiscalG50EngineController.adjustLine),
);

router.delete(
  "/g50-engine/draft/:id/line/:line_id/adjustment",
  fiscalG50EngineLineValidation,
  asyncHandler(fiscalG50EngineController.revertAdjustment),
);

router.get(
  "/g50-engine/draft/:id/control-checks",
  fiscalG50EngineHeaderValidation,
  asyncHandler(fiscalG50EngineController.getControlChecks),
);

router.post(
  "/g50-engine/draft/:id/validate",
  [
    ...fiscalG50EngineHeaderValidation.slice(0, -1),
    body("confirmation_text")
      .optional({ values: "falsy" })
      .isString()
      .withMessage("La confirmation de validation est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50EngineController.validateDraft),
);

router.post(
  "/g50-engine/draft/:id/reopen",
  [
    ...fiscalG50EngineHeaderValidation.slice(0, -1),
    body("justification")
      .optional({ values: "falsy" })
      .isString()
      .isLength({ min: 12 })
      .withMessage("La justification de réouverture est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50EngineController.reopenDraft),
);

router.post(
  "/g50-engine/draft/:id/recalculate",
  fiscalG50EngineHeaderValidation,
  asyncHandler(fiscalG50EngineController.recalculateDraft),
);

router.get(
  "/g50-engine/draft/:id/pdf",
  fiscalG50EngineHeaderValidation,
  asyncHandler(fiscalG50EngineController.exportDraftPdf),
);

router.get(
  "/g50-engine/corrections",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query(["period_from", "period_to"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("Une date de période G50 est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50EngineController.listCorrections),
);

router.get(
  "/g50-engine/history",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'année est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50EngineController.listHistory),
);

router.get(
  "/g50",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'annee de declaration est invalide."),
    query("periodicite")
      .optional({ values: "falsy" })
      .isIn(["mensuel", "trimestriel"])
      .withMessage("La periodicite G50 est invalide."),
    query("mois")
      .optional({ values: "falsy" })
      .isInt({ min: 1, max: 12 })
      .withMessage("Le mois est invalide."),
    query("trimestre")
      .optional({ values: "falsy" })
      .isInt({ min: 1, max: 4 })
      .withMessage("Le trimestre est invalide."),
    query("statut")
      .optional({ values: "falsy" })
      .isIn(["brouillon", "deposee", "validee"])
      .withMessage("Le statut G50 est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50Controller.list),
);

router.get(
  "/g50/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50Controller.getById),
);

router.post(
  "/g50",
  g50BaseValidation,
  asyncHandler(fiscalG50Controller.create),
);

router.put(
  "/g50/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    ...g50BaseValidation,
  ],
  asyncHandler(fiscalG50Controller.update),
);

router.post(
  "/g50/:id/submit",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    body("quittance_date")
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("La date de quittance est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50Controller.submit),
);

router.get(
  "/g50/:id/pdf",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50Controller.pdf),
);

router.post(
  "/g50/:id/coherence-check",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50Controller.coherenceCheck),
);

router.get(
  "/g50/:id/precompte-reporter",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50Controller.getPreviousPrecompte),
);

router.get(
  "/g50ter",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'annee de declaration est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50terController.list),
);

router.get(
  "/g50ter/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50terController.getById),
);

router.post(
  "/g50ter",
  g50terBaseValidation,
  asyncHandler(fiscalG50terController.create),
);

router.put(
  "/g50ter/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    ...g50terBaseValidation,
  ],
  asyncHandler(fiscalG50terController.update),
);

router.post(
  "/g50ter/:id/submit",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    body(["date_versement", "quittance_date"])
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("Une date G50ter est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50terController.submit),
);

router.get(
  "/g50ter/:id/pdf",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de declaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant societe invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50terController.pdf),
);

router.post(
  "/irg-bareme-calculator",
  [
    body("salaire_brut")
      .isFloat({ min: 0 })
      .withMessage("Le salaire brut doit etre positif ou nul."),
    body("nb_mois")
      .optional({ values: "falsy" })
      .isInt({ min: 1, max: 12 })
      .withMessage("Le nombre de mois est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalG50terController.irgBaremeCalculator),
);

router.get(
  "/gn12",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query("fiscal_year_id")
      .optional({ values: "falsy" })
      .isInt({ min: 1 })
      .withMessage("Identifiant exercice invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12Controller.list),
);

router.get(
  "/gn12/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12Controller.getById),
);

router.post(
  "/gn12",
  gn12BaseValidation,
  asyncHandler(fiscalGn12Controller.create),
);

router.put(
  "/gn12/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    ...gn12BaseValidation,
  ],
  asyncHandler(fiscalGn12Controller.update),
);

router.post(
  "/gn12/:id/submit",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    body("date_depot")
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("La date de dépôt est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12Controller.submit),
);

router.post(
  "/gn12/:id/validate",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12Controller.validate),
);

router.get(
  "/gn12/:id/pdf",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12Controller.pdf),
);

router.get(
  "/gn12bis",
  [
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    query("annee")
      .optional({ values: "falsy" })
      .isInt({ min: 2000, max: 2100 })
      .withMessage("L'année de déclaration est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12BisController.list),
);

router.get(
  "/gn12bis/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12BisController.getById),
);

router.post(
  "/gn12bis",
  gn12BisBaseValidation,
  asyncHandler(fiscalGn12BisController.create),
);

router.put(
  "/gn12bis/:id",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    ...gn12BisBaseValidation,
  ],
  asyncHandler(fiscalGn12BisController.update),
);

router.post(
  "/gn12bis/:id/import-from-gn12",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12BisController.importFromGn12),
);

router.post(
  "/gn12bis/:id/import-from-g50ter",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12BisController.importFromG50ter),
);

router.post(
  "/gn12bis/:id/submit",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    body("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    body("date_depot")
      .optional({ values: "falsy" })
      .isISO8601()
      .withMessage("La date de dépôt est invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12BisController.submit),
);

router.get(
  "/gn12bis/:id/pdf",
  [
    param("id")
      .isInt({ min: 1 })
      .withMessage("Identifiant de déclaration invalide."),
    query("company_id")
      .optional()
      .isInt({ min: 1 })
      .withMessage("Identifiant société invalide."),
    handleValidation,
  ],
  asyncHandler(fiscalGn12BisController.pdf),
);

export default router;
