"""Simple Docker manager for benchmarks.

Starts/stops benchmark containers with ports exposed to localhost.
"""

import json
import re
import subprocess
from pathlib import Path
from typing import Any

from pentestgpt.benchmark.config import DEFAULT_HOST, DEFAULT_PORT
from pentestgpt.benchmark.registry import BenchmarkInfo


def start_benchmark(benchmark_path: Path, port: int = DEFAULT_PORT) -> dict[str, Any]:
    """Start a benchmark container.

    Args:
        benchmark_path: Path to benchmark directory (contains docker-compose.yml)
        port: Port to expose on host (default: 8080)

    Returns:
        dict with 'success', 'target_url', and 'message'
    """
    if not benchmark_path.exists():
        return {
            "success": False,
            "target_url": None,
            "message": f"Path not found: {benchmark_path}",
        }

    compose_file = benchmark_path / "docker-compose.yml"
    if not compose_file.exists():
        return {"success": False, "target_url": None, "message": "No docker-compose.yml found"}

    # First, build the containers
    print(f"Building benchmark at {benchmark_path.name}...")
    result = subprocess.run(
        ["make", "build"],
        cwd=str(benchmark_path),
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        return {
            "success": False,
            "target_url": None,
            "message": f"Build failed: {result.stderr or result.stdout}",
        }

    # Run with port mapping override
    # Use docker compose with port override
    print(f"Starting containers on port {port}...")
    result = subprocess.run(
        ["docker", "compose", "up", "-d", "--wait"],
        cwd=str(benchmark_path),
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        return {
            "success": False,
            "target_url": None,
            "message": f"Start failed: {result.stderr or result.stdout}",
        }

    # Get the actual port assigned
    actual_port = get_exposed_port(benchmark_path)
    target_url = f"http://{DEFAULT_HOST}:{actual_port or port}"

    return {
        "success": True,
        "target_url": target_url,
        "port": actual_port or port,
        "message": f"Benchmark started at {target_url}",
    }


def stop_benchmark(benchmark_path: Path) -> dict[str, Any]:
    """Stop a benchmark container.

    Args:
        benchmark_path: Path to benchmark directory

    Returns:
        dict with 'success' and 'message'
    """
    if not benchmark_path.exists():
        return {"success": False, "message": f"Path not found: {benchmark_path}"}

    print(f"Stopping benchmark at {benchmark_path.name}...")

    # Stop containers
    result = subprocess.run(
        ["docker", "compose", "down", "--remove-orphans"],
        cwd=str(benchmark_path),
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        return {"success": False, "message": f"Stop failed: {result.stderr or result.stdout}"}

    return {"success": True, "message": "Benchmark stopped"}


def get_exposed_port(benchmark_path: Path) -> int | None:
    """Get the exposed port from running containers.

    Args:
        benchmark_path: Path to benchmark directory

    Returns:
        Port number or None if not found
    """
    result = subprocess.run(
        ["docker", "compose", "ps", "--format", "{{.Ports}}"],
        cwd=str(benchmark_path),
        capture_output=True,
        text=True,
    )

    if result.returncode != 0 or not result.stdout.strip():
        return None

    # Parse port from output like "0.0.0.0:32768->80/tcp"

    for line in result.stdout.strip().split("\n"):
        match = re.search(r"0\.0\.0\.0:(\d+)->", line)
        if match:
            return int(match.group(1))

    return None


def get_running_benchmarks() -> list[dict[str, Any]]:
    """Get list of running benchmark containers.

    Returns:
        List of dicts with container info
    """
    result = subprocess.run(
        ["docker", "ps", "--format", "{{.Names}}\t{{.Ports}}\t{{.Status}}"],
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        return []

    running = []
    for line in result.stdout.strip().split("\n"):
        if not line:
            continue
        parts = line.split("\t")
        if len(parts) >= 3:
            name, ports, status = parts[0], parts[1], parts[2]
            # Check if it looks like a benchmark container (xben pattern)
            if "xben" in name.lower():
                running.append({"name": name, "ports": ports, "status": status})

    return running


def load_benchmarks(benchmarks_dir: Path) -> dict[str, BenchmarkInfo]:
    """
    Scan directory and load all benchmarks.

    Args:
        benchmarks_dir: Path to benchmarks directory

    Returns:
        Dictionary mapping benchmark ID to BenchmarkInfo
    """
    benchmarks = {}

    if not benchmarks_dir.exists():
        raise FileNotFoundError(f"Benchmarks directory not found: {benchmarks_dir}")

    for benchmark_path in sorted(benchmarks_dir.glob("XBEN-*-24")):
        if not benchmark_path.is_dir():
            continue

        benchmark_json = benchmark_path / "benchmark.json"
        if not benchmark_json.exists():
            print(f"Warning: No benchmark.json in {benchmark_path.name}, skipping")
            continue

        try:
            # Load benchmark.json for metadata
            with open(benchmark_json) as f:
                data = json.load(f)

            # Parse .env for FLAG
            env_file = benchmark_path / ".env"
            if not env_file.exists():
                print(f"Warning: No .env file in {benchmark_path.name}, skipping")
                continue

            flag = _parse_flag_from_env(env_file)
            if not flag:
                print(f"Warning: No FLAG found in {benchmark_path.name}/.env, skipping")
                continue

            benchmarks[benchmark_path.name] = BenchmarkInfo(
                id=benchmark_path.name,
                name=data.get("name", benchmark_path.name),
                description=data.get("description", ""),
                level=int(data.get("level", 1)),
                tags=data.get("tags", []),
                path=benchmark_path,
                expected_flag=flag
            )
        except (json.JSONDecodeError, KeyError, ValueError) as e:
            print(f"Warning: Failed to parse {benchmark_path.name}: {e}")
            continue

    return benchmarks


def _parse_flag_from_env(env_file: Path) -> str | None:
    """
    Parse FLAG value from .env file.

    Args:
        env_file: Path to .env file

    Returns:
        Flag value or None if not found
    """
    try:
        content = env_file.read_text()
        for line in content.split('\n'):
            line = line.strip()
            if line.startswith('FLAG='):
                # Extract value, remove quotes
                flag = line.split('=', 1)[1].strip().strip('"').strip("'")
                return flag
    except Exception as e:
        print(f"Error parsing {env_file}: {e}")
    return None
