#!/usr/bin/env python3
"""CLI entry point for remote hardware testing.

Usage examples::

    # Run full suite on Alienware
    python tests/remote/cli.py alienware --suite full

    # Run install suite on all devices
    python tests/remote/cli.py --all --suite install

    # Ad-hoc command on a device
    python tests/remote/cli.py alienware --command "llmhosts doctor"

    # Run suite and post results to GitHub issue
    python tests/remote/cli.py alienware --suite full --post-github 359

    # Full suite with continue-on-failure
    python tests/remote/cli.py alienware --suite full --continue-on-failure
"""

from __future__ import annotations

import argparse
import asyncio
import logging
import sys

logger = logging.getLogger("tests.remote")


def _build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="remote-test",
        description="Run LLMHosts test suites on remote hardware devices",
    )
    parser.add_argument(
        "device",
        nargs="?",
        help="Device name from devices.toml (e.g., alienware, mac-mini, dual-4090)",
    )
    parser.add_argument(
        "--all",
        action="store_true",
        help="Run on all devices in the registry",
    )
    parser.add_argument(
        "--suite",
        default="full",
        help="Test suite to run (clean, install, doctor, serve, inference, upgrade, uninstall, full). Default: full",
    )
    parser.add_argument(
        "--command",
        help="Run an ad-hoc command instead of a suite",
    )
    parser.add_argument(
        "--post-github",
        type=int,
        metavar="ISSUE",
        help="Post results as a comment on GitHub issue #N",
    )
    parser.add_argument(
        "--continue-on-failure",
        action="store_true",
        help="Continue running suites even if a sub-suite fails (for full suite)",
    )
    parser.add_argument(
        "--timeout",
        type=float,
        default=60.0,
        help="Default command timeout in seconds (default: 60)",
    )
    parser.add_argument(
        "--verbose",
        "-v",
        action="store_true",
        help="Enable verbose logging",
    )
    return parser


async def _run_adhoc(device_name: str, command: str, timeout: float) -> None:
    """Run a single ad-hoc command on a device."""
    from tests.remote.devices import get_device
    from tests.remote.runner import RemoteTestRunner

    _has_rich = False
    try:
        from rich.console import Console

        _console = Console()
        _has_rich = True
    except ImportError:
        pass

    spec = get_device(device_name)
    info = spec.to_device_info()

    if _has_rich:
        _console.print(f"[bold]Connecting to {device_name}[/bold] ({spec.hostname})...")
    else:
        print(f"Connecting to {device_name} ({spec.hostname})...")

    async with RemoteTestRunner(info) as runner:
        result = await runner.run_command(command, timeout=timeout)

        if _has_rich:
            status = "[green]OK[/green]" if result.success else "[red]FAIL[/red]"
            _console.print(f"\n{status} (exit={result.exit_code}, {result.duration_s}s)")
            if result.stdout:
                _console.print("[dim]stdout:[/dim]")
                _console.print(result.stdout)
            if result.stderr:
                _console.print("[dim]stderr:[/dim]")
                _console.print(result.stderr)
        else:
            print(f"\n{'OK' if result.success else 'FAIL'} (exit={result.exit_code}, {result.duration_s}s)")
            if result.stdout:
                print("stdout:", result.stdout)
            if result.stderr:
                print("stderr:", result.stderr)


