"""Adapters return acceptance, definite rejection, or an uncertain outcome."""
from dataclasses import dataclass
from hashlib import sha256
import logging
import smtplib
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.validators import validate_email
from django.core.mail import EmailMessage
from django.utils.module_loading import import_string

logger = logging.getLogger("apps.communications.delivery")
SMTP_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
MEMORY_BACKEND = "django.core.mail.backends.locmem.EmailBackend"


def email_configuration_valid():
    if settings.EMAIL_BACKEND == MEMORY_BACKEND:
        return not settings.COMMUNICATIONS_PRODUCTION
    if (settings.EMAIL_BACKEND != SMTP_BACKEND or not settings.COMMUNICATIONS_PRODUCTION
            or not settings.COMMUNICATIONS_ALLOW_EXTERNAL):
        return False
    try:
        validate_email(settings.DEFAULT_FROM_EMAIL)
        return bool(settings.EMAIL_HOST.strip() and 1 <= int(settings.EMAIL_PORT) <= 65535
                    and 0 < float(settings.EMAIL_TIMEOUT) <= 60
                    and bool(settings.EMAIL_USE_TLS) != bool(settings.EMAIL_USE_SSL)
                    and bool(settings.EMAIL_HOST_USER) == bool(settings.EMAIL_HOST_PASSWORD))
    except (ValidationError, ValueError, TypeError):
        return False


@dataclass(frozen=True)
class DeliveryResult:
    outcome: str
    reference: str = ""


class Provider:
    identifier = "unconfigured"
    production_ready = False

    def send(self, *, destination, subject, body, idempotency_key):
        # Known not to have made a network request; safe to retry after configuration.
        return DeliveryResult("REJECTED")


class SmsProvider(Provider):
    """Vendor adapters implement this contract; never report ambiguous rejection."""


class EmailProvider(Provider):
    """Email adapter contract, independent of the notification domain."""


class FakeSmsProvider(SmsProvider):
    identifier = "fake-sms"

    def send(self, *, destination, subject, body, idempotency_key):
        return DeliveryResult("ACCEPTED", "fake-" + sha256(idempotency_key.encode()).hexdigest()[:24])


class FakeEmailProvider(FakeSmsProvider, EmailProvider):
    identifier = "fake-email"


class DjangoEmailProvider(EmailProvider):
    identifier = "django-email"
    production_ready = True

    def send(self, *, destination, subject, body, idempotency_key):
        if not email_configuration_valid():
            return self._result("REJECTED", "CONFIGURATION")
        try:
            validate_email(destination)
            if not subject or "\n" in subject or "\r" in subject:
                raise ValidationError("Invalid subject")
        except ValidationError:
            return self._result("REJECTED", "VALIDATION")
        # SMTP cannot guarantee acceptance after a timeout. Never auto-retry it.
        try:
            count = EmailMessage(subject, body, settings.DEFAULT_FROM_EMAIL, [destination]).send(fail_silently=False)
        except (smtplib.SMTPAuthenticationError, smtplib.SMTPRecipientsRefused,
                smtplib.SMTPSenderRefused):
            return self._result("REJECTED", "SMTP_REJECTION")
        except Exception:
            return self._result("UNKNOWN", "TRANSPORT_UNCERTAIN")
        return self._result("ACCEPTED" if type(count) is int and count == 1 else "UNKNOWN", "BACKEND_RESULT")

    def _result(self, outcome, category):
        # Fixed categories only; no exception text, address, body or credentials.
        try:
            logger.info("email_attempt", extra={"delivery_outcome": outcome, "failure_category": category,
                                                "delivery_provider": self.identifier})
        except Exception:
            pass
        return DeliveryResult(outcome)


def get_provider(channel):
    path = getattr(settings, "COMMUNICATION_PROVIDERS", {}).get(channel)
    if not path:
        if settings.COMMUNICATIONS_PRODUCTION:
            raise ImproperlyConfigured("No production provider selected for this channel.")
        return Provider()
    provider_class = import_string(path)
    expected = {"SMS": SmsProvider, "EMAIL": EmailProvider}.get(channel)
    if expected is None or not isinstance(provider_class, type) or not issubclass(provider_class, expected):
        raise ImproperlyConfigured("Invalid channel adapter.")
    if settings.COMMUNICATIONS_PRODUCTION:
        if not settings.COMMUNICATIONS_ALLOW_EXTERNAL or not provider_class.production_ready:
            raise ImproperlyConfigured("Production delivery is disabled or uses an unsafe adapter.")
    elif provider_class.production_ready and provider_class is not DjangoEmailProvider:
        raise ImproperlyConfigured("External adapters require production enablement.")
    if provider_class is DjangoEmailProvider and not email_configuration_valid():
        raise ImproperlyConfigured("Invalid email deployment configuration.")
    provider = provider_class()
    if not isinstance(provider, Provider):
        raise ValueError("Invalid provider configuration")
    return provider
