"""Generated-app email delivery policy.

Core decides whether a Cloud ``email.send`` job uses:

1. The project's own Resend / Mailgun / SES connector (BYOK), or
2. The workspace's own managed email domain (``src/domain/email_domains/`` — verified on
   P2HS's own AWS SES account, quota-gated per plan), or
3. The platform Cloud email default (``CLOUD_EMAIL_*``), which is independent of
   Core's own ``EMAIL_PROVIDER`` used for auth/billing/ops mail.

Cloud executes. This module never sends mail and never returns raw secrets.
"""
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING
from uuid import UUID

from src.config import Settings, get_settings
from src.domain.connectors.service import ConnectorExecutionDecision, ConnectorService
from src.integrations.email_provider import (
    cloud_email_transport_configured,
    normalized_cloud_email_provider,
)

if TYPE_CHECKING:
    from src.domain.email_domains.repository import EmailDomainRepository
    from src.domain.email_domains.service import EmailQuotaService

EMAIL_JOB_TYPES = frozenset({"email.send"})


@dataclass(frozen=True)
class EmailDeliveryPolicy:
    delivery_scope: str
    provider: str | None
    connector_decision: ConnectorExecutionDecision | None = None
    email_from: str | None = None
    email_configuration_set: str | None = None
    reason_code: str | None = None
    message: str | None = None

    @property
    def allowed(self) -> bool:
        return self.reason_code is None


def _denied(reason_code: str, message: str) -> EmailDeliveryPolicy:
    return EmailDeliveryPolicy(
        delivery_scope="none",
        provider=None,
        reason_code=reason_code,
        message=message,
    )


def _internal(settings: Settings | None = None) -> EmailDeliveryPolicy:
    settings = settings or get_settings()
    if not cloud_email_transport_configured(settings):
        return _denied(
            "email_provider_not_configured",
            "No project email connector is enabled and the platform "
            "Cloud email default is not configured",
        )
    return EmailDeliveryPolicy(
        delivery_scope="internal",
        provider=normalized_cloud_email_provider(settings),
    )


def platform_default_email_delivery(settings: Settings | None = None) -> EmailDeliveryPolicy:
    """Force platform Cloud email (CLOUD_EMAIL_*), skipping project connectors."""
    return _internal(settings)


async def _platform_domain_delivery(
    *,
    workspace_id: UUID,
    email_domain_repo: "EmailDomainRepository",
    email_quota_service: "EmailQuotaService",
) -> EmailDeliveryPolicy | None:
    """Workspace's own verified managed domain, quota-gated. None if not configured —
    caller falls through to the shared platform default. A configured-but-quota-denied
    domain returns a denial and never falls through (see module docstring tier order)."""
    domain = await email_domain_repo.get_by_workspace(workspace_id)
    if not domain or domain.status != "verified":
        return None

    decision = await email_quota_service.check_and_reserve(workspace_id)
    if not decision.allowed:
        return _denied(
            decision.reason_code or "email_quota_exceeded",
            decision.message or "Managed email domain quota exceeded",
        )

    return EmailDeliveryPolicy(
        delivery_scope="platform_domain",
        provider="ses",
        email_from=f"noreply@{domain.domain}",
        email_configuration_set=domain.ses_configuration_set_name,
    )


async def resolve_email_delivery(
    *,
    connector_service: ConnectorService,
    workspace_id: UUID,
    project_id: UUID,
    enabled_capabilities: set[str],
    connection_id: str | None = None,
    action: str | None = None,
    approval_token: str | None = None,
    provider_hint: str | None = None,
    settings: Settings | None = None,
    email_domain_repo: "EmailDomainRepository | None" = None,
    email_quota_service: "EmailQuotaService | None" = None,
) -> EmailDeliveryPolicy:
    """Resolve BYOK connector vs managed workspace domain vs platform Cloud email default.

    An explicit ``connection_id`` is authoritative: failure denies the job instead
    of silently falling back. With no connection, the first allowed email
    ``*.send_email`` connection wins (hint preferred); otherwise the workspace's own
    managed email domain if verified (quota-gated — a denial here is final, it never
    falls through to the platform default); otherwise the platform default.
    """
    settings = settings or get_settings()

    if connection_id:
        try:
            conn_uuid = UUID(connection_id)
        except ValueError:
            return _denied("invalid_connection_id", "Invalid connector connection_id")

        action_slug = action
        if not action_slug:
            inferred = await connector_service.infer_email_send_action(
                project_id=project_id,
                connection_id=conn_uuid,
            )
            if inferred is None:
                return _denied(
                    "connector_request_incomplete",
                    "action is required unless the connection is an email connector",
                )
            action_slug = inferred

        decision = await connector_service.authorize_execution(
            workspace_id=workspace_id,
            project_id=project_id,
            connection_id=conn_uuid,
            action_slug=action_slug,
            enabled_capabilities=enabled_capabilities,
            approval_token=approval_token,
        )
        if not decision.allowed:
            return _denied(
                decision.reason_code or "connector_denied",
                decision.message or "Connector action is not allowed",
            )
        provider = decision.connector.key if decision.connector else provider_hint
        return EmailDeliveryPolicy(
            delivery_scope="connector",
            provider=provider,
            connector_decision=decision,
        )

    decision = await connector_service.resolve_email_send(
        workspace_id=workspace_id,
        project_id=project_id,
        enabled_capabilities=enabled_capabilities,
        provider_hint=provider_hint,
        approval_token=approval_token,
    )
    if decision and decision.allowed:
        provider = decision.connector.key if decision.connector else provider_hint
        return EmailDeliveryPolicy(
            delivery_scope="connector",
            provider=provider,
            connector_decision=decision,
        )

    if email_domain_repo is not None and email_quota_service is not None:
        domain_policy = await _platform_domain_delivery(
            workspace_id=workspace_id,
            email_domain_repo=email_domain_repo,
            email_quota_service=email_quota_service,
        )
        if domain_policy is not None:
            return domain_policy

    return _internal(settings)
