# mypy: disable-error-code="arg-type,assignment,union-attr"
"""
Email Template service — templates CRUD, settings, logs, stats, initialization.

Phase 1 Step 4: extracted from ``compliance.api.email_template_routes``.
Version workflow (draft/review/approve/reject/publish/preview/send-test)
lives in ``compliance.services.email_template_version_service``.
"""

import uuid
from datetime import datetime, timezone
from typing import Any, Optional

from sqlalchemy.orm import Session

from compliance.db.email_template_models import (
    EmailSendLogDB,
    EmailTemplateDB,
    EmailTemplateSettingsDB,
    EmailTemplateVersionDB,
)
from compliance.domain import ConflictError, NotFoundError, ValidationError
from compliance.schemas.email_template import SettingsUpdate, TemplateCreate

# Template type catalog — shared across both services and the route module.
TEMPLATE_TYPES: dict[str, dict[str, Any]] = {
    "welcome": {"name": "Willkommen", "category": "general", "variables": ["user_name", "company_name", "login_url"]},
    "verification": {"name": "E-Mail-Verifizierung", "category": "general", "variables": ["user_name", "verification_url", "expiry_hours"]},
    "password_reset": {"name": "Passwort zuruecksetzen", "category": "general", "variables": ["user_name", "reset_url", "expiry_hours"]},
    "dsr_receipt": {"name": "DSR Eingangsbestaetigung", "category": "dsr", "variables": ["requester_name", "reference_number", "request_type", "deadline"]},
    "dsr_identity_request": {"name": "DSR Identitaetsanfrage", "category": "dsr", "variables": ["requester_name", "reference_number"]},
    "dsr_completion": {"name": "DSR Abschluss", "category": "dsr", "variables": ["requester_name", "reference_number", "request_type", "completion_date"]},
    "dsr_rejection": {"name": "DSR Ablehnung", "category": "dsr", "variables": ["requester_name", "reference_number", "rejection_reason", "legal_basis"]},
    "dsr_extension": {"name": "DSR Fristverlaengerung", "category": "dsr", "variables": ["requester_name", "reference_number", "new_deadline", "extension_reason"]},
    "consent_request": {"name": "Einwilligungsanfrage", "category": "consent", "variables": ["user_name", "purpose", "consent_url"]},
    "consent_confirmation": {"name": "Einwilligungsbestaetigung", "category": "consent", "variables": ["user_name", "purpose", "consent_date"]},
    "consent_withdrawal": {"name": "Widerruf bestaetigt", "category": "consent", "variables": ["user_name", "purpose", "withdrawal_date"]},
    "consent_reminder": {"name": "Einwilligungs-Erinnerung", "category": "consent", "variables": ["user_name", "purpose", "expiry_date"]},
    "breach_notification_authority": {"name": "Datenpanne Aufsichtsbehoerde", "category": "breach", "variables": ["incident_date", "incident_description", "affected_count", "measures_taken", "authority_name"]},
    "breach_notification_affected": {"name": "Datenpanne Betroffene", "category": "breach", "variables": ["user_name", "incident_date", "incident_description", "measures_taken", "contact_info"]},
    "breach_internal": {"name": "Datenpanne intern", "category": "breach", "variables": ["reporter_name", "incident_date", "incident_description", "severity"]},
    "vendor_dpa_request": {"name": "AVV-Anfrage", "category": "vendor", "variables": ["vendor_name", "contact_name", "deadline", "requirements"]},
    "vendor_review_reminder": {"name": "Vendor-Pruefung Erinnerung", "category": "vendor", "variables": ["vendor_name", "review_due_date", "last_review_date"]},
    "training_invitation": {"name": "Schulungseinladung", "category": "training", "variables": ["user_name", "training_title", "training_date", "training_url"]},
    "training_reminder": {"name": "Schulungs-Erinnerung", "category": "training", "variables": ["user_name", "training_title", "deadline"]},
    "training_completion": {"name": "Schulung abgeschlossen", "category": "training", "variables": ["user_name", "training_title", "completion_date", "certificate_url"]},
}

VALID_STATUSES = ["draft", "review", "approved", "published"]
VALID_CATEGORIES = ["general", "dsr", "consent", "breach", "vendor", "training"]


def _template_to_dict(
    t: EmailTemplateDB, latest_version: Optional[EmailTemplateVersionDB] = None
) -> dict[str, Any]:
    result: dict[str, Any] = {
        "id": str(t.id),
        "tenant_id": str(t.tenant_id),
        "template_type": t.template_type,
        "name": t.name,
        "description": t.description,
        "category": t.category,
        "is_active": t.is_active,
        "sort_order": t.sort_order,
        "variables": t.variables or [],
        "created_at": t.created_at.isoformat() if t.created_at else None,
        "updated_at": t.updated_at.isoformat() if t.updated_at else None,
    }
    if latest_version:
        result["latest_version"] = _version_to_dict(latest_version)
    return result


