"""
DSR User Data Export Service — aggregates all CMP data about a user.

Supports Art. 15 (access right, PDF) and Art. 20 (data portability, JSON/CSV).
Collects from: Banner Consents, Einwilligungen, Consent Audit Trail, DSR History.
"""

import csv
import io
import json
import logging
import uuid
from datetime import datetime, timezone
from typing import Any, Optional

from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle

from sqlalchemy import text
from sqlalchemy.orm import Session

from compliance.services.banner_dsr_service import BannerDSRService

logger = logging.getLogger(__name__)

PURPLE = colors.HexColor("#7c3aed")
LIGHT_PURPLE = colors.HexColor("#f5f3ff")
GRAY = colors.HexColor("#6b7280")


class DSRExportService:
    """Aggregates and exports all user data stored in the CMP."""

    def __init__(self, db: Session) -> None:
        self.db = db

    def aggregate_user_data(self, tenant_id: str, email: str) -> dict[str, Any]:
        """Collect ALL data about a user from all CMP sources."""
        now = datetime.now(timezone.utc)
        tid = tenant_id  # Keep as string — let PostgreSQL cast

        # 1. Banner consents + audit trail
        banner_data: dict[str, Any] = {"banner_consents": [], "audit_trail": []}
        try:
            banner_svc = BannerDSRService(self.db)
            banner_data = banner_svc.export_for_dsr(tenant_id, email)
        except Exception as e:
            logger.warning("Banner DSR export failed: %s", e)
            try:
                self.db.rollback()
            except Exception:
                pass

        # 2. Einwilligungen (user-based consents)
        einwilligungen: list[dict] = []
        try:
            q = text("""
                SELECT c.id, c.data_point_id, c.granted, c.granted_at, c.revoked_at,
                       c.consent_version, c.source, c.ip_address, c.user_agent, c.created_at
                FROM compliance_einwilligungen_consents c
                WHERE c.tenant_id = CAST(:tid AS VARCHAR) AND c.user_id = :email
                ORDER BY c.created_at DESC
            """)
            rows = self.db.execute(q, {"tid": tid, "email": email}).fetchall()
            for r in rows:
                entry = dict(r._mapping)
                for k, v in entry.items():
                    if isinstance(v, datetime):
                        entry[k] = v.isoformat()
                    elif isinstance(v, uuid.UUID):
                        entry[k] = str(v)
                # Get history
                hist_q = text("""
                    SELECT action, consent_version, ip_address, user_agent, source, created_at
                    FROM compliance_einwilligungen_consent_history
                    WHERE consent_id = :cid ORDER BY created_at
                """)
                hist = self.db.execute(hist_q, {"cid": entry["id"]}).fetchall()
                entry["history"] = [
                    {k: (v.isoformat() if isinstance(v, datetime) else str(v) if isinstance(v, uuid.UUID) else v)
                     for k, v in dict(h._mapping).items()}
                    for h in hist
                ]
                einwilligungen.append(entry)
        except Exception as e:
            logger.warning("Einwilligungen export failed: %s", e)
            try:
                self.db.rollback()
            except Exception:
                pass

        # 3. DSR requests by this user
        dsr_requests: list[dict] = []
        try:
            q = text("""
                SELECT id, request_number, request_type, status, received_at, deadline_at, completed_at
                FROM compliance_dsr_requests
                WHERE tenant_id = :tid AND requester_email = :email
                ORDER BY received_at DESC
            """)
            rows = self.db.execute(q, {"tid": tid, "email": email}).fetchall()
            for r in rows:
                entry = dict(r._mapping)
                for k, v in entry.items():
                    if isinstance(v, datetime):
                        entry[k] = v.isoformat()
                    elif isinstance(v, uuid.UUID):
                        entry[k] = str(v)
                dsr_requests.append(entry)
        except Exception as e:
            logger.warning("DSR requests export failed: %s", e)
            try:
                self.db.rollback()
            except Exception:
                pass

        return {
            "export_date": now.isoformat(),
            "data_subject": {"email": email},
            "banner_consents": banner_data.get("banner_consents", []),
            "consent_audit_trail": banner_data.get("audit_trail", []),
            "einwilligungen": einwilligungen,
            "dsr_requests": dsr_requests,
            "metadata": {
                "tenant_id": tenant_id,
                "data_categories": ["Banner-Consents", "Einwilligungen", "Audit-Trail", "DSR-Anfragen"],
                "legal_basis": "Art. 15 / Art. 20 DSGVO",
            },
        }

    def export_json(self, tenant_id: str, email: str) -> tuple[bytes, str]:
        data = self.aggregate_user_data(tenant_id, email)
        data["metadata"]["export_format"] = "json"
        content = json.dumps(data, indent=2, ensure_ascii=False, default=str).encode("utf-8")
        return content, f"dsr-export-{email.split('@')[0]}.json"

    def export_csv(self, tenant_id: str, email: str) -> tuple[bytes, str]:
        data = self.aggregate_user_data(tenant_id, email)
        buf = io.StringIO()
        writer = csv.writer(buf)
        writer.writerow(["Kategorie", "Schluessel", "Wert", "Zeitpunkt", "Quelle"])

        # Banner consents
        for c in data.get("banner_consents", []):
            writer.writerow(["Banner-Consent", "site_id", c.get("site_id", ""), c.get("created_at", ""), "CMP"])
            writer.writerow(["Banner-Consent", "categories", ", ".join(c.get("categories", [])), c.get("updated_at", ""), "CMP"])
            writer.writerow(["Banner-Consent", "ip_hash", c.get("ip_hash", ""), c.get("created_at", ""), "CMP"])

        # Audit trail
        for a in data.get("consent_audit_trail", []):
            writer.writerow(["Audit-Trail", a.get("action", ""), ", ".join(a.get("categories", [])), a.get("created_at", ""), "CMP"])

        # Einwilligungen
        for e in data.get("einwilligungen", []):
            status = "Erteilt" if e.get("granted") else "Widerrufen"
            writer.writerow(["Einwilligung", e.get("data_point_id", ""), status, e.get("granted_at", ""), e.get("source", "")])

        # DSR requests
        for d in data.get("dsr_requests", []):
            writer.writerow(["DSR-Anfrage", d.get("request_type", ""), d.get("status", ""), d.get("received_at", ""), ""])

        content = buf.getvalue().encode("utf-8-sig")  # BOM for Excel
        return content, f"dsr-export-{email.split('@')[0]}.csv"

    def export_pdf(self, tenant_id: str, email: str) -> tuple[bytes, str]:
        data = self.aggregate_user_data(tenant_id, email)
        buf = io.BytesIO()
        doc = SimpleDocTemplate(buf, pagesize=A4, leftMargin=20 * mm, rightMargin=20 * mm, topMargin=25 * mm, bottomMargin=20 * mm)
        ss = getSampleStyleSheet()
        ss.add(ParagraphStyle("Title2", parent=ss["Title"], fontSize=20, textColor=PURPLE, spaceAfter=6))
        ss.add(ParagraphStyle("Section", parent=ss["Heading2"], fontSize=13, textColor=PURPLE, spaceBefore=10))
        ss.add(ParagraphStyle("Body2", parent=ss["Normal"], fontSize=9, leading=13))
        ss.add(ParagraphStyle("Small", parent=ss["Normal"], fontSize=8, textColor=GRAY))
        story: list = []

        # Cover
        story.append(Paragraph("Datenauskunft gemaess Art. 15 DSGVO", ss["Title2"]))
        story.append(Paragraph(f"Betroffene Person: {email}", ss["Body2"]))
        story.append(Paragraph(f"Erstellt am: {data['export_date'][:10]}", ss["Small"]))
        story.append(Spacer(1, 8 * mm))

        tbl_style = TableStyle([
            ("BACKGROUND", (0, 0), (-1, 0), LIGHT_PURPLE),
            ("TEXTCOLOR", (0, 0), (-1, 0), PURPLE),
            ("FONTSIZE", (0, 0), (-1, -1), 8),
            ("GRID", (0, 0), (-1, -1), 0.5, colors.lightgrey),
            ("VALIGN", (0, 0), (-1, -1), "TOP"),
            ("TOPPADDING", (0, 0), (-1, -1), 3),
            ("BOTTOMPADDING", (0, 0), (-1, -1), 3),
        ])

        # Section 1: Banner Consents
        consents = data.get("banner_consents", [])
        story.append(Paragraph(f"1. Banner-Consents ({len(consents)})", ss["Section"]))
        if consents:
            rows = [["Site", "Kategorien", "IP-Hash", "Erstellt", "Aktualisiert"]]
            for c in consents:
                rows.append([
                    str(c.get("site_id", "")),
                    ", ".join(c.get("categories", [])),
                    str(c.get("ip_hash", ""))[:12] + "...",
                    str(c.get("created_at", ""))[:10],
                    str(c.get("updated_at", ""))[:10],
                ])
            t = Table(rows, colWidths=[30 * mm, 40 * mm, 30 * mm, 25 * mm, 25 * mm])
            t.setStyle(tbl_style)
            story.append(t)
        else:
            story.append(Paragraph("Keine Banner-Consents gespeichert.", ss["Body2"]))

        # Section 2: Einwilligungen
        einw = data.get("einwilligungen", [])
        story.append(Paragraph(f"2. Einwilligungen ({len(einw)})", ss["Section"]))
        if einw:
            rows = [["Datenpunkt", "Status", "Erteilt am", "Widerrufen am", "IP-Adresse"]]
            for e in einw:
                rows.append([
                    str(e.get("data_point_id", "")),
                    "Erteilt" if e.get("granted") else "Widerrufen",
                    str(e.get("granted_at", ""))[:10],
                    str(e.get("revoked_at", ""))[:10] if e.get("revoked_at") else "-",
                    str(e.get("ip_address", ""))[:15] if e.get("ip_address") else "-",
                ])
            t = Table(rows, colWidths=[35 * mm, 25 * mm, 25 * mm, 25 * mm, 35 * mm])
            t.setStyle(tbl_style)
            story.append(t)
        else:
            story.append(Paragraph("Keine Einwilligungen gespeichert.", ss["Body2"]))

        # Section 3: Audit Trail
        trail = data.get("consent_audit_trail", [])
        story.append(Paragraph(f"3. Consent-Audit-Trail ({len(trail)})", ss["Section"]))
        if trail:
            rows = [["Aktion", "Kategorien", "Datum"]]
            for a in trail[:50]:  # Limit to 50 for PDF
                rows.append([
                    str(a.get("action", "")),
                    ", ".join(a.get("categories", [])),
                    str(a.get("created_at", ""))[:19],
                ])
            t = Table(rows, colWidths=[40 * mm, 60 * mm, 45 * mm])
            t.setStyle(tbl_style)
            story.append(t)
            if len(trail) > 50:
                story.append(Paragraph(f"... und {len(trail) - 50} weitere Eintraege (im JSON-Export enthalten)", ss["Small"]))
        else:
            story.append(Paragraph("Kein Audit-Trail vorhanden.", ss["Body2"]))

        # Section 4: DSR Requests
        dsrs = data.get("dsr_requests", [])
        story.append(Paragraph(f"4. Bisherige DSR-Anfragen ({len(dsrs)})", ss["Section"]))
        if dsrs:
            rows = [["Typ", "Status", "Eingegangen", "Abgeschlossen"]]
            for d in dsrs:
                rows.append([
                    str(d.get("request_type", "")),
                    str(d.get("status", "")),
                    str(d.get("received_at", ""))[:10],
                    str(d.get("completed_at", ""))[:10] if d.get("completed_at") else "-",
                ])
            t = Table(rows, colWidths=[35 * mm, 30 * mm, 35 * mm, 35 * mm])
            t.setStyle(tbl_style)
            story.append(t)

        # Footer
        story.append(Spacer(1, 15 * mm))
        story.append(Paragraph("Erstellt mit BreakPilot Compliance SDK | Art. 15 DSGVO Datenauskunft", ss["Small"]))

        doc.build(story)
        return buf.getvalue(), f"dsr-export-{email.split('@')[0]}.pdf"
