"""TUI terminal utilities — capability detection and image encoding."""

from __future__ import annotations

import base64
import os
import struct
from dataclasses import dataclass
from typing import Any


@dataclass
class TerminalCapabilities:
    """Detected terminal capabilities."""

    images: str | None = None  # "kitty" | "iterm2" | None
    true_color: bool = False
    hyperlinks: bool = False


def detect_capabilities() -> TerminalCapabilities:
    """Detect terminal image and color capabilities."""
    term_program = os.environ.get("TERM_PROGRAM", "").lower()
    kitty_wid = os.environ.get("KITTY_WINDOW_ID")
    wezterm = os.environ.get("WEZTERM_PANE")
    iterm = os.environ.get("ITERM_SESSION_ID")
    colorterm = os.environ.get("COLORTERM", "").lower()

    images: str | None = None
    true_color = colorterm in ("truecolor", "24bit")
    hyperlinks = False

    # Kitty
    if kitty_wid or term_program == "kitty":
        images = "kitty"
        true_color = True
        hyperlinks = True
    # Ghostty (Kitty compatible)
    elif term_program == "ghostty":
        images = "kitty"
        true_color = True
        hyperlinks = True
    # WezTerm (Kitty compatible)
    elif wezterm:
        images = "kitty"
        true_color = True
        hyperlinks = True
    # iTerm2
    elif iterm or term_program in ("iterm.app", "iterm2"):
        images = "iterm2"
        true_color = True
        hyperlinks = True
    # VSCode / Alacritty — no images but may have true color
    elif term_program in ("vscode", "alacritty"):
        true_color = True

    return TerminalCapabilities(images=images, true_color=true_color, hyperlinks=hyperlinks)


# ---------------------------------------------------------------------------
# Image encoding
# ---------------------------------------------------------------------------

def encode_kitty(base64_data: str, columns: int | None = None, rows: int | None = None, image_id: int | None = None) -> str:
    """Encode image using Kitty graphics protocol."""
    CHUNK_SIZE = 4096

    # Build params
    params = "a=T,f=100,q=2"
    if columns is not None:
        params += f",c={columns}"
    if rows is not None:
        params += f",r={rows}"
    if image_id is not None:
        params += f",i={image_id}"

    chunks: list[str] = []
    data = base64_data
    offset = 0

    while offset < len(data):
        chunk = data[offset:offset + CHUNK_SIZE]
        offset += CHUNK_SIZE
        is_last = offset >= len(data)
        more = 0 if is_last else 1

        if not chunks:
            # First chunk includes full params
            chunks.append(f"\x1b_G{params},m={more};{chunk}\x1b\\")
        else:
            chunks.append(f"\x1b_Gm={more};{chunk}\x1b\\")

    return "".join(chunks)


def encode_iterm2(
    base64_data: str,
    width: str | int | None = None,
    height: str | int | None = None,
    name: str | None = None,
    preserve_aspect_ratio: bool = True,
    inline: bool = True,
) -> str:
    """Encode image using iTerm2 inline image protocol."""
    parts = ["size=" + str(len(base64.b64decode(base64_data)))]

    if width is not None:
        parts.append(f"width={width}")
    if height is not None:
        parts.append(f"height={height}")
    if name is not None:
        parts.append(f"name={base64.b64encode(name.encode()).decode()}")
    if not preserve_aspect_ratio:
        parts.append("preserveAspectRatio=0")
    if inline:
        parts.append("inline=1")

    params = ";".join(parts)
    return f"\x1b]1337;File={params}:{base64_data}\x07"


# ---------------------------------------------------------------------------
# Image dimension parsing
# ---------------------------------------------------------------------------

def get_png_dimensions(base64_data: str) -> tuple[int, int] | None:
    """Read width and height from PNG header."""
    try:
        raw = base64.b64decode(base64_data[:64])
        if len(raw) < 24:
            return None
        width = struct.unpack(">I", raw[16:20])[0]
        height = struct.unpack(">I", raw[20:24])[0]
        return (width, height)
    except Exception:
        return None


def get_jpeg_dimensions(base64_data: str) -> tuple[int, int] | None:
    """Read width and height from JPEG SOF marker."""
    try:
        raw = base64.b64decode(base64_data)
        i = 2  # Skip SOI marker
        while i < len(raw) - 1:
            if raw[i] != 0xFF:
                break
            marker = raw[i + 1]
            if marker in (0xC0, 0xC1, 0xC2):
                if i + 9 < len(raw):
                    height = struct.unpack(">H", raw[i + 5:i + 7])[0]
                    width = struct.unpack(">H", raw[i + 7:i + 9])[0]
                    return (width, height)
                break
            # Skip marker segment
            if i + 3 < len(raw):
                seg_len = struct.unpack(">H", raw[i + 2:i + 4])[0]
                i += 2 + seg_len
            else:
                break
        return None
    except Exception:
        return None


def get_gif_dimensions(base64_data: str) -> tuple[int, int] | None:
    """Read width and height from GIF header."""
    try:
        raw = base64.b64decode(base64_data[:32])
        if len(raw) < 10:
            return None
        width = struct.unpack("<H", raw[6:8])[0]
        height = struct.unpack("<H", raw[8:10])[0]
        return (width, height)
    except Exception:
        return None


def get_webp_dimensions(base64_data: str) -> tuple[int, int] | None:
    """Read width and height from WebP header."""
    try:
        raw = base64.b64decode(base64_data[:64])
        if len(raw) < 30:
            return None
        # Check RIFF/WEBP header
        if raw[:4] != b"RIFF" or raw[8:12] != b"WEBP":
            return None
        chunk_type = raw[12:16]
        if chunk_type == b"VP8 ":
            # Lossy VP8
            width = struct.unpack("<H", raw[26:28])[0] & 0x3FFF
            height = struct.unpack("<H", raw[28:30])[0] & 0x3FFF
            return (width, height)
        elif chunk_type == b"VP8L":
            # Lossless
            bits = struct.unpack("<I", raw[21:25])[0]
            width = (bits & 0x3FFF) + 1
            height = ((bits >> 14) & 0x3FFF) + 1
            return (width, height)
        elif chunk_type == b"VP8X":
            # Extended
            width = (raw[24] | (raw[25] << 8) | (raw[26] << 16)) + 1
            height = (raw[27] | (raw[28] << 8) | (raw[29] << 16)) + 1
            return (width, height)
        return None
    except Exception:
        return None


def get_image_dimensions(base64_data: str, mime_type: str | None = None) -> tuple[int, int] | None:
    """Get image dimensions from base64 data.

    Args:
        base64_data: Base64-encoded image data.
        mime_type: Optional MIME type hint.
    """
    if mime_type:
        mt = mime_type.lower()
        if "png" in mt:
            return get_png_dimensions(base64_data)
        if "jpeg" in mt or "jpg" in mt:
            return get_jpeg_dimensions(base64_data)
        if "gif" in mt:
            return get_gif_dimensions(base64_data)
        if "webp" in mt:
            return get_webp_dimensions(base64_data)

    # Try all formats
    for fn in (get_png_dimensions, get_jpeg_dimensions, get_gif_dimensions, get_webp_dimensions):
        result = fn(base64_data)
        if result:
            return result
    return None
