package compliance

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/go-pdf/fpdf"
)

// ─── colour palette ───────────────────────────────────────────────────────────

var (
	colNavy   = [3]int{30, 58, 95}
	colBlack  = [3]int{30, 30, 30}
	colGray   = [3]int{100, 100, 100}
	colLightBg = [3]int{245, 247, 250}
	colRed    = [3]int{220, 38, 38}
	colOrange = [3]int{234, 88, 12}
	colAmber  = [3]int{180, 110, 10}
	colGreen  = [3]int{21, 128, 61}
)

func riskColor(risk string) [3]int {
	switch risk {
	case "forbidden":
		return colRed
	case "high":
		return colOrange
	case "limited":
		return colAmber
	case "minimal":
		return colGreen
	default:
		return colGray
	}
}

// ─── helpers ─────────────────────────────────────────────────────────────────

func newPDF() *fpdf.Fpdf {
	pdf := fpdf.New("P", "mm", "A4", "")
	pdf.SetMargins(20, 20, 20)
	pdf.SetAutoPageBreak(true, 20)
	return pdf
}

func setFont(pdf *fpdf.Fpdf, style string, size float64, col [3]int) {
	pdf.SetFont("Helvetica", style, size)
	pdf.SetTextColor(col[0], col[1], col[2])
}

func sectionHeader(pdf *fpdf.Fpdf, title string) {
	pdf.Ln(6)
	pdf.SetFillColor(colNavy[0], colNavy[1], colNavy[2])
	pdf.SetTextColor(255, 255, 255)
	pdf.SetFont("Helvetica", "B", 10)
	pdf.CellFormat(0, 8, "  "+title, "", 1, "L", true, 0, "")
	pdf.SetTextColor(colBlack[0], colBlack[1], colBlack[2])
	pdf.Ln(2)
}

func labelValue(pdf *fpdf.Fpdf, label, value string) {
	if value == "" {
		value = "—"
	}
	setFont(pdf, "B", 9, colGray)
	pdf.CellFormat(55, 6, label+":", "", 0, "L", false, 0, "")
	setFont(pdf, "", 9, colBlack)
	pdf.MultiCell(0, 6, value, "", "L", false)
}

func tableRow(pdf *fpdf.Fpdf, cols []string, widths []float64, fill bool) {
	if fill {
		pdf.SetFillColor(colLightBg[0], colLightBg[1], colLightBg[2])
	} else {
		pdf.SetFillColor(255, 255, 255)
	}
	for i, col := range cols {
		pdf.CellFormat(widths[i], 6, col, "1", 0, "L", fill, 0, "")
	}
	pdf.Ln(-1)
}

func footer(pdf *fpdf.Fpdf) {
	pdf.SetFooterFunc(func() {
		pdf.SetY(-15)
		setFont(pdf, "I", 8, colGray)
		pdf.CellFormat(0, 10,
			fmt.Sprintf("Généré par Veylant IA · %s · Page %d/{nb}",
				time.Now().Format("02/01/2006"),
				pdf.PageNo(),
			),
			"", 0, "C", false, 0, "")
	})
	pdf.AliasNbPages("{nb}")
}

func covePage(pdf *fpdf.Fpdf, title, subtitle, tenantName string) {
	pdf.AddPage()
	pdf.Ln(30)

	// Title block
	pdf.SetFillColor(colNavy[0], colNavy[1], colNavy[2])
	pdf.SetTextColor(255, 255, 255)
	pdf.SetFont("Helvetica", "B", 22)
	pdf.CellFormat(0, 18, title, "", 1, "C", true, 0, "")
	pdf.SetFont("Helvetica", "", 13)
	pdf.CellFormat(0, 10, subtitle, "", 1, "C", true, 0, "")
	pdf.Ln(6)

	// Tenant + date
	pdf.SetTextColor(colBlack[0], colBlack[1], colBlack[2])
	pdf.SetFont("Helvetica", "", 11)
	pdf.CellFormat(0, 8, "Organisation : "+tenantName, "", 1, "C", false, 0, "")
	pdf.CellFormat(0, 8, "Date de génération : "+time.Now().Format("02 janvier 2006 à 15:04"), "", 1, "C", false, 0, "")
	pdf.Ln(10)

	// Confidential stamp
	pdf.SetFont("Helvetica", "B", 14)
	pdf.SetTextColor(colRed[0], colRed[1], colRed[2])
	pdf.CellFormat(0, 10, "⚠  DOCUMENT CONFIDENTIEL", "", 1, "C", false, 0, "")
}

// ─── GenerateArticle30 ────────────────────────────────────────────────────────

