package dev.lions.unionflow.server.service.airms;

import com.lowagie.text.*;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import dev.lions.unionflow.server.security.AuditTrailService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;
import org.jboss.logging.Logger;

/**
 * Génération du <strong>Rapport AIRMS triple</strong> (technique / moral / financier) pour les
 * mutuelles sociales clientes (cf. Règlement UEMOA 07/2009 art. AIRMS « Droits et Devoirs »).
 *
 * <p>Trois rapports obligatoires à produire annuellement et à transmettre à l'AIRMS + adhérents :
 *
 * <ol>
 *   <li><strong>Rapport technique</strong> — effectifs, sinistralité, taux de prise en charge,
 *       statistiques opérationnelles
 *   <li><strong>Rapport moral</strong> — activités, AG tenues, formations, vie associative
 *   <li><strong>Rapport financier</strong> — états financiers SYCEBNL/SYSCOHADA, ratios
 *       prudentiels, comptabilité certifiée
 * </ol>
 *
 * <p>Deadline : <strong>30 juin de chaque année</strong> (AG ordinaire + dépôt AIRMS).
 *
 * @since 2026-04-25 (P1-NEW-1)
 */
@ApplicationScoped
public class RapportAirmsService {

  private static final Logger LOG = Logger.getLogger(RapportAirmsService.class);
  private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("dd/MM/yyyy");

  @Inject AuditTrailService auditTrail;

  /** Génère le rapport AIRMS triple complet (3 sections dans un même PDF). */
  @Transactional
  public byte[] genererRapportTriple(RapportAirmsData data) throws IOException {
    if (data == null) throw new IllegalArgumentException("RapportAirmsData ne peut pas être null");

    try (Document document = new Document(PageSize.A4);
         ByteArrayOutputStream out = new ByteArrayOutputStream()) {
      PdfWriter.getInstance(document, out);
      document.open();

      // Page de garde
      addCoverPage(document, data);
      document.newPage();

      // Section 1 — Rapport technique
      addSectionTitle(document, "RAPPORT TECHNIQUE");
      addTechnique(document, data);
      document.newPage();

      // Section 2 — Rapport moral
      addSectionTitle(document, "RAPPORT MORAL");
      addMoral(document, data);
      document.newPage();

      // Section 3 — Rapport financier
      addSectionTitle(document, "RAPPORT FINANCIER");
      addFinancier(document, data);

      document.close();

      auditTrail.logSimple("Organisation", data.organisationId(), "EXPORT",
          "Génération rapport AIRMS triple — exercice " + data.exerciceAnnee());

      return out.toByteArray();
    } catch (DocumentException de) {
      throw new IOException("Erreur génération PDF rapport AIRMS : " + de.getMessage(), de);
    }
  }

  // ============================================================
  // Sections
  // ============================================================

  private void addCoverPage(Document doc, RapportAirmsData data) throws DocumentException {
    Paragraph title = new Paragraph("RAPPORT ANNUEL AIRMS",
        FontFactory.getFont(FontFactory.HELVETICA_BOLD, 22, Color.BLACK));
    title.setAlignment(Element.ALIGN_CENTER);
    title.setSpacingAfter(20);
    doc.add(title);

    Paragraph subtitle = new Paragraph("Triple rapport — Technique, Moral, Financier",
        FontFactory.getFont(FontFactory.HELVETICA, 14, Color.GRAY));
    subtitle.setAlignment(Element.ALIGN_CENTER);
    subtitle.setSpacingAfter(40);
    doc.add(subtitle);

    Paragraph orgName = new Paragraph(data.organisationDenomination(),
        FontFactory.getFont(FontFactory.HELVETICA_BOLD, 16));
    orgName.setAlignment(Element.ALIGN_CENTER);
    doc.add(orgName);

    addText(doc, "Numéro AIRMS : " + nullSafe(data.numeroAgrementAirms()));
    addText(doc, "Exercice : " + data.exerciceAnnee());
    addText(doc, "Présenté à l'Assemblée Générale du : " + data.dateAg().format(DATE_FMT));
    addText(doc, "");
    addText(doc, "Cadre légal : Règlement UEMOA 07/2009 (mutualité sociale) + AUDSCGIE OHADA");
    addText(doc, "Référentiel comptable : " + data.referentielComptable());
  }

