"""Daily Run metrics routes — dashboard card for triage and session metrics."""

from __future__ import annotations

import json
import logging
import os
from dataclasses import dataclass
from datetime import UTC, datetime, timedelta
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.request import Request as UrlRequest
from urllib.request import urlopen

from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse, JSONResponse

from config import settings
from dashboard.templating import templates

logger = logging.getLogger(__name__)

router = APIRouter(tags=["daily-run"])

REPO_ROOT = Path(settings.repo_root)
CONFIG_PATH = REPO_ROOT / "timmy_automations" / "config" / "daily_run.json"

DEFAULT_CONFIG = {
    "gitea_api": "http://localhost:3000/api/v1",
    "repo_slug": "rockachopa/Timmy-time-dashboard",
    "token_file": "~/.hermes/gitea_token",
    "layer_labels_prefix": "layer:",
}

LAYER_LABELS = ["layer:triage", "layer:micro-fix", "layer:tests", "layer:economy"]


def _load_config() -> dict:
    """Load configuration from config file with fallback to defaults."""
    config = DEFAULT_CONFIG.copy()
    if CONFIG_PATH.exists():
        try:
            file_config = json.loads(CONFIG_PATH.read_text())
            if "orchestrator" in file_config:
                config.update(file_config["orchestrator"])
        except (json.JSONDecodeError, OSError) as exc:
            logger.debug("Could not load daily_run config: %s", exc)

    # Environment variable overrides
    if os.environ.get("TIMMY_GITEA_API"):
        config["gitea_api"] = os.environ.get("TIMMY_GITEA_API")
    if os.environ.get("TIMMY_REPO_SLUG"):
        config["repo_slug"] = os.environ.get("TIMMY_REPO_SLUG")
    if os.environ.get("TIMMY_GITEA_TOKEN"):
        config["token"] = os.environ.get("TIMMY_GITEA_TOKEN")

    return config


def _get_token(config: dict) -> str | None:
    """Get Gitea token from environment or file."""
    if "token" in config:
        return config["token"]

    token_file = Path(config["token_file"]).expanduser()
    if token_file.exists():
        return token_file.read_text().strip()

    return None


class GiteaClient:
    """Simple Gitea API client with graceful degradation."""

    def __init__(self, config: dict, token: str | None):
        self.api_base = config["gitea_api"].rstrip("/")
        self.repo_slug = config["repo_slug"]
        self.token = token
        self._available: bool | None = None

    def _headers(self) -> dict:
        headers = {"Accept": "application/json"}
        if self.token:
            headers["Authorization"] = f"token {self.token}"
        return headers

    def _api_url(self, path: str) -> str:
        return f"{self.api_base}/repos/{self.repo_slug}/{path}"

    def is_available(self) -> bool:
        """Check if Gitea API is reachable."""
        if self._available is not None:
            return self._available

        try:
            req = UrlRequest(
                f"{self.api_base}/version",
                headers=self._headers(),
                method="GET",
            )
            with urlopen(req, timeout=5) as resp:
                self._available = resp.status == 200
                return self._available
        except (HTTPError, URLError, TimeoutError):
            self._available = False
            return False

    def get_paginated(self, path: str, params: dict | None = None) -> list:
        """Fetch all pages of a paginated endpoint."""
        all_items = []
        page = 1
        limit = 50

        while True:
            url = self._api_url(path)
            query_parts = [f"limit={limit}", f"page={page}"]
            if params:
                for key, val in params.items():
                    query_parts.append(f"{key}={val}")
            url = f"{url}?{'&'.join(query_parts)}"

            req = UrlRequest(url, headers=self._headers(), method="GET")
            with urlopen(req, timeout=15) as resp:
                batch = json.loads(resp.read())

            if not batch:
                break

            all_items.extend(batch)
            if len(batch) < limit:
                break
            page += 1

        return all_items


