"""Private storage + signed-URL helpers for candidate résumés.

Résumés are PII, so they are stored OUTSIDE the web root (settings.PRIVATE_MEDIA_ROOT)
and are never served statically. They are reachable only through a signed,
permission-checked download endpoint.

`ResumeStorage` is @deconstructible with NO recorded arguments, so migrations do
not bake in an environment-specific absolute path — the location is resolved from
settings at runtime.
"""
import os
import re
from uuid import uuid4

from django.conf import settings
from django.core import signing
from django.core.files.storage import FileSystemStorage
from django.urls import reverse
from django.utils.deconstruct import deconstructible

_SALT = "candidates.resume"


def attachment_path(instance, filename: str) -> str:
    """OpenCATS-style organized path for a candidate attachment:
    resumes/<candidate_id>/<uuid>__<sanitized-original>. Kept under the same
    private store so files stay outside the web root."""
    base = os.path.basename(filename or "file")
    safe = re.sub(r"[^A-Za-z0-9._-]", "_", base)[:120] or "file"
    cid = getattr(instance, "candidate_id", None) or "misc"
    return f"resumes/{cid}/{uuid4().hex}__{safe}"


@deconstructible
class ResumeStorage(FileSystemStorage):
    def __init__(self):
        # location read from settings at runtime; base_url=None so .url is unused
        super().__init__(location=str(settings.PRIVATE_MEDIA_ROOT), base_url=None)

    def deconstruct(self):
        # Record no args -> migration writes ResumeStorage() and resolves settings live.
        return ("apps.candidates.resume_storage.ResumeStorage", [], {})


resume_storage = ResumeStorage()


def sign_resume_name(name: str) -> str:
    """Return a signed, timestamped token for a stored résumé file name."""
    return signing.dumps({"n": name}, salt=_SALT)


def unsign_resume_name(token: str, max_age: int | None = None) -> str | None:
    """Validate a token and return the file name, or None if invalid/expired."""
    if max_age is None:
        max_age = getattr(settings, "RESUME_URL_TTL", 3600)
    try:
        data = signing.loads(token, salt=_SALT, max_age=max_age)
        return data.get("n")
    except signing.BadSignature:
        return None


def signed_resume_url(request, name):
    """Absolute, signed, short-lived download URL for a stored résumé `name`
    (storage-relative path like 'resumes/5_..._resume.pdf'). Returns None if empty."""
    if not name:
        return None
    path = reverse("candidates:resume-download", args=[sign_resume_name(str(name))])
    return request.build_absolute_uri(path) if request is not None else path
