from __future__ import annotations

import re
import secrets
from pathlib import Path

from fastapi import UploadFile

# backend/uploads/avatars
UPLOAD_ROOT = Path(__file__).resolve().parents[1] / "uploads"
AVATAR_DIR = UPLOAD_ROOT / "avatars"

ALLOWED_MIME = {
    "image/jpeg": ".jpg",
    "image/jpg": ".jpg",
    "image/png": ".png",
    "image/webp": ".webp",
}
MAX_BYTES = 5 * 1024 * 1024  # 5 MB


def ensure_dirs() -> None:
    AVATAR_DIR.mkdir(parents=True, exist_ok=True)


def public_avatar_url(filename: str) -> str:
    return f"/api/uploads/avatars/{filename}"


def filename_from_url(url: str | None) -> str | None:
    if not url:
        return None
    name = url.rstrip("/").split("/")[-1]
    if not name or ".." in name or "/" in name:
        return None
    return name


async def save_avatar_file(file: UploadFile, *, platform: str, email: str) -> str:
    """Validate and store an avatar. Returns public URL path."""
    ensure_dirs()
    content_type = (file.content_type or "").lower().strip()
    if content_type not in ALLOWED_MIME:
        raise ValueError("Please upload a JPG, PNG, or WEBP image")

    data = await file.read()
    if not data:
        raise ValueError("Empty file")
    if len(data) > MAX_BYTES:
        raise ValueError("Image must be 5 MB or smaller")

    # Basic magic-byte checks
    if content_type in ("image/jpeg", "image/jpg") and not data.startswith(b"\xff\xd8"):
        raise ValueError("Invalid JPEG image")
    if content_type == "image/png" and not data.startswith(b"\x89PNG\r\n\x1a\n"):
        raise ValueError("Invalid PNG image")
    if content_type == "image/webp" and not (data[0:4] == b"RIFF" and data[8:12] == b"WEBP"):
        raise ValueError("Invalid WEBP image")

    ext = ALLOWED_MIME[content_type]
    safe_platform = re.sub(r"[^a-z]", "", platform.lower())[:12] or "user"
    safe_email = re.sub(r"[^a-z0-9]", "", email.lower())[:40] or "user"
    filename = f"{safe_platform}_{safe_email}_{secrets.token_hex(8)}{ext}"
    path = AVATAR_DIR / filename
    path.write_bytes(data)
    return public_avatar_url(filename)


def delete_avatar_file(url: str | None) -> None:
    name = filename_from_url(url)
    if not name:
        return
    path = AVATAR_DIR / name
    try:
        if path.is_file() and path.resolve().parent == AVATAR_DIR.resolve():
            path.unlink(missing_ok=True)
    except OSError:
        pass