async def _run_suite(
    device_names: list[str],
    suite_name: str,
    *,
    continue_on_failure: bool = False,
    post_github: int | None = None,
) -> int:
    """Run a test suite on one or more devices. Returns exit code (0=success)."""
    from tests.remote.devices import get_device
    from tests.remote.report import generate_markdown_report, post_to_github, save_json_report
    from tests.remote.runner import RemoteTestRunner
    from tests.remote.suites import SUITE_REGISTRY
    from tests.remote.suites.base import SuiteResult

    try:
        from rich.console import Console
        from rich.table import Table

        console = Console()
        has_rich = True
    except ImportError:
        has_rich = False

    if suite_name not in SUITE_REGISTRY:
        available = ", ".join(sorted(SUITE_REGISTRY.keys()))
        print(f"Unknown suite '{suite_name}'. Available: {available}", file=sys.stderr)
        return 1

    all_results: list[SuiteResult] = []

    for device_name in device_names:
        try:
            spec = get_device(device_name)
        except KeyError as exc:
            print(f"Error: {exc}", file=sys.stderr)
            continue

        info = spec.to_device_info()

        if has_rich:
            console.print(f"\n[bold cyan]>>> {device_name}[/bold cyan] ({spec.description})")
            console.print(f"    Suite: [bold]{suite_name}[/bold]")
        else:
            print(f"\n>>> {device_name} ({spec.description})")
            print(f"    Suite: {suite_name}")

        suite_cls = SUITE_REGISTRY[suite_name]
        suite = suite_cls(continue_on_failure=continue_on_failure) if suite_name == "full" else suite_cls()

        try:
            async with RemoteTestRunner(info) as runner:
                result = await suite.run(runner, device_name=device_name)
                all_results.append(result)
        except Exception as exc:
            logger.error("Connection to %s failed: %s", device_name, exc)
            # Create a failed result for the report
            failed_result = SuiteResult(
                suite_name=suite_name,
                device_name=device_name,
            )
            from tests.remote.suites.base import StepStatus, TestStep

            failed_result.steps.append(
                TestStep(
                    name="connection",
                    description="SSH connection",
                    status=StepStatus.ERROR,
                    error_message=str(exc),
                )
            )
            all_results.append(failed_result)
            continue

        # Print results table
        if has_rich:
            table = Table(title=f"{device_name} / {suite_name}")
            table.add_column("Step", style="cyan")
            table.add_column("Status")
            table.add_column("Duration", justify="right")
            table.add_column("Details")

            for step in result.steps:
                status_style = {
                    "passed": "[green]PASS[/green]",
                    "failed": "[red]FAIL[/red]",
                    "error": "[red bold]ERROR[/red bold]",
                    "skipped": "[dim]SKIP[/dim]",
                }.get(step.status.value, step.status.value)

                detail = step.description
                if step.error_message:
                    detail = f"[red]{step.error_message}[/red]"
                if len(detail) > 80:
                    detail = detail[:77] + "..."

                table.add_row(step.name, status_style, f"{step.duration_s:.1f}s", detail)

            console.print(table)

            overall = "[green]PASSED[/green]" if result.success else "[red]FAILED[/red]"
            console.print(
                f"    Result: {overall} "
                f"({result.passed} passed, {result.failed} failed, "
                f"{result.errors} errors, {result.skipped} skipped) "
                f"in {result.total_duration_s:.1f}s"
            )
        else:
            print("\n  Steps:")
            for step in result.steps:
                marker = {"passed": "+", "failed": "x", "error": "!", "skipped": "-"}.get(step.status.value, "?")
                print(f"    [{marker}] {step.name}: {step.description} ({step.duration_s:.1f}s)")
                if step.error_message:
                    print(f"        Error: {step.error_message}")

            status_label = "PASSED" if result.success else "FAILED"
            print(
                f"\n  Result: {status_label} "
                f"({result.passed} passed, {result.failed} failed, "
                f"{result.errors} errors, {result.skipped} skipped) "
                f"in {result.total_duration_s:.1f}s"
            )

    if not all_results:
        print("No results to report.", file=sys.stderr)
        return 1

    # Save JSON report
    json_path = save_json_report(all_results)
    print(f"\nJSON report: {json_path}")

    # Post to GitHub if requested
    if post_github is not None:
        markdown = generate_markdown_report(all_results)
        if post_to_github(post_github, markdown):
            print(f"Posted to GitHub issue #{post_github}")
        else:
            print(f"Failed to post to GitHub issue #{post_github}", file=sys.stderr)

    # Return 0 only if all suites passed
    return 0 if all(r.success for r in all_results) else 1


def main() -> None:
    parser = _build_parser()
    args = parser.parse_args()

    # Configure logging
    level = logging.DEBUG if args.verbose else logging.INFO
    logging.basicConfig(
        level=level,
        format="%(asctime)s %(name)s %(levelname)s %(message)s",
        datefmt="%H:%M:%S",
    )

    # Validate arguments
    if not args.device and not args.all:
        parser.error("Specify a device name or use --all")

    if args.command and args.all:
        parser.error("--command cannot be used with --all")

    # Determine device list
    if args.all:
        from tests.remote.devices import load_registry

        device_names = list(load_registry().keys())
    else:
        device_names = [args.device]

    # Run
    if args.command:
        exit_code = asyncio.run(_run_adhoc(args.device, args.command, args.timeout))
        sys.exit(exit_code or 0)
    else:
        exit_code = asyncio.run(
            _run_suite(
                device_names,
                args.suite,
                continue_on_failure=args.continue_on_failure,
                post_github=args.post_github,
            )
        )
        sys.exit(exit_code)


if __name__ == "__main__":
    main()