@dataclass
class LayerMetrics:
    """Metrics for a single layer."""

    name: str
    label: str
    current_count: int
    previous_count: int

    @property
    def trend(self) -> str:
        """Return trend indicator."""
        if self.previous_count == 0:
            return "→" if self.current_count == 0 else "↑"
        diff = self.current_count - self.previous_count
        pct = (diff / self.previous_count) * 100
        if pct > 20:
            return "↑↑"
        elif pct > 5:
            return "↑"
        elif pct < -20:
            return "↓↓"
        elif pct < -5:
            return "↓"
        return "→"

    @property
    def trend_color(self) -> str:
        """Return color for trend (CSS variable name)."""
        trend = self.trend
        if trend in ("↑↑", "↑"):
            return "var(--green)"  # More work = positive
        elif trend in ("↓↓", "↓"):
            return "var(--amber)"  # Less work = caution
        return "var(--text-dim)"


@dataclass
class DailyRunMetrics:
    """Complete Daily Run metrics."""

    sessions_completed: int
    sessions_previous: int
    layers: list[LayerMetrics]
    total_touched_current: int
    total_touched_previous: int
    lookback_days: int
    generated_at: str

    @property
    def sessions_trend(self) -> str:
        """Return sessions trend indicator."""
        if self.sessions_previous == 0:
            return "→" if self.sessions_completed == 0 else "↑"
        diff = self.sessions_completed - self.sessions_previous
        pct = (diff / self.sessions_previous) * 100
        if pct > 20:
            return "↑↑"
        elif pct > 5:
            return "↑"
        elif pct < -20:
            return "↓↓"
        elif pct < -5:
            return "↓"
        return "→"

    @property
    def sessions_trend_color(self) -> str:
        """Return color for sessions trend."""
        trend = self.sessions_trend
        if trend in ("↑↑", "↑"):
            return "var(--green)"
        elif trend in ("↓↓", "↓"):
            return "var(--amber)"
        return "var(--text-dim)"


def _extract_layer(labels: list[dict]) -> str | None:
    """Extract layer label from issue labels."""
    for label in labels:
        name = label.get("name", "")
        if name.startswith("layer:"):
            return name.replace("layer:", "")
    return None


def _load_cycle_data(days: int = 14) -> dict:
    """Load cycle retrospective data for session counting."""
    retro_file = REPO_ROOT / ".loop" / "retro" / "cycles.jsonl"
    if not retro_file.exists():
        return {"current": 0, "previous": 0}

    try:
        entries = []
        for line in retro_file.read_text().strip().splitlines():
            try:
                entries.append(json.loads(line))
            except json.JSONDecodeError:
                continue

        now = datetime.now(UTC)
        current_cutoff = now - timedelta(days=days)
        previous_cutoff = now - timedelta(days=days * 2)

        current_count = 0
        previous_count = 0

        for entry in entries:
            ts_str = entry.get("timestamp", "")
            if not ts_str:
                continue
            try:
                ts = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
                if ts >= current_cutoff:
                    if entry.get("success", False):
                        current_count += 1
                elif ts >= previous_cutoff:
                    if entry.get("success", False):
                        previous_count += 1
            except (ValueError, TypeError):
                continue

        return {"current": current_count, "previous": previous_count}
    except (OSError, ValueError) as exc:
        logger.debug("Failed to load cycle data: %s", exc)
        return {"current": 0, "previous": 0}


def _fetch_layer_metrics(
    client: GiteaClient, lookback_days: int = 7
) -> tuple[list[LayerMetrics], int, int]:
    """Fetch metrics for each layer from Gitea issues."""
    now = datetime.now(UTC)
    current_cutoff = now - timedelta(days=lookback_days)
    previous_cutoff = now - timedelta(days=lookback_days * 2)

    layers = []
    total_current = 0
    total_previous = 0

    for layer_label in LAYER_LABELS:
        layer_name = layer_label.replace("layer:", "")
        try:
            # Fetch all issues with this layer label (both open and closed)
            issues = client.get_paginated(
                "issues",
                {"state": "all", "labels": layer_label, "limit": 100},
            )

            current_count = 0
            previous_count = 0

            for issue in issues:
                updated_at = issue.get("updated_at", "")
                if not updated_at:
                    continue
                try:
                    updated = datetime.fromisoformat(updated_at.replace("Z", "+00:00"))
                    if updated >= current_cutoff:
                        current_count += 1
                    elif updated >= previous_cutoff:
                        previous_count += 1
                except (ValueError, TypeError):
                    continue

            layers.append(
                LayerMetrics(
                    name=layer_name,
                    label=layer_label,
                    current_count=current_count,
                    previous_count=previous_count,
                )
            )
            total_current += current_count
            total_previous += previous_count

        except (HTTPError, URLError) as exc:
            logger.debug("Failed to fetch issues for %s: %s", layer_label, exc)
            layers.append(
                LayerMetrics(
                    name=layer_name,
                    label=layer_label,
                    current_count=0,
                    previous_count=0,
                )
            )

    return layers, total_current, total_previous


