from __future__ import annotations

import hashlib
import re
import secrets
from pathlib import Path

from fastapi import UploadFile

from app.config import settings
from app.services.avatars import UPLOAD_ROOT

DOCUMENT_DIR = UPLOAD_ROOT / "documents"

ALLOWED_MIME = {
    "application/pdf": ".pdf",
    "image/jpeg": ".jpg",
    "image/jpg": ".jpg",
    "image/png": ".png",
    "image/webp": ".webp",
}

MAX_BYTES = settings.max_document_size_mb * 1024 * 1024


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


def max_size_label() -> str:
    return f"{settings.max_document_size_mb} MB"


async def save_document_file(
    file: UploadFile,
    *,
    client_id: str,
) -> tuple[str, str, int, str]:
    """
    Validate and store an uploaded document.
    Returns (storage_filename, mime_type, size_bytes, sha256_hex).
    """
    ensure_dirs()
    content_type = (file.content_type or "").lower().strip()
    if content_type not in ALLOWED_MIME:
        raise ValueError("Please upload a PDF, JPG, PNG, or WEBP file")

    data = await file.read()
    if not data:
        raise ValueError("Empty file")
    if len(data) > MAX_BYTES:
        raise ValueError(f"File must be {settings.max_document_size_mb} MB or smaller")

    if content_type == "application/pdf" and not data.startswith(b"%PDF"):
        raise ValueError("Invalid PDF document")
    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_client = re.sub(r"[^a-z0-9_-]", "", client_id.lower())[:32] or "client"
    filename = f"{safe_client}_{secrets.token_hex(12)}{ext}"
    path = DOCUMENT_DIR / filename
    path.write_bytes(data)
    file_hash = hashlib.sha256(data).hexdigest()
    return filename, content_type, len(data), file_hash


def resolve_document_path(storage_key: str | None) -> Path | None:
    if not storage_key:
        return None
    name = storage_key.rstrip("/").split("/")[-1]
    if not name or ".." in name or "/" in name or "\\" in name:
        return None
    path = DOCUMENT_DIR / name
    try:
        if path.is_file() and path.resolve().parent == DOCUMENT_DIR.resolve():
            return path
    except OSError:
        return None
    return None


def delete_document_file(storage_key: str | None) -> None:
    path = resolve_document_path(storage_key)
    if path:
        try:
            path.unlink(missing_ok=True)
        except OSError:
            pass
