"""LLMHosts TUI Dashboard -- real-time proxy monitoring.

Launch with ``LLMHostsDashboard.run()`` or from the CLI via ``llmhosts dashboard``.

Layout::

    ┌──────────────────────────────────────────────────────────┐
    │  LLMHosts v0.1.0         Uptime: 2h 15m     [Q]uit     │
    ├────────────────────┬─────────────────────────────────────┤
    │   SAVINGS          │   BACKENDS                         │
    │   $83.50 saved     │   ● Ollama: 3 models (healthy)    │
    │   64% reduction    │   ● OpenAI: connected              │
    │   142 requests     │   ● Anthropic: connected           │
    ├────────────────────┴─────────────────────────────────────┤
    │   RECENT REQUESTS                                        │
    │   12:34:56 │ gpt-4o-mini → llama3.2:8b │ local │ $0.00 │
    │   12:34:52 │ claude-3.5  → anthropic    │ cloud │ $0.04 │
    │   12:34:48 │ gpt-4o-mini → llama3.2:8b │ cache │ $0.00 │
    ├──────────────────────────────────────────────────────────┤
    │   METRICS          │ Cache: 72% hit   │ Avg: 145ms      │
    │   RPM: 12.3        │ Local: 85%       │ Tokens: 45.2K   │
    └──────────────────────────────────────────────────────────┘

Updates every 2 seconds via ``set_interval`` timer.
"""

from __future__ import annotations

import logging
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any, ClassVar

from textual.app import App, ComposeResult
from textual.containers import Horizontal, Vertical
from textual.widgets import Footer

from llmhosts.constants import VERSION
from llmhosts.dashboard.widgets import (
    BackendsWidget,
    HeaderWidget,
    MetricsWidget,
    RequestLogWidget,
    SavingsWidget,
)

if TYPE_CHECKING:
    from textual.binding import BindingType

    from llmhosts.health.monitor import HealthMonitor
    from llmhosts.metrics.collector import MetricsCollector, MetricsSnapshot

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# Textual CSS
# ---------------------------------------------------------------------------

_APP_CSS = """
Screen {
    background: rgb(15, 15, 25);
    color: white;
}

#header {
    dock: top;
    height: 3;
    background: rgb(20, 20, 45);
    color: white;
    padding: 1 0 0 0;
    border-bottom: solid rgb(60, 60, 100);
}

#main-content {
    height: 1fr;
    padding: 0;
}

#top-row {
    height: auto;
    max-height: 10;
    min-height: 8;
    padding: 0;
}

#savings-panel {
    width: 1fr;
    min-width: 28;
    max-width: 35;
    height: 100%;
    border: solid rgb(60, 60, 100);
    border-title-color: dodgerblue;
    padding: 0;
}

#backends-panel {
    width: 2fr;
    height: 100%;
    border: solid rgb(60, 60, 100);
    border-title-color: dodgerblue;
    padding: 0;
}

#requests-panel {
    height: 1fr;
    min-height: 10;
    border: solid rgb(60, 60, 100);
    border-title-color: dodgerblue;
    padding: 0;
}

#metrics-panel {
    height: auto;
    min-height: 6;
    max-height: 7;
    dock: bottom;
    border: solid rgb(60, 60, 100);
    border-title-color: dodgerblue;
    padding: 0;
}

Footer {
    background: rgb(20, 20, 45);
    color: rgb(160, 160, 180);
}
"""


# ---------------------------------------------------------------------------
# LLMHostsDashboard App
# ---------------------------------------------------------------------------


