"""Secure, opaque unsubscribe tokens.

Reuses the project's existing emailed-deep-link encryption (core.crypto, Fernet)
— the same mechanism the JD-approval "View JD" link uses — so no new crypto is
introduced here.

Encryption (not just signing) matters for this URL: django.core.signing produces
a base64 payload that anyone can decode, which would leak the recipient's address
to anyone who sees the link. Fernet makes the token genuinely opaque AND
authenticated, so the address is never exposed and a tampered/forged token is
rejected.

Unsubscribe links intentionally do not expire — a recipient must be able to opt
out from an old email.
"""

import base64
import hashlib

from django.conf import settings
from cryptography.fernet import Fernet, InvalidToken


def _fernet() -> Fernet:
    """Fernet keyed like core.crypto, but with its own derived key so an
    unsubscribe token can never be swapped in for an encrypted-id token."""
    key = getattr(settings, "UNSUBSCRIBE_ENCRYPTION_KEY", "") or ""
    if key:
        return Fernet(key.encode() if isinstance(key, str) else key)
    digest = hashlib.sha256(f"{settings.SECRET_KEY}::unsubscribe".encode()).digest()
    return Fernet(base64.urlsafe_b64encode(digest))


def sign_unsubscribe_token(email: str) -> str:
    """Encrypt the recipient address into an opaque, URL-safe token.

    Trailing '=' padding is stripped (some email clients mangle it in query
    strings); unsign_unsubscribe_token restores it — same as core.crypto.
    """
    email = (email or "").strip().lower()
    if not email:
        return ""
    return _fernet().encrypt(email.encode()).decode().rstrip("=")


def unsign_unsubscribe_token(token: str, max_age: int | None = None) -> str | None:
    """Return the email for a valid token, or None if missing/tampered/expired.

    `max_age` (seconds) is optional; when set, Fernet enforces the token age.
    Never raises — an invalid token is simply "not found".
    """
    if not token:
        return None
    padded = token + "=" * (-len(token) % 4)
    try:
        if max_age is not None:
            raw = _fernet().decrypt(padded.encode(), ttl=max_age).decode()
        else:
            raw = _fernet().decrypt(padded.encode()).decode()
    except (InvalidToken, ValueError, TypeError):
        return None
    return raw.strip().lower() or None


def mask_email(email: str) -> str:
    """Partially hide an address for display on the confirmation page, e.g.
    'candidate@example.com' -> 'c*******e@example.com'. Lets the recipient see
    which mailbox they're unsubscribing without echoing the full address back."""
    email = (email or "").strip()
    if "@" not in email:
        return "your email address"
    local, _, domain = email.partition("@")
    if len(local) <= 2:
        hidden = local[0] + "*" if local else "*"
    else:
        hidden = f"{local[0]}{'*' * (len(local) - 2)}{local[-1]}"
    return f"{hidden}@{domain}"


def unsubscribe_url(email: str) -> str:
    """Absolute frontend unsubscribe URL carrying only an opaque signed token."""
    base = (getattr(settings, "FRONTEND_URL", "") or "").rstrip("/")
    if not base:
        origins = getattr(settings, "CORS_ALLOWED_ORIGINS", None) or ["http://localhost:3000"]
        base = str(origins[0]).rstrip("/")
    return f"{base}/unsubscribe?token={sign_unsubscribe_token(email)}"