  private void addTechnique(Document doc, RapportAirmsData data) throws DocumentException {
    addText(doc, "1.1 — Effectifs au 31/12/" + data.exerciceAnnee());
    addKeyValue(doc, "Membres actifs", data.technique().nombreMembresActifs());
    addKeyValue(doc, "Membres suspendus", data.technique().nombreMembresSuspendus());
    addKeyValue(doc, "Membres radiés", data.technique().nombreMembresRadies());
    addKeyValue(doc, "Nouveaux adhérents", data.technique().nouveauxAdherents());
    addKeyValue(doc, "Démissions volontaires", data.technique().demissions());
    addText(doc, "");

    addText(doc, "1.2 — Couverture santé / risques (le cas échéant)");
    addKeyValue(doc, "Bénéficiaires CMU enrôlés", data.technique().beneficiairesCmu());
    addKeyValue(doc, "Sinistres déclarés", data.technique().sinistresDeclares());
    addKeyValue(doc, "Sinistres pris en charge", data.technique().sinistresPriseEnCharge());
    addText(doc, "Taux de prise en charge : " + data.technique().tauxPriseEnCharge() + "%");
    addText(doc, "");

    addText(doc, "1.3 — Délais moyens de traitement");
    addText(doc, "Délai moyen demande d'aide : " + data.technique().delaiMoyenDemandeAideJours() + " jours");
    addText(doc, "Délai moyen remboursement : " + data.technique().delaiMoyenRemboursementJours() + " jours");
  }

  private void addMoral(Document doc, RapportAirmsData data) throws DocumentException {
    addText(doc, "2.1 — Vie associative");
    addKeyValue(doc, "Nombre d'AG tenues", data.moral().nombreAg());
    addKeyValue(doc, "Nombre de réunions CA", data.moral().nombreReunionsCa());
    addText(doc, "Quorum moyen : " + data.moral().quorumMoyen() + "%");
    addText(doc, "");

    addText(doc, "2.2 — Formations dispensées");
    if (data.moral().formationsDispensees().isEmpty()) {
      addText(doc, "Aucune formation cette année.");
    } else {
      for (String f : data.moral().formationsDispensees()) {
        addText(doc, "• " + f);
      }
    }
    addText(doc, "");

    addText(doc, "2.3 — Activités phares");
    if (data.moral().activitesPhares().isEmpty()) {
      addText(doc, "—");
    } else {
      for (String a : data.moral().activitesPhares()) {
        addText(doc, "• " + a);
      }
    }
  }

