"""
Docker domain command handlers.

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

These commands wrap docker-compose for local development workflow.
"""

from __future__ import annotations

import os
import subprocess
import sys
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any


def _find_compose_file() -> Path | None:
    """Find docker-compose.yml in platform directory."""
    # Try common locations
    search_paths = [
        Path.cwd() / "docker-compose.yml",
        Path.cwd() / "docker-compose.yaml",
        Path.cwd() / "lightwave-platform" / "docker-compose.yml",
        Path.cwd() / "lightwave-platform" / "lwm_core" / "docker-compose.yml",
    ]

    # Also check LIGHTWAVE_PLATFORM_PATH env var
    platform_path = os.environ.get("LIGHTWAVE_PLATFORM_PATH")
    if platform_path:
        search_paths.insert(0, Path(platform_path) / "docker-compose.yml")

    for path in search_paths:
        if path.exists():
            return path

    return None


def _get_compose_cmd() -> list[str]:
    """Get the docker compose command (docker compose or docker-compose)."""
    # Modern docker compose (plugin)
    result = subprocess.run(
        ["docker", "compose", "version"],
        capture_output=True,
        text=True,
    )
    if result.returncode == 0:
        return ["docker", "compose"]

    # Legacy docker-compose
    return ["docker-compose"]


def _run_compose(
    args: list[str],
    compose_file: Path | None = None,
    *,
    capture_output: bool = False,
    check: bool = True,
) -> subprocess.CompletedProcess[str]:
    """Run a docker compose command."""
    compose_cmd = _get_compose_cmd()

    if compose_file is None:
        compose_file = _find_compose_file()

    cmd = compose_cmd.copy()
    if compose_file:
        cmd.extend(["-f", str(compose_file)])
    cmd.extend(args)

    return subprocess.run(
        cmd,
        capture_output=capture_output,
        text=True,
        check=check,
        cwd=compose_file.parent if compose_file else None,
    )


def docker_up(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Start development stack.

    SST: domains.docker.commands[name=up]
    flags: [--bg, --build, --service]
    """
    compose_args = ["up"]

    # Parse flags
    background = "--bg" in args or "-d" in args
    build = "--build" in args
    service = None

    # Extract service flag
    for i, arg in enumerate(args):
        if arg == "--service" and i + 1 < len(args):
            service = args[i + 1]
            break

    if background:
        compose_args.append("-d")
    if build:
        compose_args.append("--build")
    if service:
        compose_args.append(service)

    if dry_run:
        cmd_str = " ".join(_get_compose_cmd() + compose_args)
        if not json_output:
            print(f"[dry-run] Would run: {cmd_str}")
        return {
            "success": True,
            "dry_run": True,
            "command": cmd_str,
        }

    compose_file = _find_compose_file()
    if compose_file is None:
        if json_output:
            return {"success": False, "error": "No docker-compose.yml found"}
        print("Error: No docker-compose.yml found", file=sys.stderr)
        sys.exit(1)

    if verbose:
        print(f"Using compose file: {compose_file}")

    try:
        _run_compose(compose_args, compose_file)
        if json_output:
            return {"success": True, "message": "Docker stack started"}
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)


def docker_down(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Stop development stack.

    SST: domains.docker.commands[name=down]
    flags: [--volumes]
    """
    compose_args = ["down"]

    if "--volumes" in args or "-v" in args:
        compose_args.append("-v")

    if dry_run:
        cmd_str = " ".join(_get_compose_cmd() + compose_args)
        if not json_output:
            print(f"[dry-run] Would run: {cmd_str}")
        return {"success": True, "dry_run": True, "command": cmd_str}

    try:
        _run_compose(compose_args)
        if json_output:
            return {"success": True, "message": "Docker stack stopped"}
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)


def docker_logs(
    args: list[str],
    *,
    json_output: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    View service logs.

    SST: domains.docker.commands[name=logs]
    args: [service]
    flags: [--follow, --tail]
    """
    compose_args = ["logs"]

    # Parse flags
    if "--follow" in args or "-f" in args:
        compose_args.append("-f")

    # Extract tail count
    for i, arg in enumerate(args):
        if arg == "--tail" and i + 1 < len(args):
            compose_args.extend(["--tail", args[i + 1]])
            break

    # Add service name (first non-flag arg)
    for arg in args:
        if not arg.startswith("-") and arg not in ("--tail",):
            compose_args.append(arg)
            break

    try:
        _run_compose(compose_args)
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)


def docker_rebuild(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Rebuild and restart a service.

    SST: domains.docker.commands[name=rebuild]
    args: [service]
    flags: [--no-cache]
    """
    # Get service name
    service = None
    for arg in args:
        if not arg.startswith("-"):
            service = arg
            break

    if not service:
        if json_output:
            return {"success": False, "error": "Service name required"}
        print("Error: Service name required", file=sys.stderr)
        sys.exit(1)

    no_cache = "--no-cache" in args

    if dry_run:
        steps = [
            f"docker compose build {'--no-cache ' if no_cache else ''}{service}",
            f"docker compose up -d {service}",
        ]
        if not json_output:
            print("[dry-run] Would run:")
            for step in steps:
                print(f"  {step}")
        return {"success": True, "dry_run": True, "steps": steps}

    try:
        # Build
        build_args = ["build"]
        if no_cache:
            build_args.append("--no-cache")
        build_args.append(service)
        _run_compose(build_args)

        # Restart
        _run_compose(["up", "-d", service])

        if json_output:
            return {"success": True, "message": f"Service {service} rebuilt"}
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)


def docker_shell(
    args: list[str],
    *,
    json_output: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Open shell in running container.

    SST: domains.docker.commands[name=shell]
    args: [service]
    """
    # Get service name
    service = None
    for arg in args:
        if not arg.startswith("-"):
            service = arg
            break

    if not service:
        service = "django"  # Default service

    # Try bash first, fall back to sh
    try:
        _run_compose(["exec", service, "/bin/bash"])
    except subprocess.CalledProcessError:
        try:
            _run_compose(["exec", service, "/bin/sh"])
        except subprocess.CalledProcessError as e:
            if json_output:
                return {"success": False, "error": str(e)}
            sys.exit(e.returncode)

    return None
