"""Shared helpers for copying collection landing assets into channel storage."""

from __future__ import annotations

import hashlib
import mimetypes
import re
from dataclasses import dataclass
from pathlib import PurePosixPath
from typing import Optional
from urllib.parse import unquote, urlparse

import requests

from db.image_urls import normalize_external_image_url


MAX_COLLECTION_ASSET_BYTES = 25 * 1024 * 1024
_SAFE_NAME_RE = re.compile(r"[^a-zA-Z0-9._-]+")


@dataclass(frozen=True)
class CollectionAsset:
    source_url: str
    content: bytes
    content_sha256: str
    filename: str
    mime_type: str
    is_image: bool


def fetch_collection_asset(source_url: str) -> CollectionAsset:
    """Download and identify one image/PDF collection asset."""
    normalized = normalize_external_image_url(str(source_url or "").strip())
    if not normalized:
        raise ValueError("Collection asset URL is empty")
    response = requests.get(normalized, timeout=120, allow_redirects=True)
    response.raise_for_status()
    content = response.content
    if not content:
        raise ValueError(f"Collection asset returned an empty body: {normalized}")
    if len(content) > MAX_COLLECTION_ASSET_BYTES:
        raise ValueError(
            f"Collection asset exceeds {MAX_COLLECTION_ASSET_BYTES // (1024 * 1024)} MB: {normalized}"
        )

    declared = str(response.headers.get("Content-Type") or "").split(";", 1)[0].strip().lower()
    path_name = PurePosixPath(unquote(urlparse(normalized).path)).name
    guessed = mimetypes.guess_type(path_name)[0] or ""
    supported = {"image/jpeg", "image/png", "image/gif", "image/webp", "application/pdf"}
    sniffed = _sniff_mime_type(content)
    mime_type = declared if declared in supported else guessed if guessed in supported else sniffed
    is_image = mime_type.startswith("image/")
    if not is_image and mime_type != "application/pdf":
        raise ValueError(f"Unsupported collection asset type {mime_type or 'unknown'}: {normalized}")
    if is_image and not _matches_image_signature(content, mime_type):
        raise ValueError(f"Collection image content does not match {mime_type}: {normalized}")

    digest = hashlib.sha256(content).hexdigest()
    extension = mimetypes.guess_extension(mime_type) or PurePosixPath(path_name).suffix
    if mime_type == "image/jpeg":
        extension = ".jpg"
    base = PurePosixPath(path_name).stem or ("image" if is_image else "document")
    safe_base = _SAFE_NAME_RE.sub("-", base).strip("-._")[:80] or "asset"
    filename = f"{safe_base}-{digest[:16]}{extension or ''}"
    return CollectionAsset(
        source_url=normalized,
        content=content,
        content_sha256=digest,
        filename=filename,
        mime_type=mime_type,
        is_image=is_image,
    )


def _matches_image_signature(content: bytes, mime_type: str) -> bool:
    signatures = {
        "image/jpeg": content.startswith(b"\xff\xd8\xff"),
        "image/png": content.startswith(b"\x89PNG\r\n\x1a\n"),
        "image/gif": content.startswith((b"GIF87a", b"GIF89a")),
        "image/webp": content.startswith(b"RIFF") and content[8:12] == b"WEBP",
    }
    return signatures.get(mime_type, False)


def _sniff_mime_type(content: bytes) -> str:
    if content.startswith(b"%PDF-"):
        return "application/pdf"
    for mime_type in ("image/jpeg", "image/png", "image/gif", "image/webp"):
        if _matches_image_signature(content, mime_type):
            return mime_type
    return ""
