"""Diagnostic checks for manta-client connectivity and configuration."""

from __future__ import annotations

import asyncio
import sqlite3
from dataclasses import asdict, dataclass, field

import httpx
import typer

from manta_client.config import config_manager
from manta_client.output import console, print_json

app = typer.Typer(help="Run diagnostic checks")

TIMEOUT = 5.0


@dataclass
class CheckResult:
    name: str
    status: str  # "pass", "warn", "fail"
    message: str


@dataclass
class DoctorReport:
    checks: list[CheckResult] = field(default_factory=list)

    def add(self, name: str, status: str, message: str) -> None:
        self.checks.append(CheckResult(name=name, status=status, message=message))

    @property
    def passed(self) -> int:
        return sum(1 for c in self.checks if c.status == "pass")

    @property
    def warnings(self) -> int:
        return sum(1 for c in self.checks if c.status == "warn")

    @property
    def failures(self) -> int:
        return sum(1 for c in self.checks if c.status == "fail")

    @property
    def has_failures(self) -> bool:
        return self.failures > 0


_SYMBOLS = {
    "pass": "[green]✓[/green]",
    "warn": "[yellow]![/yellow]",
    "fail": "[red]✗[/red]",
}


async def _run_checks() -> DoctorReport:
    report = DoctorReport()

    # 1. Config file
    config_path = config_manager.config_path
    if config_path.exists():
        report.add("Config file", "pass", f"Found at {config_path}")
    else:
        report.add("Config file", "fail", f"Not found at {config_path}")

    # Load config (best-effort)
    try:
        config = config_manager.load_config()
    except Exception as e:
        report.add("Config load", "fail", f"Cannot load config: {e}")
        return report

    # 2. API URL
    api_url = config.api_url
    if api_url:
        report.add("API URL", "pass", f"URL: {api_url}")
    else:
        report.add(
            "API URL",
            "fail",
            "Not configured (run: manta-client config set api_url <url>)",
        )
        api_url = None

    # 3 & 4. API connectivity
    api_reachable = False
    if api_url:
        base = api_url.rstrip("/")
        try:
            async with httpx.AsyncClient(timeout=TIMEOUT) as client:
                resp = await client.get(f"{base}/healthz")
                if resp.status_code == 200:
                    report.add("API liveness", "pass", "Server is reachable")
                    api_reachable = True
                else:
                    report.add(
                        "API liveness",
                        "fail",
                        f"Unexpected status: {resp.status_code}",
                    )
        except Exception as e:
            report.add("API liveness", "fail", f"Cannot reach server: {e}")

        if api_reachable:
            try:
                async with httpx.AsyncClient(timeout=TIMEOUT) as client:
                    resp = await client.get(f"{base}/ready")
                    if resp.status_code == 200:
                        report.add("API readiness", "pass", "DB + Redis OK")
                    else:
                        body = resp.text[:200]
                        report.add("API readiness", "warn", f"Not ready: {body}")
            except Exception as e:
                report.add("API readiness", "warn", f"Readiness check failed: {e}")

    # 5. Token exists
    if config.access_token:
        report.add("Access token", "pass", "Token present")
    else:
        report.add("Access token", "fail", "No token (run: manta-client auth login)")

    # 7. Token valid
    if config.access_token and api_reachable:
        base = api_url.rstrip("/")  # type: ignore[union-attr]
        try:
            async with httpx.AsyncClient(timeout=TIMEOUT) as client:
                resp = await client.get(
                    f"{base}/api/v1/sys/users/me",
                    headers={"Authorization": f"Bearer {config.access_token}"},
                )
                if resp.status_code == 200:
                    data = resp.json().get("data", {})
                    username = data.get("username", "unknown")
                    report.add("Token valid", "pass", f"Authenticated as {username}")
                else:
                    report.add("Token valid", "fail", "Token invalid or expired")
        except Exception as e:
            report.add("Token valid", "warn", f"Could not validate token: {e}")

    # 8. Data directory writable
    data_dir = config_manager.config_dir
    if data_dir.exists() and data_dir.is_dir():
        test_file = data_dir / ".doctor_test"
        try:
            test_file.write_text("test")
            test_file.unlink()
            report.add("Data directory", "pass", f"Dir OK: {data_dir}")
        except OSError as e:
            report.add("Data directory", "fail", f"Not writable: {e}")
    else:
        report.add("Data directory", "fail", f"Directory not found: {data_dir}")

    # 9. Local database
    db_path = config_manager.config_dir / "manta.db"
    if db_path.exists():
        try:
            conn = sqlite3.connect(str(db_path))
            cursor = conn.execute("SELECT COUNT(*) FROM jobs")
            count = cursor.fetchone()[0]
            conn.close()
            report.add("Local database", "pass", f"OK ({count} jobs cached)")
        except Exception as e:
            report.add("Local database", "warn", f"Error reading DB: {e}")
    else:
        report.add("Local database", "warn", "Not found (will create on first use)")

    # 10. Sessions directory
    sessions_dir = config_manager.config_dir / "sessions"
    if sessions_dir.exists():
        count = len(list(sessions_dir.glob("*.jsonl")))
        report.add("Sessions directory", "pass", f"{count} sessions")
    else:
        report.add("Sessions directory", "warn", "Not found (will create on first use)")

    return report


def _print_report(report: DoctorReport) -> None:
    console.print("\n[bold]Manta Client Doctor[/bold]")
    console.print("=" * 40)
    for check in report.checks:
        symbol = _SYMBOLS[check.status]
        console.print(f"{symbol} {check.message}")
    console.print()
    console.print(
        f"[bold]{report.passed}[/bold] checks passed, "
        f"[yellow]{report.warnings}[/yellow] warnings, "
        f"[red]{report.failures}[/red] failures"
    )


@app.callback(invoke_without_command=True)
def doctor(
    json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
) -> None:
    """Run diagnostic checks on client configuration and connectivity."""
    report = asyncio.run(_run_checks())

    if json_output:
        print_json(
            {
                "checks": [asdict(c) for c in report.checks],
                "summary": {
                    "passed": report.passed,
                    "warnings": report.warnings,
                    "failures": report.failures,
                },
            }
        )
    else:
        _print_report(report)

    if report.has_failures:
        raise typer.Exit(1)
