"""
Test Runner - pytest wrapper for the LightWave CLI.

SST Reference: packages/lightwave-core/lightwave/schema/definitions/cli.yaml
Domain: test (runtime: python)
"""

from __future__ import annotations

import subprocess
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any

from lightwave.cli.runner import CommandError


def run_pytest(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run pytest on specified path.

    SST: domains.test.commands[name=run]
    args: [path]
    flags: [--coverage, --verbose, --failfast, -k, -x]
    """
    # Build pytest command
    cmd = ["pytest"]

    # Add path if provided
    if args:
        cmd.append(args[0])

    # Parse flags
    pytest_args = []
    i = 0
    while i < len(args[1:]):
        arg = args[1:][i]
        if arg == "--coverage":
            pytest_args.extend(["--cov", "--cov-report=term-missing"])
        elif arg == "--verbose" or arg == "-v":
            pytest_args.append("-v")
        elif arg == "--failfast":
            pytest_args.append("-x")
        elif arg == "-k":
            if i + 1 < len(args[1:]):
                pytest_args.extend(["-k", args[1:][i + 1]])
                i += 1
        elif arg == "-x":
            pytest_args.append("-x")
        else:
            pytest_args.append(arg)
        i += 1

    cmd.extend(pytest_args)

    if verbose:
        print(f"Running: {' '.join(cmd)}")

    if dry_run:
        if json_output:
            return {"success": True, "dry_run": True, "command": cmd}
        print(f"[dry-run] Would run: {' '.join(cmd)}")
        return None

    # Run pytest
    result = subprocess.run(cmd, capture_output=json_output)

    if json_output:
        return {
            "success": result.returncode == 0,
            "exit_code": result.returncode,
            "stdout": result.stdout.decode() if result.stdout else "",
            "stderr": result.stderr.decode() if result.stderr else "",
        }

    # Let pytest output go directly to terminal
    result = subprocess.run(cmd)
    if result.returncode != 0:
        raise CommandError(f"pytest exited with code {result.returncode}")

    return None


def run_coverage(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run tests with coverage report.

    SST: domains.test.commands[name=coverage]
    flags: [--html, --xml, --fail-under]
    """
    cmd = ["pytest", "--cov", "--cov-report=term-missing"]

    # Parse flags
    for i, arg in enumerate(args):
        if arg == "--html":
            cmd.append("--cov-report=html")
        elif arg == "--xml":
            cmd.append("--cov-report=xml")
        elif arg == "--fail-under" and i + 1 < len(args):
            cmd.append(f"--cov-fail-under={args[i + 1]}")

    if verbose:
        print(f"Running: {' '.join(cmd)}")

    if dry_run:
        if json_output:
            return {"success": True, "dry_run": True, "command": cmd}
        print(f"[dry-run] Would run: {' '.join(cmd)}")
        return None

    result = subprocess.run(cmd, capture_output=json_output)

    if json_output:
        return {
            "success": result.returncode == 0,
            "exit_code": result.returncode,
            "stdout": result.stdout.decode() if result.stdout else "",
        }

    result = subprocess.run(cmd)
    if result.returncode != 0:
        raise CommandError(f"pytest exited with code {result.returncode}")

    return None


def run_adversarial(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run adversarial test suite.

    SST: domains.test.commands[name=adversarial]
    args: [suite]
    flags: [--category, --report]
    """
    try:
        from lightwave.schema.testing.adversarial import AdversarialTestRunner
    except ImportError as e:
        raise CommandError(f"Could not import adversarial testing module: {e}") from e

    suite = args[0] if args else "all"

    # Parse flags
    category = None
    report = False
    for i, arg in enumerate(args[1:]):
        if arg == "--category" and i + 1 < len(args[1:]):
            category = args[1:][i + 1]
        elif arg == "--report":
            report = True

    if verbose:
        print(f"Running adversarial tests: suite={suite}, category={category}")

    if dry_run:
        if json_output:
            return {"success": True, "dry_run": True, "suite": suite, "category": category}
        print(f"[dry-run] Would run adversarial tests: suite={suite}")
        return None

    runner = AdversarialTestRunner()
    results = runner.run(suite=suite, category=category, report=report)

    if json_output:
        return {
            "success": results.all_passed,
            "total": results.total,
            "passed": results.passed,
            "failed": results.failed,
            "failures": results.failure_details,
        }

    # Print results
    print(f"\nAdversarial Test Results: {results.passed}/{results.total} passed")
    if results.failures:
        for failure in results.failure_details:
            print(f"  FAIL: {failure['name']}: {failure['message']}")

    if not results.all_passed:
        raise CommandError(f"{results.failed} adversarial tests failed")

    return None


def run_drill(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run security attack drill.

    SST: domains.test.commands[name=drill]
    args: [category]
    flags: [--actor, --output]
    """
    try:
        from lightwave.schema.testing.adversarial import AttackDrillSuite
    except ImportError as e:
        raise CommandError(f"Could not import attack drill module: {e}") from e

    if not args:
        raise CommandError("Category required. Example: lw test drill injection")

    category = args[0]

    # Parse flags
    actor = "attacker"
    output_file = None
    for i, arg in enumerate(args[1:]):
        if arg == "--actor" and i + 1 < len(args[1:]):
            actor = args[1:][i + 1]
        elif arg == "--output" and i + 1 < len(args[1:]):
            output_file = args[1:][i + 1]

    if verbose:
        print(f"Running attack drill: category={category}, actor={actor}")

    if dry_run:
        if json_output:
            return {"success": True, "dry_run": True, "category": category, "actor": actor}
        print(f"[dry-run] Would run attack drill: category={category}")
        return None

    drill = AttackDrillSuite(category=category, actor=actor)
    results = drill.execute()

    if output_file:
        import json as json_module

        with open(output_file, "w") as f:
            json_module.dump(results.to_dict(), f, indent=2)
        print(f"Results written to {output_file}")

    if json_output:
        return results.to_dict()

    # Print results
    print(f"\nAttack Drill: {category}")
    print(f"Actor: {actor}")
    print(f"Payloads tested: {results.total}")
    print(f"Blocked: {results.blocked}")
    print(f"Allowed (vulnerabilities): {results.allowed}")

    if results.allowed > 0:
        raise CommandError(f"{results.allowed} attack payloads were not blocked!")

    return None