// GenerateArticle30 generates a GDPR Article 30 processing registry PDF.
func GenerateArticle30(entries []ProcessingEntry, tenantName string, w io.Writer) error {
	if tenantName == "" {
		tenantName = "Organisation"
	}

	pdf := newPDF()
	footer(pdf)
	covePage(pdf, "Registre des Activités de Traitement",
		"Conformément à l'Article 30 du Règlement (UE) 2016/679 (RGPD)", tenantName)

	// Section 1 — Responsable de traitement
	pdf.AddPage()
	sectionHeader(pdf, "1. Identification du Responsable de Traitement")
	pdf.Ln(2)
	labelValue(pdf, "Organisation", tenantName)
	labelValue(pdf, "Plateforme IA", "Veylant IA — Proxy IA multi-fournisseurs")
	labelValue(pdf, "DPO / Contact", "dpo@"+strings.ToLower(strings.ReplaceAll(tenantName, " ", ""))+".fr")
	labelValue(pdf, "Cadre réglementaire", "RGPD (UE) 2016/679, Loi Informatique et Libertés")

	// Section 2 — Tableau des traitements
	sectionHeader(pdf, "2. Activités de Traitement")
	pdf.Ln(2)

	if len(entries) == 0 {
		setFont(pdf, "I", 9, colGray)
		pdf.CellFormat(0, 8, "Aucun traitement enregistré.", "", 1, "L", false, 0, "")
	} else {
		widths := []float64{55, 40, 30, 40}
		headers := []string{"Cas d'usage", "Finalité", "Base légale", "Catégories de données"}
		setFont(pdf, "B", 9, colBlack)
		tableRow(pdf, headers, widths, true)

		for i, e := range entries {
			cats := strings.Join(e.DataCategories, ", ")
			if len(cats) > 35 {
				cats = cats[:32] + "..."
			}
			purpose := e.Purpose
			if len(purpose) > 38 {
				purpose = purpose[:35] + "..."
			}
			legalLabel := LegalBasisLabels[e.LegalBasis]
			if legalLabel == "" {
				legalLabel = e.LegalBasis
			}
			setFont(pdf, "", 8, colBlack)
			tableRow(pdf, []string{e.UseCaseName, purpose, legalLabel, cats}, widths, i%2 == 0)
		}
	}

	// Section 3 — Sous-traitants
	sectionHeader(pdf, "3. Destinataires et Sous-Traitants (Fournisseurs LLM)")
	pdf.Ln(2)

	allProcessors := map[string]bool{}
	for _, e := range entries {
		for _, p := range e.Processors {
			allProcessors[p] = true
		}
		for _, r := range e.Recipients {
			allProcessors[r] = true
		}
	}
	if len(allProcessors) == 0 {
		allProcessors["OpenAI (GPT-4o)"] = true
		allProcessors["Anthropic (Claude)"] = true
	}
	for proc := range allProcessors {
		setFont(pdf, "", 9, colBlack)
		pdf.CellFormat(5, 6, "•", "", 0, "L", false, 0, "")
		pdf.CellFormat(0, 6, proc+" — fournisseur LLM (sous-traitant au sens de l'Art. 28 RGPD)", "", 1, "L", false, 0, "")
	}

	// Section 4 — Durées de conservation
	sectionHeader(pdf, "4. Durées de Conservation")
	pdf.Ln(2)
	if len(entries) > 0 {
		widths := []float64{85, 80}
		headers := []string{"Cas d'usage", "Durée de conservation"}
		setFont(pdf, "B", 9, colBlack)
		tableRow(pdf, headers, widths, true)
		for i, e := range entries {
			setFont(pdf, "", 8, colBlack)
			tableRow(pdf, []string{e.UseCaseName, e.RetentionPeriod}, widths, i%2 == 0)
		}
	}
	pdf.Ln(3)
	setFont(pdf, "I", 8, colGray)
	pdf.MultiCell(0, 5,
		"Architecture Veylant IA : journaux chauds 90 jours (ClickHouse), archives tièdes 1 an, archives froides 5 ans (TTL automatique).",
		"", "L", false)

	// Section 5 — Mesures de sécurité
	sectionHeader(pdf, "5. Mesures de Sécurité Techniques et Organisationnelles")
	pdf.Ln(2)
	measures := []string{
		"Chiffrement AES-256-GCM des prompts avant stockage",
		"Pseudonymisation automatique des données personnelles (PII) avant transmission aux LLM",
		"Contrôle d'accès RBAC (Admin, Manager, Utilisateur, Auditeur)",
		"Authentification forte via Keycloak (OIDC/SAML 2.0 / MFA)",
		"Journaux d'audit immuables (ClickHouse append-only, TTL uniquement)",
		"TLS 1.3 pour toutes les communications externes",
		"Circuit breaker pour la résilience des fournisseurs",
		"Séparation logique multi-locataires (Row-Level Security PostgreSQL)",
	}
	for _, m := range measures {
		setFont(pdf, "", 9, colBlack)
		pdf.CellFormat(5, 6, "✓", "", 0, "L", false, 0, "")
		pdf.MultiCell(0, 6, m, "", "L", false)
	}

	// Section 6 — Droits des personnes
	sectionHeader(pdf, "6. Droits des Personnes Concernées")
	pdf.Ln(2)
	rights := []struct{ art, desc string }{
		{"Art. 15", "Droit d'accès — Endpoint GET /v1/admin/compliance/gdpr/access/{user_id}"},
		{"Art. 16", "Droit de rectification — via l'interface d'administration"},
		{"Art. 17", "Droit à l'effacement — Endpoint DELETE /v1/admin/compliance/gdpr/erase/{user_id}"},
		{"Art. 18", "Droit à la limitation — contact DPO"},
		{"Art. 20", "Droit à la portabilité — export JSON/CSV disponible"},
		{"Art. 21", "Droit d'opposition — contact DPO"},
		{"Art. 22", "Droit à ne pas faire l'objet d'une décision automatisée — supervision humaine obligatoire"},
	}
	widths := []float64{20, 145}
	setFont(pdf, "B", 9, colBlack)
	tableRow(pdf, []string{"Article", "Description"}, widths, true)
	for i, r := range rights {
		setFont(pdf, "", 8, colBlack)
		tableRow(pdf, []string{r.art, r.desc}, widths, i%2 == 0)
	}

	var buf bytes.Buffer
	if err := pdf.Output(&buf); err != nil {
		return fmt.Errorf("pdf output: %w", err)
	}
	_, err := w.Write(buf.Bytes())
	return err
}

