"""Telegram media utilities - constants, URL helpers, and size splitting."""

from __future__ import annotations

import logging
import re
from typing import Any, Final
from urllib.parse import urlparse

_LOGGER = logging.getLogger(__name__)

# Telegram constants
TELEGRAM_API_BASE_URL: Final = "https://api.telegram.org/bot"
TELEGRAM_MAX_PHOTO_SIZE: Final = 10 * 1024 * 1024  # 10 MB
TELEGRAM_MAX_VIDEO_SIZE: Final = 50 * 1024 * 1024  # 50 MB
TELEGRAM_MAX_DIMENSION_SUM: Final = 10000
# Telegram message-text limit (sendMessage) and caption limit
# (sendPhoto/sendVideo/sendDocument/first item of sendMediaGroup).
TELEGRAM_MAX_TEXT_LENGTH: Final = 4096
TELEGRAM_MAX_CAPTION_LENGTH: Final = 1024

# Strict canonical-UUID pattern (8-4-4-4-12) for asset IDs. The previous
# loose ``[a-f0-9-]{36}`` matched 36 hyphens / arbitrary digit groupings,
# which could collide across providers when used as a cache key.
_ASSET_ID_PATTERN = re.compile(
    r"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$",
    re.IGNORECASE,
)
# Cache key: "host:uuid" or bare "uuid"
_ASSET_CACHE_KEY_PATTERN = re.compile(
    r"^(?:[^:]+:)?[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$",
    re.IGNORECASE,
)

# URL patterns to extract asset IDs (generic enough for Immich-style URLs)
_ASSET_ID_URL_PATTERNS = [
    re.compile(r"/api/assets/([a-f0-9-]{36})/(?:original|thumbnail|video)"),
    re.compile(r"/share/[^/]+/photos/([a-f0-9-]{36})"),
]


def is_asset_id(value: str) -> bool:
    """Check if a string is a valid asset ID (UUID format)."""
    return bool(_ASSET_ID_PATTERN.match(value))


def is_asset_cache_key(value: str) -> bool:
    """Check if a string is an asset cache key (bare UUID or host:UUID)."""
    return bool(_ASSET_CACHE_KEY_PATTERN.match(value))


def asset_id_from_cache_key(key: str) -> str:
    """Extract bare asset ID from a cache key (strips host: prefix if present)."""
    idx = key.find(":")
    return key[idx + 1:] if idx != -1 else key


def extract_asset_id_from_url(url: str) -> str | None:
    """Extract host-qualified asset cache key (host:uuid) from a URL."""
    if not url:
        return None
    for pattern in _ASSET_ID_URL_PATTERNS:
        match = pattern.search(url)
        if match:
            host = urlparse(url).hostname or ""
            return f"{host}:{match.group(1)}" if host else match.group(1)
    return None


def build_telegram_asset_entry(
    *,
    url: str,
    media_type: str,
    api_key: str | None = None,
    internal_url: str = "",
    external_url: str = "",
    cache_key: str | None = None,
) -> dict[str, Any] | None:
    """Build a ``TelegramClient.send_notification`` asset dict from raw fields.

    Shared by the notification dispatcher and provider command handlers so
    both paths agree on media typing, URL rewriting, and auth headers. In
    particular: video assets MUST be typed ``"video"`` and point at a real
    video endpoint (e.g. Immich ``/video/playback``) — if they are sent as
    ``"photo"`` pointing at a thumbnail URL, Telegram delivers a still image
    for every video in a media group and the user sees a dead poster frame
    instead of a playable clip.

    Args:
        url: Source URL for the asset bytes. Prefer a transcoded/preview
            URL for videos (``/video/playback``) and a preview-sized
            thumbnail for photos.
        media_type: Case-insensitive type token. Accepts ``"video"``/
            ``"VIDEO"``/``MediaType.VIDEO`` or any photo-like string.
        api_key: Optional API key. Attached as ``x-api-key`` iff the URL is
            served by one of the provider hosts in ``internal_url`` /
            ``external_url`` (prevents leaking the key to unrelated hosts).
        internal_url: LAN-facing provider URL. Used to rewrite
            ``external_url`` prefixes so Docker-host downloads stay on the
            LAN instead of egressing to the public domain.
        external_url: Public provider URL the notification URL was built
            from. Only used for the LAN rewrite and the api-key scope check.
        cache_key: Optional explicit cache key. Providers whose URLs don't
            embed a stable asset id (Google Photos) pass one through so the
            file_id cache still works.

    Returns ``None`` iff ``url`` is empty.
    """
    if not url:
        return None

    if internal_url and external_url and url.startswith(external_url):
        url = internal_url + url[len(external_url):]

    normalized_type = str(media_type or "").lower()
    entry_type = "video" if normalized_type == "video" else "photo"

    headers: dict[str, str] = {}
    provider_urls = [u for u in (internal_url, external_url) if u]
    if api_key and (not provider_urls or any(url.startswith(u) for u in provider_urls)):
        headers["x-api-key"] = api_key

    entry: dict[str, Any] = {"url": url, "type": entry_type, "headers": headers}
    if cache_key:
        entry["cache_key"] = cache_key
    return entry


def split_media_by_upload_size(
    media_items: list[tuple], max_upload_size: int
) -> list[list[tuple]]:
    """Split media items into sub-groups respecting upload size limit."""
    if not media_items:
        return []

    groups: list[list[tuple]] = []
    current_group: list[tuple] = []
    current_size = 0

    for item in media_items:
        media_ref = item[1]
        is_cached = item[4]
        item_size = 0 if is_cached else (len(media_ref) if isinstance(media_ref, bytes) else 0)

        if current_group and current_size + item_size > max_upload_size:
            groups.append(current_group)
            current_group = []
            current_size = 0

        current_group.append(item)
        current_size += item_size

    if current_group:
        groups.append(current_group)

    return groups


def check_photo_limits(
    data: bytes,
) -> tuple[bool, str | None, int | None, int | None]:
    """Check if photo data exceeds Telegram photo limits."""
    if len(data) > TELEGRAM_MAX_PHOTO_SIZE:
        return (True, f"size {len(data)} bytes exceeds {TELEGRAM_MAX_PHOTO_SIZE} bytes limit", None, None)

    try:
        from PIL import Image
        import io

        img = Image.open(io.BytesIO(data))
        width, height = img.size
        dimension_sum = width + height

        if dimension_sum > TELEGRAM_MAX_DIMENSION_SUM:
            return (True, f"dimensions {width}x{height} (sum={dimension_sum}) exceed {TELEGRAM_MAX_DIMENSION_SUM} limit", width, height)

        return False, None, width, height
    except ImportError:
        return False, None, None, None
    except (OSError, ValueError, MemoryError) as exc:
        # PIL surfaces ``UnidentifiedImageError`` (subclass of OSError),
        # truncated-image / decompression-bomb errors here. Log so a
        # corrupt asset isn't silently passed to Telegram and rejected
        # downstream with a less actionable error.
        _LOGGER.warning("check_photo_limits: failed to inspect image (%d bytes): %s", len(data), exc)
        return False, None, None, None