class LLMHostsDashboard(App[None]):
    """LLMHosts TUI Dashboard -- real-time proxy monitoring.

    Can run standalone (polling the local proxy API) or be supplied with
    a :class:`~llmhosts.metrics.collector.MetricsCollector` instance directly.
    """

    CSS = _APP_CSS

    TITLE = "LLMHosts Dashboard"
    SUB_TITLE = f"v{VERSION}"

    BINDINGS: ClassVar[list[BindingType]] = [
        ("q", "quit", "Quit"),
        ("r", "refresh", "Refresh"),
        ("d", "toggle_detail", "Detail"),
    ]

    def __init__(
        self,
        metrics_collector: MetricsCollector | None = None,
        health_monitor: HealthMonitor | None = None,
        proxy_url: str = "http://127.0.0.1:4000",
        refresh_interval: float = 2.0,
        **kwargs: Any,
    ) -> None:
        super().__init__(**kwargs)
        self._metrics_collector = metrics_collector
        self._health_monitor = health_monitor
        self._proxy_url = proxy_url.rstrip("/")
        self._refresh_interval = refresh_interval
        self._detail_mode = False

    # ------------------------------------------------------------------
    # Compose the widget tree
    # ------------------------------------------------------------------

    def compose(self) -> ComposeResult:
        yield HeaderWidget(version=VERSION, id="header")
        with Vertical(id="main-content"):
            with Horizontal(id="top-row"):
                yield SavingsWidget(id="savings-panel")
                yield BackendsWidget(id="backends-panel")
            yield RequestLogWidget(id="requests-panel")
        yield MetricsWidget(id="metrics-panel")
        yield Footer()

    # ------------------------------------------------------------------
    # Lifecycle
    # ------------------------------------------------------------------

    def on_mount(self) -> None:
        """Start the periodic refresh timer after the app is mounted."""
        self.set_interval(self._refresh_interval, self._tick)
        # Do an immediate first refresh
        self.call_after_refresh(self._tick)
        logger.info(
            "Dashboard mounted (collector=%s, interval=%.1fs)",
            "direct" if self._metrics_collector else "http",
            self._refresh_interval,
        )

    # ------------------------------------------------------------------
    # Periodic update
    # ------------------------------------------------------------------

    async def _tick(self) -> None:
        """Fetch fresh data and push it into widgets."""
        try:
            snapshot = await self._get_snapshot()
            if snapshot is None:
                return

            # Push data into each widget
            header: HeaderWidget = self.query_one("#header", HeaderWidget)
            header.update_data(snapshot.uptime_seconds)

            savings: SavingsWidget = self.query_one("#savings-panel", SavingsWidget)
            savings.update_data(snapshot)

            metrics: MetricsWidget = self.query_one("#metrics-panel", MetricsWidget)
            metrics.update_data(snapshot)

            # Update backends from health monitor if available
            await self._update_backends()

        except Exception:
            logger.debug("Dashboard tick error", exc_info=True)

    async def _get_snapshot(self) -> MetricsSnapshot | None:
        """Get a metrics snapshot, either directly or via HTTP."""
        # Direct access (running in same process)
        if self._metrics_collector is not None:
            return self._metrics_collector.snapshot()

        # HTTP fallback: fetch from proxy API
        return await self._fetch_snapshot_http()

    async def _fetch_snapshot_http(self) -> MetricsSnapshot | None:
        """Fetch metrics snapshot from the proxy HTTP API."""
        import httpx

        from llmhosts.metrics.collector import MetricsSnapshot

        try:
            async with httpx.AsyncClient(timeout=httpx.Timeout(3.0)) as client:
                resp = await client.get(f"{self._proxy_url}/api/metrics")
                if resp.status_code != 200:
                    return None
                data = resp.json()
                return MetricsSnapshot(
                    uptime_seconds=data.get("uptime", 0.0),
                    total_requests=data.get("requests_total", 0),
                    requests_per_minute=data.get("rpm", 0.0),
                    cache_hit_rate=data.get("cache_hit_rate", 0.0),
                    avg_latency_ms=data.get("avg_latency", 0.0),
                    total_tokens=data.get("total_tokens", 0),
                    total_cost=data.get("total_cost", 0.0),
                    total_savings=data.get("savings_total", 0.0),
                    savings_percent=0.0,
                    requests_by_backend=data.get("by_backend", {}),
                    active_models=data.get("by_model", []),
                )
        except Exception:
            logger.debug("Failed to fetch metrics from %s", self._proxy_url, exc_info=True)
            return None

    async def _update_backends(self) -> None:
        """Update the backends widget from health monitor or HTTP API."""
        backends_widget: BackendsWidget = self.query_one("#backends-panel", BackendsWidget)

        # Direct access
        if self._health_monitor is not None:
            statuses = self._health_monitor.get_all_status()
            backends_widget.update_from_health(statuses)
            return

        # HTTP fallback
        try:
            import httpx

            async with httpx.AsyncClient(timeout=httpx.Timeout(3.0)) as client:
                resp = await client.get(f"{self._proxy_url}/api/backends")
                if resp.status_code != 200:
                    return
                data = resp.json()
                entries: list[tuple[str, bool, int]] = []
                for b in data.get("backends", []):
                    entries.append(
                        (
                            b.get("type", "unknown"),
                            b.get("healthy", False),
                            b.get("model_count", 0),
                        )
                    )
                backends_widget.update_backends(entries)
        except Exception:
            logger.debug("Failed to fetch backends from %s", self._proxy_url, exc_info=True)

    # ------------------------------------------------------------------
    # Actions
    # ------------------------------------------------------------------

    def action_refresh(self) -> None:
        """Manually trigger a data refresh."""
        self.call_after_refresh(self._tick)

    def action_toggle_detail(self) -> None:
        """Toggle detail mode (placeholder for future expansion)."""
        self._detail_mode = not self._detail_mode
        self.notify(
            f"Detail mode {'enabled' if self._detail_mode else 'disabled'}",
            severity="information",
            timeout=2,
        )

    # ------------------------------------------------------------------
    # Public API for external code to push request-log entries
    # ------------------------------------------------------------------

    def push_request(
        self,
        timestamp: datetime | None = None,
        model_requested: str = "",
        model_used: str = "",
        backend: str = "",
        cached: bool = False,
        cost: float = 0.0,
        latency_ms: float = 0.0,
    ) -> None:
        """Push a new request entry into the live request log.

        Called by the proxy middleware after each completed request.
        """
        from llmhosts.dashboard.widgets import RequestEntry

        entry = RequestEntry(
            timestamp=timestamp or datetime.now(tz=timezone.utc),
            model_requested=model_requested,
            model_used=model_used,
            backend=backend,
            cached=cached,
            cost=cost,
            latency_ms=latency_ms,
        )
        try:
            request_log: RequestLogWidget = self.query_one("#requests-panel", RequestLogWidget)
            request_log.add_request(entry)
        except Exception:
            logger.debug("Could not push request to dashboard log", exc_info=True)


# ---------------------------------------------------------------------------
# Standalone entry point
# ---------------------------------------------------------------------------


def run_dashboard(
    proxy_url: str = "http://127.0.0.1:4000",
    refresh_interval: float = 2.0,
) -> None:
    """Launch the TUI dashboard as a standalone application.

    Connects to the proxy API via HTTP for metrics data.
    """
    app = LLMHostsDashboard(
        proxy_url=proxy_url,
        refresh_interval=refresh_interval,
    )
    app.run()