// ─── GenerateAiActReport ──────────────────────────────────────────────────────

// GenerateAiActReport generates an EU AI Act risk classification report PDF.
func GenerateAiActReport(entries []ProcessingEntry, tenantName string, w io.Writer) error {
	if tenantName == "" {
		tenantName = "Organisation"
	}

	pdf := newPDF()
	footer(pdf)
	covePage(pdf, "Rapport de Classification AI Act",
		"Conformément au Règlement (UE) 2024/1689 sur l'Intelligence Artificielle", tenantName)

	pdf.AddPage()

	// Summary
	sectionHeader(pdf, "Synthèse de la Classification")
	pdf.Ln(2)

	counts := map[string]int{"forbidden": 0, "high": 0, "limited": 0, "minimal": 0, "": 0}
	for _, e := range entries {
		counts[e.RiskLevel]++
	}
	widths := []float64{50, 30, 85}
	setFont(pdf, "B", 9, colBlack)
	tableRow(pdf, []string{"Niveau de risque", "Nb systèmes", "Obligations réglementaires"}, widths, true)

	obligations := map[string]string{
		"forbidden": "INTERDIT — blocage automatique requis",
		"high":      "DPIA obligatoire · supervision humaine · journalisation renforcée",
		"limited":   "Obligation de transparence (Art. 50) · mention IA requise",
		"minimal":   "Journalisation standard uniquement",
		"":          "Non classifié — questionnaire à compléter",
	}
	riskOrder := []string{"forbidden", "high", "limited", "minimal", ""}
	for i, risk := range riskOrder {
		label := RiskLabels[risk]
		if label == "" {
			label = "Non classifié"
		}
		col := riskColor(risk)
		pdf.SetTextColor(col[0], col[1], col[2])
		pdf.SetFont("Helvetica", "B", 8)
		fill := i%2 == 0
		if fill {
			pdf.SetFillColor(colLightBg[0], colLightBg[1], colLightBg[2])
		} else {
			pdf.SetFillColor(255, 255, 255)
		}
		pdf.CellFormat(widths[0], 6, label, "1", 0, "L", fill, 0, "")
		setFont(pdf, "", 8, colBlack)
		pdf.CellFormat(widths[1], 6, fmt.Sprintf("%d", counts[risk]), "1", 0, "C", fill, 0, "")
		pdf.CellFormat(widths[2], 6, obligations[risk], "1", 1, "L", fill, 0, "")
	}

	// Per-system detail
	if len(entries) > 0 {
		sectionHeader(pdf, "Détail par Système IA")
		pdf.Ln(2)

		for _, e := range entries {
			col := riskColor(e.RiskLevel)
			riskLabel := RiskLabels[e.RiskLevel]
			if riskLabel == "" {
				riskLabel = "Non classifié"
			}

			// System header
			pdf.SetFillColor(colLightBg[0], colLightBg[1], colLightBg[2])
			pdf.SetFont("Helvetica", "B", 10)
			pdf.SetTextColor(colNavy[0], colNavy[1], colNavy[2])
			pdf.CellFormat(0, 8, "  "+e.UseCaseName, "LRT", 1, "L", true, 0, "")

			// Risk badge
			pdf.SetFont("Helvetica", "B", 9)
			pdf.SetTextColor(col[0], col[1], col[2])
			pdf.CellFormat(40, 6, "  Niveau : "+riskLabel, "LB", 0, "L", true, 0, "")
			setFont(pdf, "", 9, colBlack)
			pdf.CellFormat(0, 6, "  Base légale : "+LegalBasisLabels[e.LegalBasis], "RB", 1, "L", true, 0, "")

			// Details
			pdf.Ln(1)
			labelValue(pdf, "Finalité", e.Purpose)
			labelValue(pdf, "Données traitées", strings.Join(e.DataCategories, ", "))
			labelValue(pdf, "Durée conservation", e.RetentionPeriod)
			if len(e.AiActAnswers) > 0 {
				yesItems := []string{}
				for _, q := range AiActQuestions {
					if e.AiActAnswers[q.Key] {
						yesItems = append(yesItems, "• "+q.Label)
					}
				}
				if len(yesItems) > 0 {
					setFont(pdf, "B", 9, colGray)
					pdf.CellFormat(55, 6, "Critères AI Act :", "", 1, "L", false, 0, "")
					setFont(pdf, "", 8, colBlack)
					for _, yi := range yesItems {
						pdf.MultiCell(0, 5, "  "+yi, "", "L", false)
					}
				}
			}
			pdf.Ln(4)
		}
	}

	// Regulatory note
	sectionHeader(pdf, "Note Réglementaire")
	pdf.Ln(2)
	setFont(pdf, "", 9, colBlack)
	pdf.MultiCell(0, 6,
		"Ce rapport est généré conformément au Règlement (UE) 2024/1689 sur l'Intelligence Artificielle (AI Act), "+
			"entré en vigueur le 1er août 2024. Les systèmes classifiés \"Haut risque\" sont soumis à une évaluation "+
			"de conformité avant déploiement. Les systèmes \"Interdits\" ne peuvent être mis en service sur le territoire "+
			"de l'Union Européenne. Ce document doit être mis à jour à chaque modification substantielle d'un système IA.",
		"", "L", false)

	var buf bytes.Buffer
	if err := pdf.Output(&buf); err != nil {
		return fmt.Errorf("pdf output: %w", err)
	}
	_, err := w.Write(buf.Bytes())
	return err
}

