"""Dashboard screen — project overview with stats."""

from __future__ import annotations

from datetime import datetime, timedelta, timezone

from textual import on
from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical
from textual.screen import Screen
from textual.widgets import Footer, Header, OptionList, Static
from textual.widgets.option_list import Option


class DashboardScreen(Screen):
    BINDINGS = [
        Binding("g", "grade", "Grade"),
        Binding("r", "refresh", "Refresh", show=True),
        Binding("q", "quit", "Quit", show=True),
    ]

    def compose(self) -> ComposeResult:
        yield Header()
        with Horizontal(id="dashboard-layout"):
            with Vertical(id="sidebar"):
                yield Static("Projects", classes="section-title")
                yield OptionList(id="project-list")
            with Vertical(id="main-panel"):
                yield Static("", id="project-header")
                with Horizontal(id="stats-row"):
                    yield Static("", id="stat-traces")
                    yield Static("", id="stat-grading")
                    yield Static("", id="stat-averages")
                yield Static("", id="model-breakdown")
                yield Static("", id="span-breakdown")
        yield Footer()

    def on_mount(self) -> None:
        self._refresh_projects()

    def _refresh_projects(self) -> None:
        from ..app import BanditoApp

        app: BanditoApp = self.app
        projects = app.store.list_projects()
        option_list = self.query_one("#project-list", OptionList)
        option_list.clear_options()
        for p in projects:
            option_list.add_option(Option(p, id=p))
        if app.project and app.project in projects:
            idx = projects.index(app.project)
            option_list.highlighted = idx
            self._load_project(app.project)
        elif projects:
            option_list.highlighted = 0
            self._load_project(projects[0])
        else:
            self.query_one("#project-header", Static).update("No projects found")

    @on(OptionList.OptionHighlighted, "#project-list")
    def on_project_highlighted(self, event: OptionList.OptionHighlighted) -> None:
        if event.option and event.option.id:
            self._load_project(str(event.option.id))

    @on(OptionList.OptionSelected, "#project-list")
    def on_project_selected(self, event: OptionList.OptionSelected) -> None:
        self.action_grade()

    def _load_project(self, project: str) -> None:
        from ..app import BanditoApp
        from ...retrieval import get_retrieval

        app: BanditoApp = self.app
        app.project = project
        r = get_retrieval(project)

        evals = r.evals()

        if app.client:
            traces = r.traces(tier="trace", limit=1000)
            data = app.client.analyze_basics(traces, evals or None)
            stats = data["stats"]
        else:
            from ...review.aggregator import aggregate_summaries
            from dataclasses import asdict
            summaries = r.traces(tier="summary")
            stats = asdict(aggregate_summaries(summaries, evals))

        cost = stats["cost"]
        latency = stats["latency"]
        tokens = stats["tokens"]

        self.query_one("#project-header", Static).update(
            f"[bold]{project}[/bold]"
        )

        trace_summaries = r.traces(tier="summary")
        daily: dict[str, int] = {}
        for t in trace_summaries:
            started = t.get("started_at", "")
            if started:
                try:
                    dt = datetime.fromisoformat(started.replace("Z", "+00:00"))
                    daily[dt.strftime("%Y-%m-%d")] = daily.get(dt.strftime("%Y-%m-%d"), 0) + 1
                except (ValueError, TypeError):
                    pass
        today = datetime.now(timezone.utc).date()
        daily_counts = [
            daily.get((today - timedelta(days=6 - i)).isoformat(), 0)
            for i in range(7)
        ]
        spark_chars = " ▁▂▃▄▅▆▇█"
        max_val = max(daily_counts) if any(daily_counts) else 1
        sparkline = "".join(
            spark_chars[min(int(v / max_val * 8), 8)]
            for v in daily_counts
        )
        total_trace_count = len(trace_summaries)
        self.query_one("#stat-traces", Static).update(
            f"[bold]Traces (7d)[/bold]\n"
            f"[cyan]{total_trace_count}[/cyan] total\n"
            f"[dim]{sparkline}[/dim]\n"
            f"[dim]Mon─────Sun[/dim]"
        )

        ungraded = stats["trace_count"] - stats.get("eval_count", 0)

        human_avg = f"{stats['human_avg_grade']:.2f}" if stats.get("human_avg_grade") is not None else "—"
        judge_avg = f"{stats['judge_avg_grade']:.2f}" if stats.get("judge_avg_grade") is not None else "—"
        self.query_one("#stat-grading", Static).update(
            f"[bold]Grading[/bold]\n"
            f"[green]{stats.get('human_eval_count', 0)}[/green] human (avg [cyan]{human_avg}[/cyan])\n"
            f"[magenta]{stats.get('judge_eval_count', 0)}[/magenta] judge (avg [cyan]{judge_avg}[/cyan])\n"
            f"[yellow]{ungraded}[/yellow] ungraded"
        )

        self.query_one("#stat-averages", Static).update(
            f"[bold]Cost & Performance[/bold]\n"
            f"Total:   [cyan]${cost['total']:.4f}[/cyan]\n"
            f"Avg:     [cyan]${cost['mean']:.4f}[/cyan]\n"
            f"Latency: [cyan]{latency['mean']:.1f}s[/cyan]\n"
            f"Tokens:  [cyan]{tokens['mean']:.0f}[/cyan]"
        )

        # Model breakdown
        models = stats.get("models", [])
        if models:
            m_lines = ["[bold]Models[/bold]"]
            for m in sorted(models, key=lambda x: -x.get("total_cost", 0)):
                name = m["model"]
                if m.get("provider"):
                    name = f"{m['model']} [dim]({m['provider']})[/dim]"
                count = m["count"]
                avg_cost = m["total_cost"] / count if count else 0
                m_lines.append(
                    f"  {name}\n"
                    f"    [cyan]{count}[/cyan] calls · "
                    f"[cyan]${m['total_cost']:.4f}[/cyan] total · "
                    f"[cyan]${avg_cost:.4f}[/cyan] avg"
                )
            self.query_one("#model-breakdown", Static).update("\n".join(m_lines))
        else:
            self.query_one("#model-breakdown", Static).update("")

        # Span kinds
        span_kinds = stats.get("span_kinds", [])
        total_spans = sum(sk["count"] for sk in span_kinds) or 1
        lines = ["[bold]Span Kinds[/bold]"]
        for sk in sorted(span_kinds, key=lambda x: -x["count"]):
            pct_val = sk["count"] / total_spans
            bar_width = 30
            filled = int(pct_val * bar_width)
            bar = "█" * filled + "░" * (bar_width - filled)
            lines.append(f"  {sk['kind']:<12} [cyan]{bar}[/cyan]  {pct_val:>4.0%}")
        self.query_one("#span-breakdown", Static).update("\n".join(lines))

    def action_refresh(self) -> None:
        self._refresh_projects()

    def action_grade(self) -> None:
        from .grading import GradingScreen

        self.app.push_screen(GradingScreen())

    def action_quit(self) -> None:
        self.app.exit()