def _get_metrics(lookback_days: int = 7) -> DailyRunMetrics | None:
    """Get Daily Run metrics from Gitea API."""
    config = _load_config()
    token = _get_token(config)
    client = GiteaClient(config, token)

    if not client.is_available():
        logger.debug("Gitea API not available for Daily Run metrics")
        return None

    try:
        # Get layer metrics from issues
        layers, total_current, total_previous = _fetch_layer_metrics(client, lookback_days)

        # Get session data from cycle retrospectives
        cycle_data = _load_cycle_data(days=lookback_days)

        return DailyRunMetrics(
            sessions_completed=cycle_data["current"],
            sessions_previous=cycle_data["previous"],
            layers=layers,
            total_touched_current=total_current,
            total_touched_previous=total_previous,
            lookback_days=lookback_days,
            generated_at=datetime.now(UTC).isoformat(),
        )
    except Exception as exc:
        logger.debug("Error fetching Daily Run metrics: %s", exc)
        return None


@router.get("/daily-run/metrics", response_class=JSONResponse)
async def daily_run_metrics_api(lookback_days: int = 7):
    """Return Daily Run metrics as JSON API."""
    metrics = _get_metrics(lookback_days)
    if not metrics:
        return JSONResponse(
            {"error": "Gitea API unavailable", "status": "unavailable"},
            status_code=503,
        )

    # Check for quest completions based on Daily Run metrics
    quest_rewards = []
    try:
        from dashboard.routes.quests import check_daily_run_quests

        quest_rewards = await check_daily_run_quests(agent_id="system")
    except Exception as exc:
        logger.debug("Quest checking failed: %s", exc)

    return JSONResponse(
        {
            "status": "ok",
            "lookback_days": metrics.lookback_days,
            "sessions": {
                "completed": metrics.sessions_completed,
                "previous": metrics.sessions_previous,
                "trend": metrics.sessions_trend,
            },
            "layers": [
                {
                    "name": layer.name,
                    "label": layer.label,
                    "current": layer.current_count,
                    "previous": layer.previous_count,
                    "trend": layer.trend,
                }
                for layer in metrics.layers
            ],
            "totals": {
                "current": metrics.total_touched_current,
                "previous": metrics.total_touched_previous,
            },
            "generated_at": metrics.generated_at,
            "quest_rewards": quest_rewards,
        }
    )


@router.get("/daily-run/panel", response_class=HTMLResponse)
async def daily_run_panel(request: Request, lookback_days: int = 7):
    """Return Daily Run metrics panel HTML for HTMX polling."""
    metrics = _get_metrics(lookback_days)

    # Build Gitea URLs for filtered issue lists
    config = _load_config()
    repo_slug = config.get("repo_slug", "rockachopa/Timmy-time-dashboard")
    gitea_base = config.get("gitea_api", "http://localhost:3000/api/v1").replace("/api/v1", "")

    # Logbook URL (link to issues with any layer label)
    layer_labels = ",".join(LAYER_LABELS)
    logbook_url = f"{gitea_base}/{repo_slug}/issues?labels={layer_labels}&state=all"

    # Layer-specific URLs
    layer_urls = {
        layer: f"{gitea_base}/{repo_slug}/issues?labels=layer:{layer}&state=all"
        for layer in ["triage", "micro-fix", "tests", "economy"]
    }

    return templates.TemplateResponse(
        request,
        "partials/daily_run_panel.html",
        {
            "metrics": metrics,
            "logbook_url": logbook_url,
            "layer_urls": layer_urls,
            "gitea_available": metrics is not None,
        },
    )
