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

import dev.lions.unionflow.server.security.OrganisationContextHolder;
import dev.lions.unionflow.server.security.AuditTrailService;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
import org.jboss.logging.Logger;

/**
 * Génération de Déclaration d'Opération Suspecte (DOS) au format CENTIF Côte d'Ivoire.
 *
 * <p>Conforme aux <strong>Lignes directrices CENTIF</strong> publiées en mars 2025
 * (<a href="https://www.centif.ci/wp-content/uploads/2025/03/lignesdirectrices.pdf">PDF officiel</a>) :
 *
 * <ul>
 *   <li><strong>Format Word (.docx)</strong> — DOS complète à transmettre à la CENTIF
 *   <li><strong>Format Excel (.xlsx)</strong> — relevé d'opérations atypiques associé
 * </ul>
 *
 * <p>Confidentialité absolue : la DOS ne doit jamais être communiquée au client. Le déclarant
 * est anonymisé envers le procureur. UnionFlow s'assure que :
 *
 * <ul>
 *   <li>Seul le Compliance Officer de l'organisation peut générer la DOS
 *   <li>Toute génération est tracée dans {@code audit_trail_operations} (action_type {@code
 *       EXPORT})
 *   <li>Les fichiers générés ne sont jamais persistés sur disque (streaming download uniquement)
 * </ul>
 *
 * <p>Anticipation : module {@code goAML XML} également prévu (P2-NEW-4) pour quand CI adoptera le
 * format ONUDC standard.
 *
 * @since 2026-04-25 (P0-NEW-16)
 */
@ApplicationScoped
public class DosCentifService {

  private static final Logger LOG = Logger.getLogger(DosCentifService.class);
  private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("dd/MM/yyyy");
  private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");

  @Inject AuditTrailService auditTrail;
  @Inject OrganisationContextHolder context;

  /**
   * Génère la DOS au format Word (.docx). Ne persiste rien sur disque — retourne directement
   * le binaire pour streaming HTTP.
   */
  @Transactional
  public byte[] genererDosWord(DosCentifData data) throws IOException {
    if (data == null) {
      throw new IllegalArgumentException("DosCentifData ne peut pas être null");
    }

    try (XWPFDocument doc = new XWPFDocument();
         ByteArrayOutputStream out = new ByteArrayOutputStream()) {

      // En-tête
      addHeader(doc, "DÉCLARATION D'OPÉRATION SUSPECTE");
      addParagraph(doc, "À l'attention de : CENTIF — Cellule Nationale de Traitement des Informations Financières");
      addParagraph(doc, "République de Côte d'Ivoire");
      addParagraph(doc, "");
      addParagraph(doc, "N° DOS interne : " + data.numeroDosInterne());
      addParagraph(doc, "Date de déclaration : " + data.dateDeclaration().format(DATE_FMT));
      addParagraph(doc, "");

      // Section 1 — Déclarant (anonymisé envers le procureur)
      addSection(doc, "1. INSTITUTION DÉCLARANTE");
      addParagraph(doc, "Dénomination : " + data.institutionDenomination());
      addParagraph(doc, "Nature : " + data.institutionNature());
      addParagraph(doc, "Numéro d'agrément BCEAO : " + nullSafe(data.numeroAgrementBceao()));
      addParagraph(doc, "Adresse : " + nullSafe(data.institutionAdresse()));
      addParagraph(doc, "Ville : " + nullSafe(data.institutionVille()));

      // Section 2 — Personne ou entité déclarée
      addSection(doc, "2. PERSONNE OU ENTITÉ FAISANT L'OBJET DE LA DÉCLARATION");
      addParagraph(doc, "Type : " + data.typeSujet()); // PERSONNE_PHYSIQUE / PERSONNE_MORALE
      addParagraph(doc, "Nom / Raison sociale : " + data.sujetNom());
      addParagraph(doc, "Date de naissance / immatriculation : " +
          (data.sujetDateNaissance() != null ? data.sujetDateNaissance().format(DATE_FMT) : "—"));
      addParagraph(doc, "Nationalité / Pays : " + nullSafe(data.sujetNationalite()));
      addParagraph(doc, "Pièce d'identité : " + nullSafe(data.sujetPieceIdentite()));
      addParagraph(doc, "Profession / Activité : " + nullSafe(data.sujetProfessionActivite()));
      addParagraph(doc, "PEP : " + (data.sujetEstPep() ? "OUI — " + nullSafe(data.sujetPepCategorie()) : "Non"));

      // Section 3 — Opération(s) suspecte(s)
      addSection(doc, "3. DÉTAIL DES OPÉRATIONS SUSPECTES");
      if (data.operations().isEmpty()) {
        addParagraph(doc, "Aucune opération précisée.");
      } else {
        XWPFTable table = doc.createTable();
        XWPFTableRow header = table.getRow(0);
        header.getCell(0).setText("Date");
        header.addNewTableCell().setText("Montant (FCFA)");
        header.addNewTableCell().setText("Devise");
        header.addNewTableCell().setText("Type");
        header.addNewTableCell().setText("Bénéficiaire");
        for (DosOperation op : data.operations()) {
          XWPFTableRow row = table.createRow();
          row.getCell(0).setText(op.date().format(DATE_FMT));
          row.getCell(1).setText(op.montant().toPlainString());
          row.getCell(2).setText(op.devise());
          row.getCell(3).setText(op.typeOperation());
          row.getCell(4).setText(nullSafe(op.beneficiaire()));
        }
      }

      // Section 4 — Motifs du soupçon
      addSection(doc, "4. MOTIFS DU SOUPÇON");
      addParagraph(doc, nullSafe(data.motifsSoupcon()));

      // Section 5 — Mesures conservatoires
      addSection(doc, "5. MESURES CONSERVATOIRES PRISES");
      addParagraph(doc, nullSafe(data.mesuresConservatoires()));

      // Section 6 — Pièces jointes
      addSection(doc, "6. PIÈCES JOINTES");
      if (data.piecesJointes().isEmpty()) {
        addParagraph(doc, "Aucune pièce jointe.");
      } else {
        for (String piece : data.piecesJointes()) {
          addParagraph(doc, "• " + piece);
        }
      }

      // Footer confidentialité
      addParagraph(doc, "");
      addParagraph(doc, "—");
      addParagraph(doc,
          "DÉCLARATION CONFIDENTIELLE — Conformément à la directive UEMOA 02/2015/CM/UEMOA et "
              + "aux Lignes directrices CENTIF (mars 2025), la présente déclaration ne peut être "
              + "communiquée au client. Le déclarant est anonymisé envers le procureur de la République.");

      doc.write(out);
      auditTrail.logSimple("AlerteAml", data.alerteId(), "EXPORT",
          "Génération DOS CENTIF Word — N° " + data.numeroDosInterne());
      return out.toByteArray();
    }
  }