  private void addFinancier(Document doc, RapportAirmsData data) throws DocumentException {
    addText(doc, "3.1 — Synthèse des produits et charges (FCFA)");
    PdfPTable t = new PdfPTable(2);
    t.setWidthPercentage(80);
    addRow(t, "Cotisations encaissées", formatFcfa(data.financier().cotisationsEncaissees()));
    addRow(t, "Subventions reçues", formatFcfa(data.financier().subventionsRecues()));
    addRow(t, "Dons reçus", formatFcfa(data.financier().donsRecus()));
    addRow(t, "Total produits", formatFcfa(data.financier().totalProduits()));
    addRow(t, "Charges de fonctionnement", formatFcfa(data.financier().chargesFonctionnement()));
    addRow(t, "Aides versées", formatFcfa(data.financier().aidesVersees()));
    addRow(t, "Total charges", formatFcfa(data.financier().totalCharges()));
    addRow(t, "Excédent / Déficit", formatFcfa(data.financier().excedent()));
    doc.add(t);
    doc.add(new Paragraph(" "));

    addText(doc, "3.2 — Ratios prudentiels (mutuelles + SFD)");
    if (data.financier().ratioSolvabilitePct() != null) {
      addText(doc, "Solvabilité : " + data.financier().ratioSolvabilitePct() + "% (norme BCEAO ≥ 15%)");
    }
    if (data.financier().par30Pct() != null) {
      addText(doc, "PAR30 (Portfolio At Risk) : " + data.financier().par30Pct() + "% (norme < 5%)");
    }
    addText(doc, "");

    addText(doc, "3.3 — Trésorerie au 31/12");
    addText(doc, "Solde compte principal : " + formatFcfa(data.financier().soldeCompte()));
    addText(doc, "Réserves légales : " + formatFcfa(data.financier().reservesLegales()));
    addText(doc, "Fonds dédiés (SYCEBNL) : " + formatFcfa(data.financier().fondsDedies()));
  }

  // ============================================================
  // Helpers PDF
  // ============================================================

  private void addSectionTitle(Document doc, String text) throws DocumentException {
    Paragraph p = new Paragraph(text,
        FontFactory.getFont(FontFactory.HELVETICA_BOLD, 18, Color.BLACK));
    p.setAlignment(Element.ALIGN_LEFT);
    p.setSpacingAfter(15);
    doc.add(p);
  }

  private void addText(Document doc, String text) throws DocumentException {
    if (text == null) text = "";
    doc.add(new Paragraph(text, FontFactory.getFont(FontFactory.HELVETICA, 11)));
  }

  private void addKeyValue(Document doc, String key, Number value) throws DocumentException {
    addText(doc, key + " : " + (value == null ? "0" : value.toString()));
  }

  private void addRow(PdfPTable table, String label, String value) {
    table.addCell(new PdfPCell(new Phrase(label)));
    PdfPCell vcell = new PdfPCell(new Phrase(value));
    vcell.setHorizontalAlignment(Element.ALIGN_RIGHT);
    table.addCell(vcell);
  }

  private static String formatFcfa(BigDecimal value) {
    if (value == null) return "—";
    return String.format("%,.0f FCFA", value).replace(",", " ");
  }

  private static String nullSafe(String s) {
    return s == null ? "—" : s;
  }

  // ============================================================
  // DTOs
  // ============================================================

  public record RapportAirmsData(
      UUID organisationId,
      String organisationDenomination,
      String numeroAgrementAirms,
      int exerciceAnnee,
      LocalDate dateAg,
      String referentielComptable,
      RapportTechnique technique,
      RapportMoral moral,
      RapportFinancier financier
  ) {}

  public record RapportTechnique(
      int nombreMembresActifs,
      int nombreMembresSuspendus,
      int nombreMembresRadies,
      int nouveauxAdherents,
      int demissions,
      int beneficiairesCmu,
      int sinistresDeclares,
      int sinistresPriseEnCharge,
      BigDecimal tauxPriseEnCharge,
      int delaiMoyenDemandeAideJours,
      int delaiMoyenRemboursementJours
  ) {}

  public record RapportMoral(
      int nombreAg,
      int nombreReunionsCa,
      BigDecimal quorumMoyen,
      List<String> formationsDispensees,
      List<String> activitesPhares
  ) {}

  public record RapportFinancier(
      BigDecimal cotisationsEncaissees,
      BigDecimal subventionsRecues,
      BigDecimal donsRecus,
      BigDecimal totalProduits,
      BigDecimal chargesFonctionnement,
      BigDecimal aidesVersees,
      BigDecimal totalCharges,
      BigDecimal excedent,
      BigDecimal ratioSolvabilitePct,
      BigDecimal par30Pct,
      BigDecimal soldeCompte,
      BigDecimal reservesLegales,
      BigDecimal fondsDedies
  ) {}
}
