"""Email backends.

R2-B spike: only ConsoleEmailBackend is functional. SendGrid is a stub
intentionally left to be wired in R3-C.
"""

from __future__ import annotations

import json
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional, Protocol

try:
    from sendgrid import SendGridAPIClient
    from sendgrid.helpers.mail import Mail, ReplyTo
except ImportError:  # pragma: no cover — SendGrid installed separately.
    SendGridAPIClient = None  # type: ignore[misc]
    Mail = None  # type: ignore[misc]
    ReplyTo = None  # type: ignore[misc]

from app.config import settings


# Repository convention: backend/var/email_outbox.jsonl
# This module lives at backend/app/services/email.py — go up two levels to
# reach backend/, then var/.
_BACKEND_ROOT = Path(__file__).resolve().parent.parent.parent
OUTBOX_PATH = _BACKEND_ROOT / "var" / "email_outbox.jsonl"


class EmailBackend(Protocol):
    def send(
        self,
        to: str,
        subject: str,
        html: str,
        text: Optional[str] = None,
    ) -> None:
        ...


class ConsoleEmailBackend:
    """Dev/spike backend. Prints to stdout AND appends a JSON line to
    backend/var/email_outbox.jsonl so the round-trip can be inspected
    after the fact.
    """

    def send(
        self,
        to: str,
        subject: str,
        html: str,
        text: Optional[str] = None,
    ) -> None:
        record = {
            "ts": datetime.now(timezone.utc).isoformat(),
            "to": to,
            "subject": subject,
            "html": html,
            "text": text,
        }
        print(
            f"[ConsoleEmailBackend] -> {to} | {subject}",
            file=sys.stdout,
            flush=True,
        )
        OUTBOX_PATH.parent.mkdir(parents=True, exist_ok=True)
        with OUTBOX_PATH.open("a", encoding="utf-8") as f:
            f.write(json.dumps(record) + "\n")


class SendGridEmailBackend:
    def __init__(self) -> None:
        self._client = SendGridAPIClient(settings.SENDGRID_API_KEY)
        self._from_email = settings.SENDGRID_FROM_EMAIL
        self._reply_to = settings.SENDGRID_REPLY_TO

    def send(
        self,
        to: str,
        subject: str,
        html: str,
        text: Optional[str] = None,
    ) -> None:
        message = Mail(
            from_email=self._from_email,
            to_emails=to,
            subject=subject,
            html_content=html,
            plain_text_content=text,
        )
        message.reply_to = ReplyTo(self._reply_to)
        response = self._client.send(message)
        if response.status_code >= 400:
            raise RuntimeError(
                f"SendGrid error {response.status_code}: {response.body}"
            )


def get_email_backend() -> EmailBackend:
    backend = (settings.EMAIL_BACKEND or "console").lower()
    if backend == "sendgrid":
        return SendGridEmailBackend()
    return ConsoleEmailBackend()
