"""
Live View visual helpers: image resize/encode and convenience emitter.
Used by tool modules to send preview images and text to the Live View panel.
"""
from __future__ import annotations

import base64
import io
from urllib.parse import urlparse

from tools._logging import emit_live_view_event


def image_to_preview_base64(
    pil_image_or_bytes,
    max_width: int = 400,
    quality: int = 82,
) -> str | None:
    """Resize a PIL Image or raw bytes to max_width (keeping aspect ratio) and return JPEG base64.

    Returns None on any failure so callers can safely skip the preview.
    PIL is imported lazily so modules that never call this pay no import cost.
    """
    try:
        from PIL import Image as _PILImage

        if isinstance(pil_image_or_bytes, bytes):
            img = _PILImage.open(io.BytesIO(pil_image_or_bytes))
        else:
            img = pil_image_or_bytes

        if img.width > max_width:
            ratio = max_width / img.width
            new_h = max(1, int(img.height * ratio))
            img = img.resize((max_width, new_h), _PILImage.LANCZOS)

        buf = io.BytesIO()
        has_alpha = img.mode in ("RGBA", "LA", "PA") or (img.mode == "P" and "transparency" in img.info)
        if has_alpha:
            img.save(buf, format="PNG", optimize=True)
        else:
            if img.mode != "RGB":
                img = img.convert("RGB")
            img.save(buf, format="JPEG", quality=quality, optimize=True)
        return base64.b64encode(buf.getvalue()).decode("ascii")
    except Exception:
        return None


def bytes_to_preview_base64(
    raw_bytes: bytes,
    max_width: int = 400,
    quality: int = 82,
) -> str | None:
    """Convenience wrapper: raw image bytes -> resized base64. Returns None on failure."""
    return image_to_preview_base64(raw_bytes, max_width=max_width, quality=quality)


def emit_live_view_preview(
    message: str,
    *,
    tool_name: str | None = None,
    progress_pct: int | None = None,
    progress_indeterminate: bool = False,
    preview_text: str | None = None,
    preview_image_base64: str | None = None,
    url: str | None = None,
    duration_sec: float | None = None,
    detail: str | None = None,
    error_detail: str | None = None,
    page: int | None = None,
    total_pages: int | None = None,
    file_path: str | None = None,
) -> None:
    """Build and emit a rich Live View event with optional image/text preview.

    Uses type "preview" when an image or text is present, otherwise "progress" when
    progress_pct is set or progress_indeterminate is True, else "status".
    ``progress_indeterminate=True`` shows an indeterminate (animated) bar with no percentage.

    Optional rich fields (shown in Live View when present):
    - duration_sec: e.g. "Completed in 12s"
    - detail: extra info (truncated to 1000 chars)
    - error_detail: for errors, expandable detail (truncated to 1000 chars)
    - page / total_pages: e.g. "Page 3 of 5"
    - file_path: path or filename (truncated to 200 chars)
    """
    has_visual = preview_image_base64 is not None or preview_text is not None
    if has_visual:
        etype = "preview"
    elif progress_pct is not None or progress_indeterminate:
        etype = "progress"
    else:
        etype = "status"

    event: dict = {"type": etype, "message": message}
    if tool_name:
        event["tool_name"] = tool_name
    if progress_indeterminate:
        event["progress_indeterminate"] = True
    elif progress_pct is not None:
        event["progress_pct"] = max(0, min(100, int(progress_pct)))
    if preview_text is not None:
        event["preview_text"] = preview_text[:2000]
    if preview_image_base64 is not None:
        event["preview_image_base64"] = preview_image_base64
    if url:
        event["url"] = url
    if duration_sec is not None:
        event["duration_sec"] = round(duration_sec, 1)
    if detail is not None:
        event["detail"] = detail[:1000]
    if error_detail is not None:
        event["error_detail"] = error_detail[:1000]
    if page is not None:
        event["page"] = int(page)
    if total_pages is not None:
        event["total_pages"] = int(total_pages)
    if file_path is not None:
        event["file_path"] = file_path[:200]
    emit_live_view_event(event)


def get_webpage_thumbnail(html_content: str, page_url: str) -> bytes | None:
    """Extract og:image or twitter:image from HTML; fallback to Google favicon. Returns image bytes or None."""
    try:
        from bs4 import BeautifulSoup
        import requests as _requests

        soup = BeautifulSoup(html_content[:50000], "html.parser")
        img_url = None
        for meta in soup.find_all("meta"):
            prop = (meta.get("property") or meta.get("name") or "").lower()
            content = (meta.get("content") or "").strip()
            if prop in ("og:image", "twitter:image") and content:
                img_url = content
                break

        if img_url:
            if img_url.startswith("//"):
                img_url = "https:" + img_url
            elif img_url.startswith("/"):
                parsed = urlparse(page_url)
                img_url = f"{parsed.scheme}://{parsed.netloc}{img_url}"
            try:
                resp = _requests.get(img_url, timeout=5, stream=True, headers={
                    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
                })
                if resp.status_code == 200:
                    data = resp.content[:2 * 1024 * 1024]
                    if len(data) > 500:
                        return data
            except Exception:
                pass

        # Fallback: favicon via Google service
        domain = urlparse(page_url).netloc
        if domain:
            try:
                fav_url = f"https://www.google.com/s2/favicons?domain={domain}&sz=64"
                resp = _requests.get(fav_url, timeout=4)
                if resp.status_code == 200 and len(resp.content) > 100:
                    return resp.content
            except Exception:
                pass
    except Exception:
        pass
    return None


def get_webpage_title(html_content: str) -> str:
    """Extract <title> from HTML, or return empty string."""
    try:
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html_content[:20000], "html.parser")
        tag = soup.find("title")
        if tag and tag.string:
            return tag.string.strip()[:120]
    except Exception:
        pass
    return ""