  /** Génère le relevé d'opérations atypiques au format Excel (.xlsx). */
  @Transactional
  public byte[] genererReleveExcel(DosCentifData data) throws IOException {
    if (data == null) {
      throw new IllegalArgumentException("DosCentifData ne peut pas être null");
    }
    try (XSSFWorkbook wb = new XSSFWorkbook();
         ByteArrayOutputStream out = new ByteArrayOutputStream()) {
      Sheet sheet = wb.createSheet("Operations atypiques");

      // Style header
      CellStyle headerStyle = wb.createCellStyle();
      Font headerFont = wb.createFont();
      headerFont.setBold(true);
      headerStyle.setFont(headerFont);

      String[] headers = {"N° DOS", "Date opération", "Montant (FCFA)", "Devise", "Type",
          "Bénéficiaire", "Sujet déclaré", "Motif"};
      Row headerRow = sheet.createRow(0);
      for (int i = 0; i < headers.length; i++) {
        Cell cell = headerRow.createCell(i);
        cell.setCellValue(headers[i]);
        cell.setCellStyle(headerStyle);
      }

      int rowNum = 1;
      for (DosOperation op : data.operations()) {
        Row row = sheet.createRow(rowNum++);
        row.createCell(0).setCellValue(data.numeroDosInterne());
        row.createCell(1).setCellValue(op.date().format(DATE_FMT));
        row.createCell(2).setCellValue(op.montant().doubleValue());
        row.createCell(3).setCellValue(op.devise());
        row.createCell(4).setCellValue(op.typeOperation());
        row.createCell(5).setCellValue(nullSafe(op.beneficiaire()));
        row.createCell(6).setCellValue(data.sujetNom());
        row.createCell(7).setCellValue(nullSafe(data.motifsSoupcon()));
      }

      for (int i = 0; i < headers.length; i++) {
        sheet.autoSizeColumn(i);
      }

      wb.write(out);
      auditTrail.logSimple("AlerteAml", data.alerteId(), "EXPORT",
          "Génération relevé opérations CENTIF Excel — N° " + data.numeroDosInterne());
      return out.toByteArray();
    }
  }

  // Helpers
  private void addHeader(XWPFDocument doc, String text) {
    XWPFParagraph p = doc.createParagraph();
    p.setAlignment(ParagraphAlignment.CENTER);
    XWPFRun run = p.createRun();
    run.setBold(true);
    run.setFontSize(16);
    run.setText(text);
  }

  private void addSection(XWPFDocument doc, String text) {
    XWPFParagraph p = doc.createParagraph();
    p.setSpacingBefore(200);
    XWPFRun run = p.createRun();
    run.setBold(true);
    run.setFontSize(13);
    run.setText(text);
  }

  private void addParagraph(XWPFDocument doc, String text) {
    XWPFParagraph p = doc.createParagraph();
    XWPFRun run = p.createRun();
    run.setText(text);
  }

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

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

  /** Données pour générer une DOS CENTIF (Word + Excel). */
  public record DosCentifData(
      UUID alerteId,
      String numeroDosInterne,
      LocalDateTime dateDeclaration,
      // Institution déclarante
      String institutionDenomination,
      String institutionNature,        // SFD / EME / Banque / EP / IMF
      String numeroAgrementBceao,
      String institutionAdresse,
      String institutionVille,
      // Sujet déclaré
      String typeSujet,                // PERSONNE_PHYSIQUE / PERSONNE_MORALE
      String sujetNom,
      LocalDate sujetDateNaissance,
      String sujetNationalite,
      String sujetPieceIdentite,
      String sujetProfessionActivite,
      boolean sujetEstPep,
      String sujetPepCategorie,
      // Opérations
      List<DosOperation> operations,
      // Motifs
      String motifsSoupcon,
      String mesuresConservatoires,
      List<String> piecesJointes
  ) {}

  /** Détail d'une opération suspecte. */
  public record DosOperation(
      LocalDate date,
      BigDecimal montant,
      String devise,
      String typeOperation,            // VIREMENT, DEPOT, RETRAIT, COTISATION, AUTRE
      String beneficiaire
  ) {}
}