def _version_to_dict(v: EmailTemplateVersionDB) -> dict[str, Any]:
    return {
        "id": str(v.id),
        "template_id": str(v.template_id),
        "version": v.version,
        "language": v.language,
        "subject": v.subject,
        "body_html": v.body_html,
        "body_text": v.body_text,
        "status": v.status,
        "submitted_at": v.submitted_at.isoformat() if v.submitted_at else None,
        "submitted_by": v.submitted_by,
        "published_at": v.published_at.isoformat() if v.published_at else None,
        "published_by": v.published_by,
        "created_at": v.created_at.isoformat() if v.created_at else None,
        "created_by": v.created_by,
    }


def _render_template(html: str, variables: dict[str, str]) -> str:
    """Replace {{variable}} placeholders with values."""
    result = html
    for key, value in variables.items():
        result = result.replace(f"{{{{{key}}}}}", str(value))
    return result


class EmailTemplateService:
    """Business logic for templates, settings, logs, stats, initialization."""

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

    # ------------------------------------------------------------------
    # Type catalog + defaults
    # ------------------------------------------------------------------

    @staticmethod
    def list_types() -> list[dict[str, Any]]:
        return [
            {
                "type": ttype,
                "name": info["name"],
                "category": info["category"],
                "variables": info["variables"],
            }
            for ttype, info in TEMPLATE_TYPES.items()
        ]

    @staticmethod
    def default_content(template_type: str) -> dict[str, Any]:
        if template_type not in TEMPLATE_TYPES:
            raise NotFoundError(f"Unknown template type: {template_type}")
        info = TEMPLATE_TYPES[template_type]
        vars_html = " ".join(
            f'<span style="color:#4F46E5">{{{{{v}}}}}</span>' for v in info["variables"]
        )
        return {
            "template_type": template_type,
            "name": info["name"],
            "category": info["category"],
            "variables": info["variables"],
            "default_subject": f"{info['name']} - {{{{company_name}}}}",
            "default_body_html": (
                f"<p>Sehr geehrte(r) {{{{user_name}}}},</p>\n"
                f"<p>[Inhalt hier einfuegen]</p>\n"
                f"<p>Verfuegbare Variablen: {vars_html}</p>\n"
                f"<p>Mit freundlichen Gruessen<br/>{{{{sender_name}}}}</p>"
            ),
        }

    # ------------------------------------------------------------------
    # Stats
    # ------------------------------------------------------------------

    def stats(self, tenant_id: str) -> dict[str, Any]:
        tid = uuid.UUID(tenant_id)
        base = self.db.query(EmailTemplateDB).filter(EmailTemplateDB.tenant_id == tid)
        total = base.count()
        active = base.filter(EmailTemplateDB.is_active).count()

        published_count = 0
        for t in base.all():
            has_published = (
                self.db.query(EmailTemplateVersionDB)
                .filter(
                    EmailTemplateVersionDB.template_id == t.id,
                    EmailTemplateVersionDB.status == "published",
                )
                .count()
                > 0
            )
            if has_published:
                published_count += 1

        by_category: dict[str, int] = {}
        for cat in VALID_CATEGORIES:
            by_category[cat] = base.filter(EmailTemplateDB.category == cat).count()

        total_sent = (
            self.db.query(EmailSendLogDB)
            .filter(EmailSendLogDB.tenant_id == tid)
            .count()
        )

        return {
            "total": total,
            "active": active,
            "published": published_count,
            "draft": total - published_count,
            "by_category": by_category,
            "total_sent": total_sent,
        }

    # ------------------------------------------------------------------
    # Settings
    # ------------------------------------------------------------------

    @staticmethod
    def _settings_defaults() -> dict[str, Any]:
        return {
            "sender_name": "Datenschutzbeauftragter",
            "sender_email": "datenschutz@example.de",
            "reply_to": None,
            "logo_url": None,
            "primary_color": "#4F46E5",
            "secondary_color": "#7C3AED",
            "footer_text": "Datenschutzhinweis: Diese E-Mail enthaelt vertrauliche Informationen.",
            "company_name": None,
            "company_address": None,
        }

    @staticmethod
    def _settings_to_dict(s: EmailTemplateSettingsDB) -> dict[str, Any]:
        return {
            "sender_name": s.sender_name,
            "sender_email": s.sender_email,
            "reply_to": s.reply_to,
            "logo_url": s.logo_url,
            "primary_color": s.primary_color,
            "secondary_color": s.secondary_color,
            "footer_text": s.footer_text,
            "company_name": s.company_name,
            "company_address": s.company_address,
        }

    def get_settings(self, tenant_id: str) -> dict[str, Any]:
        tid = uuid.UUID(tenant_id)
        settings = (
            self.db.query(EmailTemplateSettingsDB)
            .filter(EmailTemplateSettingsDB.tenant_id == tid)
            .first()
        )
        if not settings:
            return self._settings_defaults()
        return self._settings_to_dict(settings)

    def update_settings(self, tenant_id: str, body: SettingsUpdate) -> dict[str, Any]:
        tid = uuid.UUID(tenant_id)
        settings = (
            self.db.query(EmailTemplateSettingsDB)
            .filter(EmailTemplateSettingsDB.tenant_id == tid)
            .first()
        )
        if not settings:
            settings = EmailTemplateSettingsDB(tenant_id=tid)
            self.db.add(settings)
        for field in (
            "sender_name", "sender_email", "reply_to", "logo_url",
            "primary_color", "secondary_color", "footer_text",
            "company_name", "company_address",
        ):
            val = getattr(body, field, None)
            if val is not None:
                setattr(settings, field, val)
        settings.updated_at = datetime.now(timezone.utc)
        self.db.commit()
        self.db.refresh(settings)
        return self._settings_to_dict(settings)

    # ------------------------------------------------------------------
    # Send logs
    # ------------------------------------------------------------------

    def send_logs(
        self,
        tenant_id: str,
        limit: int,
        offset: int,
        template_type: Optional[str],
    ) -> dict[str, Any]:
        tid = uuid.UUID(tenant_id)
        q = self.db.query(EmailSendLogDB).filter(EmailSendLogDB.tenant_id == tid)
        if template_type:
            q = q.filter(EmailSendLogDB.template_type == template_type)
        total = q.count()
        logs = (
            q.order_by(EmailSendLogDB.sent_at.desc())
            .offset(offset)
            .limit(limit)
            .all()
        )
        return {
            "logs": [
                {
                    "id": str(lg.id),
                    "template_type": lg.template_type,
                    "recipient": lg.recipient,
                    "subject": lg.subject,
                    "status": lg.status,
                    "variables": lg.variables or {},
                    "error_message": lg.error_message,
                    "sent_at": lg.sent_at.isoformat() if lg.sent_at else None,
                }
                for lg in logs
            ],
            "total": total,
            "limit": limit,
            "offset": offset,
        }

    # ------------------------------------------------------------------
    # Initialization + template CRUD
    # ------------------------------------------------------------------

    def initialize_defaults(self, tenant_id: str) -> dict[str, Any]:
        tid = uuid.UUID(tenant_id)
        existing = (
            self.db.query(EmailTemplateDB)
            .filter(EmailTemplateDB.tenant_id == tid)
            .count()
        )
        if existing > 0:
            return {"message": "Templates already initialized", "count": existing}

        created = 0
        for idx, (ttype, info) in enumerate(TEMPLATE_TYPES.items()):
            self.db.add(
                EmailTemplateDB(
                    tenant_id=tid,
                    template_type=ttype,
                    name=info["name"],
                    category=info["category"],
                    sort_order=idx * 10,
                    variables=info["variables"],
                )
            )
            created += 1
        self.db.commit()
        return {"message": f"{created} templates created", "count": created}

    def list_templates(
        self, tenant_id: str, category: Optional[str]
    ) -> list[dict[str, Any]]:
        tid = uuid.UUID(tenant_id)
        q = self.db.query(EmailTemplateDB).filter(EmailTemplateDB.tenant_id == tid)
        if category:
            q = q.filter(EmailTemplateDB.category == category)
        templates = q.order_by(EmailTemplateDB.sort_order).all()
        result = []
        for t in templates:
            latest = (
                self.db.query(EmailTemplateVersionDB)
                .filter(EmailTemplateVersionDB.template_id == t.id)
                .order_by(EmailTemplateVersionDB.created_at.desc())
                .first()
            )
            result.append(_template_to_dict(t, latest))
        return result

    def create_template(self, tenant_id: str, body: TemplateCreate) -> dict[str, Any]:
        if body.template_type not in TEMPLATE_TYPES:
            raise ValidationError(f"Unknown template type: {body.template_type}")
        tid = uuid.UUID(tenant_id)
        existing = (
            self.db.query(EmailTemplateDB)
            .filter(
                EmailTemplateDB.tenant_id == tid,
                EmailTemplateDB.template_type == body.template_type,
            )
            .first()
        )
        if existing:
            raise ConflictError(
                f"Template type '{body.template_type}' already exists"
            )
        info = TEMPLATE_TYPES[body.template_type]
        t = EmailTemplateDB(
            tenant_id=tid,
            template_type=body.template_type,
            name=body.name or info["name"],
            description=body.description,
            category=body.category or info["category"],
            is_active=body.is_active,
            variables=info["variables"],
        )
        self.db.add(t)
        self.db.commit()
        self.db.refresh(t)
        return _template_to_dict(t)

    def get_template(self, tenant_id: str, template_id: str) -> dict[str, Any]:
        try:
            tid = uuid.UUID(template_id)
        except ValueError as exc:
            raise ValidationError("Invalid template ID") from exc

        t = (
            self.db.query(EmailTemplateDB)
            .filter(
                EmailTemplateDB.id == tid,
                EmailTemplateDB.tenant_id == uuid.UUID(tenant_id),
            )
            .first()
        )
        if not t:
            raise NotFoundError("Template not found")
        latest = (
            self.db.query(EmailTemplateVersionDB)
            .filter(EmailTemplateVersionDB.template_id == t.id)
            .order_by(EmailTemplateVersionDB.created_at.desc())
            .first()
        )
        return _template_to_dict(t, latest)