// ─── GenerateDPIA ─────────────────────────────────────────────────────────────

// GenerateDPIA generates a pre-filled DPIA template for a processing entry (Art. 35 GDPR).
func GenerateDPIA(entry ProcessingEntry, tenantName string, w io.Writer) error {
	if tenantName == "" {
		tenantName = "Organisation"
	}

	pdf := newPDF()
	footer(pdf)
	covePage(pdf, "Analyse d'Impact relative à la Protection des Données",
		"Data Protection Impact Assessment (DPIA) — Article 35 RGPD", tenantName)

	pdf.AddPage()

	// Section 1 — Description
	sectionHeader(pdf, "1. Description du Traitement")
	pdf.Ln(2)
	labelValue(pdf, "Cas d'usage", entry.UseCaseName)
	labelValue(pdf, "Finalité", entry.Purpose)
	labelValue(pdf, "Base légale", LegalBasisLabels[entry.LegalBasis])
	labelValue(pdf, "Catégories de données", strings.Join(entry.DataCategories, ", "))
	labelValue(pdf, "Destinataires", strings.Join(entry.Recipients, ", "))
	labelValue(pdf, "Sous-traitants LLM", strings.Join(entry.Processors, ", "))
	labelValue(pdf, "Durée de conservation", entry.RetentionPeriod)
	labelValue(pdf, "Classification AI Act", RiskLabels[entry.RiskLevel])

	// Section 2 — Nécessité et proportionnalité
	sectionHeader(pdf, "2. Nécessité et Proportionnalité")
	pdf.Ln(2)
	setFont(pdf, "", 9, colBlack)
	pdf.MultiCell(0, 6,
		"Le traitement est nécessaire pour atteindre la finalité identifiée. "+
			"La pseudonymisation automatique des données personnelles par Veylant IA "+
			"(avant transmission aux fournisseurs LLM) constitue une mesure de minimisation des données "+
			"conforme à l'Art. 5(1)(c) RGPD. "+
			"Seules les catégories de données strictement nécessaires sont traitées.",
		"", "L", false)

	// Section 3 — Risques
	sectionHeader(pdf, "3. Évaluation des Risques")
	pdf.Ln(2)

	risks := []struct{ risk, proba, impact, mitigation string }{
		{
			"Accès non autorisé aux données",
			"Faible",
			"Élevé",
			"RBAC strict, MFA, TLS 1.3, chiffrement AES-256-GCM",
		},
		{
			"Fuite de données vers fournisseur LLM",
			"Très faible",
			"Élevé",
			"Pseudonymisation PII avant envoi, contrats DPA avec fournisseurs (Art. 28)",
		},
		{
			"Rétention excessive des données",
			"Faible",
			"Moyen",
			"TTL automatique ClickHouse, politique de rétention définie (" + entry.RetentionPeriod + ")",
		},
		{
			"Décision automatisée non supervisée",
			"Moyen",
			"Élevé",
			"Supervision humaine obligatoire pour décisions à impact légal",
		},
		{
			"Indisponibilité du service",
			"Faible",
			"Moyen",
			"Circuit breaker, failover multi-fournisseurs, monitoring Prometheus",
		},
	}
	widths := []float64{60, 22, 22, 61}
	setFont(pdf, "B", 9, colBlack)
	tableRow(pdf, []string{"Risque", "Probabilité", "Impact", "Mesure d'atténuation"}, widths, true)
	for i, r := range risks {
		setFont(pdf, "", 8, colBlack)
		tableRow(pdf, []string{r.risk, r.proba, r.impact, r.mitigation}, widths, i%2 == 0)
	}

	// Section 4 — Mesures d'atténuation
	sectionHeader(pdf, "4. Mesures d'Atténuation Implémentées")
	pdf.Ln(2)
	if entry.SecurityMeasures != "" {
		labelValue(pdf, "Mesures spécifiques", entry.SecurityMeasures)
	}
	genericMeasures := []string{
		"Pseudonymisation automatique des PII (regex + NER + validation LLM)",
		"Chiffrement AES-256-GCM au repos et TLS 1.3 en transit",
		"RBAC avec 4 niveaux (Admin, Manager, Utilisateur, Auditeur)",
		"Journaux d'audit immuables avec conservation " + entry.RetentionPeriod,
		"Tests de sécurité SAST/DAST en pipeline CI/CD",
		"Contrats de sous-traitance (DPA) avec chaque fournisseur LLM",
	}
	for _, m := range genericMeasures {
		setFont(pdf, "", 9, colBlack)
		pdf.CellFormat(5, 6, "✓", "", 0, "L", false, 0, "")
		pdf.MultiCell(0, 6, m, "", "L", false)
	}

	// Section 5 — Risque résiduel
	sectionHeader(pdf, "5. Risque Résiduel et Conclusion")
	pdf.Ln(2)
	setFont(pdf, "", 9, colBlack)
	pdf.MultiCell(0, 6,
		"Après application des mesures d'atténuation identifiées, le risque résiduel est évalué comme "+
			"ACCEPTABLE. Ce traitement peut être mis en œuvre sous réserve du respect continu des mesures "+
			"de sécurité décrites. Une réévaluation annuelle ou lors de toute modification substantielle "+
			"du traitement est recommandée.",
		"", "L", false)

	// Section 6 — Signatures
	sectionHeader(pdf, "6. Approbation")
	pdf.Ln(4)
	col1 := 85.0
	col2 := 85.0
	setFont(pdf, "B", 9, colBlack)
	pdf.CellFormat(col1, 6, "Responsable de traitement", "", 0, "C", false, 0, "")
	pdf.CellFormat(col2, 6, "Délégué à la Protection des Données", "", 1, "C", false, 0, "")
	pdf.Ln(10)
	setFont(pdf, "", 9, colGray)
	pdf.CellFormat(col1, 6, "Signature : ________________________", "", 0, "C", false, 0, "")
	pdf.CellFormat(col2, 6, "Signature : ________________________", "", 1, "C", false, 0, "")
	pdf.Ln(3)
	pdf.CellFormat(col1, 6, "Date : ____/____/________", "", 0, "C", false, 0, "")
	pdf.CellFormat(col2, 6, "Date : ____/____/________", "", 1, "C", false, 0, "")

	var buf bytes.Buffer
	if err := pdf.Output(&buf); err != nil {
		return fmt.Errorf("pdf output: %w", err)
	}
	_, err := w.Write(buf.Bytes())
	return err
}
