"""Panzoom-backed document preview component."""

from __future__ import annotations

from functools import lru_cache
from pathlib import Path
from urllib.parse import quote
from uuid import uuid4

from nicegui import ui

from transcription.config import get_settings
from transcription.db.models import Source

PANGOZOOM_CDN_URL = "https://unpkg.com/@panzoom/panzoom@4.6.2/dist/panzoom.min.js"
UPLOADS_URL_PREFIX = "/uploads"


def render_document_panzoom(*, source: Source) -> None:
    """Render a source preview with pan and zoom interactions."""
    _register_panzoom_assets()

    host_id = f"document-panzoom-{uuid4().hex}"
    document_url = _document_url(source)
    document_kind = _document_kind(source)

    with ui.card().classes("w-full q-pa-md vibe-card"):
        with ui.row().classes("w-full items-center justify-between no-wrap"):
            ui.label("Document preview").classes("text-subtitle1 text-weight-medium")
            ui.label(source.filename).classes("text-caption vibe-text-muted ellipsis").style(
                "max-width: 60%; text-align: right;"
            )

        with (
            ui.element("div").classes("w-full document-panzoom-host rounded-borders q-mt-md")
            # .style(f"height: {height};")
        ) as host:
            host.props(f"id={host_id}")
            with ui.element("div").classes("document-panzoom-surface"):
                if document_kind == "pdf":
                    ui.html(
                        f'<iframe class="document-panzoom-iframe" '
                        f'src="{document_url}" title="{source.filename}" '
                        "data-panzoom-target></iframe>"
                    )
                else:
                    ui.html(
                        f'<img class="document-panzoom-media" '
                        f'src="{document_url}" alt="{source.filename}" '
                        "data-panzoom-target data-panzoom-media />"
                    )

    _attach_panzoom(host_id)


@lru_cache(maxsize=1)
def _register_panzoom_assets() -> None:
    ui.add_head_html(
        f'<script src="{PANGOZOOM_CDN_URL}"></script>',
        shared=True,
    )
    ui.add_head_html(
        """
        <style>
            .document-panzoom-host {
                overflow: hidden;
                touch-action: none;
            }

            .document-panzoom-surface {
                width: 100%;
                height: 100%;
                display: flex;
                align-items: flex-start;
                justify-content: flex-start;
            }

            .document-panzoom-media {
                width: auto;
                height: auto;
                display: block;
                max-width: 100%;
                max-height: 100%;
                user-select: none;
                -webkit-user-drag: none;
            }

            .document-panzoom-iframe {
                width: 100%;
                height: 100%;
                border: 0;
                pointer-events: none;
                background: var(--theme-surface-raised);
            }
        </style>
        """,
        shared=True,
    )


def _document_url(source: Source) -> str:
    file_path = Path(source.file_path)
    upload_dir = get_settings().upload_dir

    relative_path: Path
    try:
        relative_path = file_path.resolve().relative_to(upload_dir.resolve())
    except ValueError:
        parts = file_path.parts
        if "uploads" in parts:
            uploads_index = parts.index("uploads")
            relative_path = Path(*parts[uploads_index + 1 :])
        else:
            relative_path = Path(file_path.name)

    encoded_relative_path = "/".join(quote(part) for part in relative_path.parts)
    return f"{UPLOADS_URL_PREFIX}/{encoded_relative_path}"


def _document_kind(source: Source) -> str:
    suffix = Path(source.file_path).suffix.lower()
    if suffix == ".pdf":
        return "pdf"
    return "image"


def _attach_panzoom(host_id: str) -> None:
    ui.run_javascript(
        f"""
        (function() {{
            if (!window.Panzoom) return;
            window.__transcriptionPanzoom = window.__transcriptionPanzoom || {{}};
            const host = document.getElementById({host_id!r});
            if (!host) return;
            const target = host.querySelector('[data-panzoom-target]');
            const media = host.querySelector('[data-panzoom-media]');
            if (!target) return;

            const cleanup = () => {{
                const existing = window.__transcriptionPanzoom[{host_id!r}];
                if (existing?.resizeObserver) existing.resizeObserver.disconnect();
                if (existing?.wheelHandler) host.removeEventListener('wheel', existing.wheelHandler);
                if (existing?.instance) existing.instance.destroy();
            }};

            const computeFitScale = () => {{
                const hostRect = host.getBoundingClientRect();
                if (hostRect.width <= 0 || hostRect.height <= 0) return null;
                return 1;
            }};

            const buildInstance = () => {{
                cleanup();

                const fitScale = computeFitScale();
                if (fitScale === null) return false;

                const minScale = Math.min(fitScale, 0.01);
                const instance = Panzoom(target, {{
                    startX: 0,
                    startY: 0,
                    startScale: fitScale,
                    minScale: minScale,
                    maxScale: 256,
                    step: 0.2,
                    roundPixels: false,
                    panOnlyWhenZoomed: true,
                    overflow: 'hidden',
                }});

                const wheelHandler = (event) => instance.zoomWithWheel(event);
                host.addEventListener('wheel', wheelHandler, {{ passive: false }});

                requestAnimationFrame(() => {{
                    instance.reset({{ animate: false }});
                }});

                const resizeObserver = new ResizeObserver(() => {{
                    const nextFitScale = computeFitScale();
                    if (nextFitScale === null) return;
                    instance.setOptions({{
                        startScale: nextFitScale,
                        minScale: Math.min(nextFitScale, 0.01),
                    }});
                    instance.reset({{ animate: false }});
                }});
                resizeObserver.observe(host);

                window.__transcriptionPanzoom[{host_id!r}] = {{
                    instance,
                    wheelHandler,
                    resizeObserver,
                }};
                return true;
            }};

            const initWhenReady = (retries = 15) => {{
                if (buildInstance()) return;
                if (retries <= 0) return;
                requestAnimationFrame(() => initWhenReady(retries - 1));
            }};

            if (media && media.tagName === 'IMG' && !media.complete) {{
                media.addEventListener('load', () => initWhenReady(), {{ once: true }});
                return;
            }}

            initWhenReady();
        }})();
        """
    )
