"""hdx test — run test fixtures against an MCP server."""

from __future__ import annotations

import asyncio
import platform
import shlex
from fnmatch import fnmatch
from pathlib import Path

import click

from hatchdx.harness.fixtures import FixtureError, load_fixtures
from hatchdx.harness.reporter import report_results
from hatchdx.harness.runner import run_all_tests
from hatchdx.harness.simulator import SimulatorError
from hatchdx.utils import console
from hatchdx.utils.config import ConfigError, load_config


def _resolve_command(raw_command: str) -> list[str]:
    """Parse *raw_command* and resolve ``python`` to the project's venv if one exists."""
    parts = shlex.split(raw_command)
    if not parts:
        return parts

    # If the command starts with "python", check for a local venv
    if parts[0] in ("python", "python3"):
        if platform.system() == "Windows":
            venv_python = Path(".venv/Scripts/python.exe")
        else:
            venv_python = Path(".venv/bin/python")

        if venv_python.is_file():
            parts[0] = str(venv_python.absolute())

    return parts


@click.command("test")
@click.option(
    "--fixtures-dir",
    "-d",
    default=None,
    help="Path to fixtures directory (default: from pyproject.toml)",
)
@click.option(
    "--timeout",
    "-t",
    default=5000,
    type=int,
    help="Timeout per tool call in milliseconds",
)
@click.option(
    "--verbose",
    "-v",
    is_flag=True,
    help="Show request/response details for each test",
)
@click.option(
    "--filter",
    "-f",
    "name_filter",
    default=None,
    help="Run only tests whose name matches this pattern (supports * wildcards)",
)
def test_cmd(fixtures_dir, timeout, verbose, name_filter):
    """Run test fixtures against your MCP server."""
    # 1. Load project config
    try:
        config = load_config()
    except ConfigError as exc:
        console.error(str(exc))
        raise SystemExit(1)

    console.info(f"Testing {config.server_name}")
    click.echo()

    # 2. Discover fixtures
    fixtures_path = Path(fixtures_dir) if fixtures_dir else config.fixtures_path

    try:
        test_cases = load_fixtures(fixtures_path)
    except FixtureError as exc:
        console.error(str(exc))
        raise SystemExit(1)

    if not test_cases:
        console.warning(f"No test fixtures found in {fixtures_path}/")
        click.echo()
        click.echo("  Create a fixture file:")
        click.echo(f"    {fixtures_path}/test_tools.yaml")
        click.echo()
        click.echo("  Example:")
        click.echo("    tests:")
        click.echo('      - name: "my first test"')
        click.echo('        tool: "my_tool"')
        click.echo("        input:")
        click.echo('          key: "value"')
        click.echo("        expect:")
        click.echo('          status: "success"')
        raise SystemExit(1)

    # 3. Apply filter
    if name_filter:
        test_cases = [
            tc for tc in test_cases if fnmatch(tc.name.lower(), name_filter.lower())
        ]
        if not test_cases:
            console.warning(f"No tests match filter: {name_filter}")
            raise SystemExit(1)

    console.step(f"Discovered {len(test_cases)} test fixture{'s' if len(test_cases) != 1 else ''}")

    # 4. Start server and run tests
    try:
        command = _resolve_command(config.server_command)
    except ValueError as exc:
        console.error(f"Invalid server command: {exc}")
        raise SystemExit(1)

    if not command:
        console.error("Server command is empty. Check your hdx config.")
        raise SystemExit(1)

    timeout_sec = timeout / 1000

    console.step("Starting server...")

    try:
        results = asyncio.run(
            run_all_tests(command, test_cases, timeout=timeout_sec)
        )
    except SimulatorError as exc:
        click.echo()
        console.error(f"Failed to start server: {exc}")
        click.echo()
        click.echo("  Make sure your server is installed:")
        click.echo("    uv sync")
        raise SystemExit(1)

    console.success("Server started")

    # 5. Persist results to .hdx/eval_results.db
    from hatchdx.eval.models import AssertionOutcome, EvalAssertion, EvalCase, EvalResult
    from hatchdx.eval.storage import EvalStorage

    storage = EvalStorage()
    run_id = storage.start_run()
    suite_name = config.server_name

    for r in results:
        # Build a minimal EvalCase from the TestCase
        eval_case = EvalCase(
            name=r.test_case.name,
            tool=r.test_case.tool,
            input=r.test_case.input,
            assertions=[],
        )

        # Build assertion outcomes from the test result
        assertion_results: list[AssertionOutcome] = []
        if r.passed:
            assertion_results.append(
                AssertionOutcome(
                    assertion=EvalAssertion(type="test_expectation"),
                    passed=True,
                    reason="All expectations met",
                )
            )
        elif r.failure_reason:
            assertion_results.append(
                AssertionOutcome(
                    assertion=EvalAssertion(type="test_expectation"),
                    passed=False,
                    reason=r.failure_reason,
                )
            )

        eval_result = EvalResult(
            eval_case=eval_case,
            passed=r.passed,
            assertion_results=assertion_results,
            latency_ms=r.latency_ms,
            error=r.failure_reason if not r.passed else None,
        )
        storage.store_result(run_id, suite_name, eval_result)

    storage.finish_run(run_id)
    storage.close()

    click.echo()

    # 6. Report results
    exit_code = report_results(
        results,
        server_name=config.server_name,
        verbose=verbose,
    )
    raise SystemExit(exit_code)
