from __future__ import annotations

import sys
import time
from dataclasses import dataclass, field
from datetime import datetime

from ..models import CheckResult, IssueRecord
from ..runner import run_once
from .common import (
    BANNER,
    LINE_WIDTH,
    console,
    format_ago,
    render_probes,
)


@dataclass
class WatchState:
    interval: int
    results: list[CheckResult] = field(default_factory=list)
    max_results: int = 100

    def append(self, result: CheckResult) -> None:
        self.results.append(result)
        if len(self.results) > self.max_results:
            self.results = self.results[-self.max_results :]

    @property
    def consecutive_ok(self) -> int:
        count = 0
        for r in reversed(self.results):
            if not r.healthy:
                break
            count += 1
        return count

    @property
    def consecutive_problem(self) -> int:
        count = 0
        for r in reversed(self.results):
            if r.healthy:
                break
            count += 1
        return count

    @property
    def has_had_errors(self) -> bool:
        return any(not r.healthy for r in self.results)

    @property
    def last_error(self) -> CheckResult | None:
        for r in reversed(self.results):
            if not r.healthy:
                return r
        return None

    def issue_history(self) -> list[IssueRecord]:
        return IssueRecord.from_results(self.results)


def _format_issue_line(rec: IssueRecord, timing: str) -> str:
    prefix = "\u26a0" if rec.active else " "
    # Structured columns: source | name | cause | count × timing
    source = f"{rec.source:8s}"
    name = rec.name
    cause = f"({rec.cause})" if rec.cause else ""
    left = f"  {prefix} {source}{name} {cause}"
    right = f"({rec.count}x, {timing})"
    pad = max(1, LINE_WIDTH - len(left) - len(right))
    color = "red" if rec.active else "dim"
    return f"[{color}]{left}{' ' * pad}{right}[/{color}]"


def _render(state: WatchState) -> str:
    """Build the monitoring dashboard output as a rich-markup string."""
    lines: list[str] = []
    lines.append(BANNER)

    latest = state.results[-1]

    lines.append("\n[dim]Is it slow?[/dim]")
    if not latest.healthy:
        lines.append("[red]●[/red] YES!")
    elif state.has_had_errors:
        lines.append("[yellow]●[/yellow] Sorta?")
    else:
        lines.append("[green]●[/green] Nope!")

    history = state.issue_history()
    if history:
        lines.append("\n[dim]Why?[/dim]")
        now = datetime.now()
        for rec in history:
            ago_seconds = (now - rec.last_seen).total_seconds()
            timing = "ongoing" if rec.active else format_ago(ago_seconds)
            lines.append(_format_issue_line(rec, timing))

    lines.append("\n[dim]Details[/dim]")
    lines.append(render_probes(latest))

    lines.append("")
    return "\n".join(lines)


def render(state: WatchState) -> None:
    """Clear screen and draw the dashboard."""
    console.clear()
    console.print(_render(state), end="")


def notify(state: WatchState) -> None:
    """Send notification only on transition from healthy to problem."""
    if len(state.results) < 2:
        return
    curr = state.results[-1]
    if not curr.issues:
        return
    if state.results[-2].healthy:
        worst = curr.issues[0]
        msg = f"yslow: {worst.name}"
        # Terminal bell + OSC 9 desktop notification
        sys.stdout.write(f"\a\033]9;{msg}\033\\")
        sys.stdout.flush()


def watch(interval: int = 30, count: int = 1) -> None:
    """Run monitoring loop. count=0 means infinite."""
    state = WatchState(interval=interval)
    iterations = 0
    try:
        while count == 0 or iterations < count:
            with console.status(
                "[dim]checking why...[/dim]", spinner="dots", spinner_style="dim"
            ):
                result = run_once()
            state.append(result)
            render(state)
            notify(state)
            iterations += 1
            if count == 0 or iterations < count:
                with console.status(
                    f"[dim]rechecking in {interval}s...[/dim]",
                    spinner="dots",
                    spinner_style="dim",
                ):
                    time.sleep(interval)
    except KeyboardInterrupt:
        console.print()
