# mypy: disable-error-code="arg-type,assignment,union-attr"
"""
Email Template version service — version workflow (draft/review/approve/
reject/publish/preview/send-test).

Phase 1 Step 4: extracted from ``compliance.api.email_template_routes``.
Template-level CRUD + settings + stats live in
``compliance.services.email_template_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,
    EmailTemplateApprovalDB,
    EmailTemplateDB,
    EmailTemplateVersionDB,
)
from compliance.domain import ConflictError, NotFoundError, ValidationError
from compliance.schemas.email_template import (
    PreviewRequest,
    SendTestRequest,
    VersionCreate,
    VersionUpdate,
)
from compliance.services.email_template_service import (
    _render_template,
    _version_to_dict,
)


class EmailTemplateVersionService:
    """Business logic for email-template version workflow + preview + test-send."""

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

    # ------------------------------------------------------------------
    # Internal lookups
    # ------------------------------------------------------------------

    @staticmethod
    def _parse_template_uuid(template_id: str) -> uuid.UUID:
        try:
            return uuid.UUID(template_id)
        except ValueError as exc:
            raise ValidationError("Invalid template ID") from exc

    @staticmethod
    def _parse_version_uuid(version_id: str) -> uuid.UUID:
        try:
            return uuid.UUID(version_id)
        except ValueError as exc:
            raise ValidationError("Invalid version ID") from exc

    def _template_or_raise(
        self, tenant_id: str, tid: uuid.UUID
    ) -> EmailTemplateDB:
        template = (
            self.db.query(EmailTemplateDB)
            .filter(
                EmailTemplateDB.id == tid,
                EmailTemplateDB.tenant_id == uuid.UUID(tenant_id),
            )
            .first()
        )
        if not template:
            raise NotFoundError("Template not found")
        return template

    def _version_or_raise(self, vid: uuid.UUID) -> EmailTemplateVersionDB:
        v = (
            self.db.query(EmailTemplateVersionDB)
            .filter(EmailTemplateVersionDB.id == vid)
            .first()
        )
        if not v:
            raise NotFoundError("Version not found")
        return v

    # ------------------------------------------------------------------
    # Create / list / get versions
    # ------------------------------------------------------------------

    def create_version(
        self, tenant_id: str, template_id: str, body: VersionCreate
    ) -> dict[str, Any]:
        tid = self._parse_template_uuid(template_id)
        self._template_or_raise(tenant_id, tid)
        v = EmailTemplateVersionDB(
            template_id=tid,
            version=body.version,
            language=body.language,
            subject=body.subject,
            body_html=body.body_html,
            body_text=body.body_text,
            status="draft",
        )
        self.db.add(v)
        self.db.commit()
        self.db.refresh(v)
        return _version_to_dict(v)

    def list_versions(
        self, tenant_id: str, template_id: str
    ) -> list[dict[str, Any]]:
        tid = self._parse_template_uuid(template_id)
        self._template_or_raise(tenant_id, tid)
        versions = (
            self.db.query(EmailTemplateVersionDB)
            .filter(EmailTemplateVersionDB.template_id == tid)
            .order_by(EmailTemplateVersionDB.created_at.desc())
            .all()
        )
        return [_version_to_dict(v) for v in versions]

    def get_version(self, version_id: str) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        return _version_to_dict(self._version_or_raise(vid))

    def update_version(
        self, version_id: str, body: VersionUpdate
    ) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        v = self._version_or_raise(vid)
        if v.status != "draft":
            raise ValidationError("Only draft versions can be edited")
        if body.subject is not None:
            v.subject = body.subject
        if body.body_html is not None:
            v.body_html = body.body_html
        if body.body_text is not None:
            v.body_text = body.body_text
        self.db.commit()
        self.db.refresh(v)
        return _version_to_dict(v)

    # ------------------------------------------------------------------
    # Workflow transitions
    # ------------------------------------------------------------------

    def submit(self, version_id: str) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        v = self._version_or_raise(vid)
        if v.status != "draft":
            raise ValidationError("Only draft versions can be submitted")
        v.status = "review"
        v.submitted_at = datetime.now(timezone.utc)
        v.submitted_by = "admin"
        self.db.commit()
        self.db.refresh(v)
        return _version_to_dict(v)

    def approve(self, version_id: str, comment: Optional[str]) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        v = self._version_or_raise(vid)
        if v.status != "review":
            raise ValidationError("Only review versions can be approved")
        v.status = "approved"
        self.db.add(
            EmailTemplateApprovalDB(
                version_id=vid,
                action="approve",
                comment=comment,
                approved_by="admin",
            )
        )
        self.db.commit()
        self.db.refresh(v)
        return _version_to_dict(v)

    def reject(self, version_id: str, comment: Optional[str]) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        v = self._version_or_raise(vid)
        if v.status != "review":
            raise ValidationError("Only review versions can be rejected")
        v.status = "draft"
        self.db.add(
            EmailTemplateApprovalDB(
                version_id=vid,
                action="reject",
                comment=comment,
                approved_by="admin",
            )
        )
        self.db.commit()
        self.db.refresh(v)
        return _version_to_dict(v)

    def publish(self, version_id: str) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        v = self._version_or_raise(vid)
        if v.status not in ("approved", "review", "draft"):
            raise ValidationError("Version cannot be published")
        v.status = "published"
        v.published_at = datetime.now(timezone.utc)
        v.published_by = "admin"
        self.db.commit()
        self.db.refresh(v)
        return _version_to_dict(v)

    # ------------------------------------------------------------------
    # Preview + test send
    # ------------------------------------------------------------------

    def preview(self, version_id: str, body: PreviewRequest) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        v = self._version_or_raise(vid)

        variables = dict(body.variables or {})
        template = (
            self.db.query(EmailTemplateDB)
            .filter(EmailTemplateDB.id == v.template_id)
            .first()
        )
        if template and template.variables:
            for var in list(template.variables):
                if var not in variables:
                    variables[var] = f"[{var}]"

        return {
            "subject": _render_template(v.subject, variables),
            "body_html": _render_template(v.body_html, variables),
            "variables_used": variables,
        }

    def send_test(
        self, tenant_id: str, version_id: str, body: SendTestRequest
    ) -> dict[str, Any]:
        vid = self._parse_version_uuid(version_id)
        v = self._version_or_raise(vid)
        template = (
            self.db.query(EmailTemplateDB)
            .filter(EmailTemplateDB.id == v.template_id)
            .first()
        )
        variables = body.variables or {}
        rendered_subject = _render_template(v.subject, variables)

        self.db.add(
            EmailSendLogDB(
                tenant_id=uuid.UUID(tenant_id),
                template_type=template.template_type if template else "unknown",
                version_id=vid,
                recipient=body.recipient,
                subject=rendered_subject,
                status="test_sent",
                variables=variables,
            )
        )
        self.db.commit()
        return {
            "success": True,
            "message": f"Test-E-Mail an {body.recipient} gesendet (Simulation)",
            "subject": rendered_subject,
        }
