"""Helper utilities for devcontainer handler Robot smoke tests.

Each command prints a single ``<command>-ok`` token on success so the
calling Robot test can assert on ``stdout``.
"""

from __future__ import annotations

import json
import sys
import tempfile
from pathlib import Path
from typing import Any

from cleveragents.domain.models.core.resource_type import ResourceTypeSpec
from cleveragents.resource.handlers.devcontainer import DevcontainerHandler
from cleveragents.resource.handlers.discovery import (
    DevcontainerDiscoveryResult,
    discover_devcontainers,
)
from cleveragents.resource.handlers.protocol import ResourceHandler
from cleveragents.resource.handlers.resolver import (
    clear_handler_cache,
    resolve_handler,
)

_VALID_DC_JSON: str = json.dumps(
    {"name": "test-devcontainer", "image": "ubuntu:latest"}
)

# -- Built-in type definitions for DAG checks --------------------------------

_BUILTIN_TYPES: list[dict[str, Any]] = [
    {
        "name": "git-checkout",
        "description": "Git checkout",
        "resource_kind": "physical",
        "sandbox_strategy": "git_worktree",
        "built_in": True,
        "child_types": [
            "fs-directory",
            "fs-file",
            "devcontainer-instance",
            "devcontainer-file",
        ],
    },
    {
        "name": "devcontainer-instance",
        "description": "Devcontainer instance",
        "resource_kind": "physical",
        "sandbox_strategy": "snapshot",
        "built_in": True,
        "parent_types": ["git-checkout", "fs-directory"],
        "child_types": ["devcontainer-file"],
    },
    {
        "name": "devcontainer-file",
        "description": "Devcontainer config file",
        "resource_kind": "physical",
        "sandbox_strategy": "copy_on_write",
        "built_in": True,
        "parent_types": ["devcontainer-instance"],
        "child_types": [],
    },
]


def cmd_protocol_check() -> None:
    """Verify DevcontainerHandler satisfies ResourceHandler."""
    handler = DevcontainerHandler()
    assert isinstance(handler, ResourceHandler), "Protocol check failed"
    print("protocol-check-ok")


def cmd_strategy_check() -> None:
    """Verify DevcontainerHandler default strategy.

    F22/F25 fix: handler uses ``none`` because SandboxFactory has not yet
    implemented ``snapshot``.  The container itself provides isolation.
    Known limitation — will switch to ``snapshot`` once implemented.
    """
    handler = DevcontainerHandler()
    assert handler._default_strategy.value == "none"
    print("strategy-check-ok")


def cmd_discovery_valid() -> None:
    """Discover devcontainer from valid config."""
    with tempfile.TemporaryDirectory() as tmp:
        dc_dir = Path(tmp) / ".devcontainer"
        dc_dir.mkdir()
        (dc_dir / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
        results = discover_devcontainers(tmp, "git-checkout")
        assert len(results) == 1, f"Expected 1 result, got {len(results)}"
        assert str(results[0].config_path).endswith("devcontainer.json")
    print("discovery-valid-ok")


def cmd_discovery_invalid() -> None:
    """Skip invalid devcontainer.json."""
    with tempfile.TemporaryDirectory() as tmp:
        dc_dir = Path(tmp) / ".devcontainer"
        dc_dir.mkdir()
        (dc_dir / "devcontainer.json").write_text("not valid json {{", encoding="utf-8")
        results = discover_devcontainers(tmp, "git-checkout")
        assert len(results) == 0, f"Expected 0 results, got {len(results)}"
    print("discovery-invalid-ok")


def cmd_discovery_root() -> None:
    """Discover root-level .devcontainer.json."""
    with tempfile.TemporaryDirectory() as tmp:
        (Path(tmp) / ".devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
        results = discover_devcontainers(tmp, "fs-directory")
        assert len(results) == 1, f"Expected 1 result, got {len(results)}"
    print("discovery-root-ok")


def cmd_discovery_nontrigger() -> None:
    """No discovery for non-trigger types."""
    with tempfile.TemporaryDirectory() as tmp:
        dc_dir = Path(tmp) / ".devcontainer"
        dc_dir.mkdir()
        (dc_dir / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
        results = discover_devcontainers(tmp, "fs-file")
        assert len(results) == 0, f"Expected 0 results, got {len(results)}"
    print("discovery-nontrigger-ok")


def cmd_builtin_types() -> None:
    """Verify devcontainer types in BUILTIN_NAMES."""
    names = ResourceTypeSpec.BUILTIN_NAMES
    assert "devcontainer-instance" in names
    assert "devcontainer-file" in names
    assert "container-instance" in names
    print("builtin-types-ok")


def cmd_dag_hierarchy() -> None:
    """Validate devcontainer DAG parent/child constraints."""
    for type_def in _BUILTIN_TYPES:
        name = type_def["name"]
        if name == "devcontainer-instance":
            assert "git-checkout" in type_def["parent_types"]
            assert "fs-directory" in type_def["parent_types"]
            assert "devcontainer-file" in type_def["child_types"]
        elif name == "devcontainer-file":
            assert "devcontainer-instance" in type_def["parent_types"]
            assert len(type_def["child_types"]) == 0
        elif name == "git-checkout":
            assert "devcontainer-instance" in type_def["child_types"]
    print("dag-hierarchy-ok")


def cmd_resolver_import() -> None:
    """Verify resolve_handler loads DevcontainerHandler."""
    clear_handler_cache()
    handler = resolve_handler(
        "cleveragents.resource.handlers.devcontainer:DevcontainerHandler"
    )
    assert isinstance(handler, ResourceHandler)
    assert isinstance(handler, DevcontainerHandler)
    print("resolver-import-ok")


def cmd_result_validation() -> None:
    """Verify DevcontainerDiscoveryResult validates inputs."""
    with tempfile.TemporaryDirectory() as tmp:
        dc_file = Path(tmp) / "devcontainer.json"
        dc_file.write_text(_VALID_DC_JSON, encoding="utf-8")

        # Valid creation
        result = DevcontainerDiscoveryResult(
            config_path=dc_file,
            config_data=json.loads(_VALID_DC_JSON),
            parent_location=tmp,
        )
        assert result.config_path == dc_file

        # Empty parent_location should raise
        try:
            DevcontainerDiscoveryResult(
                config_path=dc_file,
                config_data=json.loads(_VALID_DC_JSON),
                parent_location="",
            )
            raise AssertionError("Should have raised ValueError")
        except ValueError:
            pass

    print("result-validation-ok")


def cmd_discovery_named_single() -> None:
    """Discover a single named devcontainer configuration."""
    with tempfile.TemporaryDirectory() as tmp:
        named_dir = Path(tmp) / ".devcontainer" / "api"
        named_dir.mkdir(parents=True)
        (named_dir / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
        results = discover_devcontainers(tmp, "git-checkout")
        assert len(results) == 1, f"Expected 1 result, got {len(results)}"
        assert results[0].config_name == "api", (
            f"Expected config_name='api', got {results[0].config_name!r}"
        )
    print("discovery-named-single-ok")


def cmd_discovery_named_multiple() -> None:
    """Discover multiple named devcontainer configurations."""
    with tempfile.TemporaryDirectory() as tmp:
        for name in ["api", "frontend"]:
            d = Path(tmp) / ".devcontainer" / name
            d.mkdir(parents=True)
            (d / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
        results = discover_devcontainers(tmp, "git-checkout")
        assert len(results) == 2, f"Expected 2 results, got {len(results)}"
        names = {r.config_name for r in results}
        assert names == {"api", "frontend"}, (
            f"Expected {{'api', 'frontend'}}, got {names!r}"
        )
    print("discovery-named-multiple-ok")


def cmd_discovery_named_mixed() -> None:
    """Discover mixed root and named devcontainer configurations."""
    with tempfile.TemporaryDirectory() as tmp:
        dc_dir = Path(tmp) / ".devcontainer"
        dc_dir.mkdir()
        (dc_dir / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
        named_dir = dc_dir / "api"
        named_dir.mkdir()
        (named_dir / "devcontainer.json").write_text(_VALID_DC_JSON, encoding="utf-8")
        results = discover_devcontainers(tmp, "git-checkout")
        assert len(results) == 2, f"Expected 2 results, got {len(results)}"
        root_results = [r for r in results if r.config_name is None]
        named_results = [r for r in results if r.config_name == "api"]
        assert len(root_results) == 1, "Expected 1 root result"
        assert len(named_results) == 1, "Expected 1 named result"
    print("discovery-named-mixed-ok")


def cmd_discovery_named_config_name_attr() -> None:
    """Verify config_name attribute on DevcontainerDiscoveryResult."""
    with tempfile.TemporaryDirectory() as tmp:
        # Named config has config_name set
        named_dir = Path(tmp) / ".devcontainer" / "backend"
        named_dir.mkdir(parents=True)
        dc_file = named_dir / "devcontainer.json"
        dc_file.write_text(_VALID_DC_JSON, encoding="utf-8")
        result = DevcontainerDiscoveryResult(
            config_path=dc_file,
            config_data=json.loads(_VALID_DC_JSON),
            parent_location=tmp,
            config_name="backend",
        )
        assert result.config_name == "backend", (
            f"Expected 'backend', got {result.config_name!r}"
        )
        # Root config has config_name=None
        root_result = DevcontainerDiscoveryResult(
            config_path=dc_file,
            config_data=json.loads(_VALID_DC_JSON),
            parent_location=tmp,
            config_name=None,
        )
        assert root_result.config_name is None, (
            f"Expected None, got {root_result.config_name!r}"
        )
    print("discovery-named-config-name-attr-ok")


_COMMANDS = {
    "protocol-check": cmd_protocol_check,
    "strategy-check": cmd_strategy_check,
    "discovery-valid": cmd_discovery_valid,
    "discovery-invalid": cmd_discovery_invalid,
    "discovery-root": cmd_discovery_root,
    "discovery-nontrigger": cmd_discovery_nontrigger,
    "builtin-types": cmd_builtin_types,
    "dag-hierarchy": cmd_dag_hierarchy,
    "resolver-import": cmd_resolver_import,
    "result-validation": cmd_result_validation,
    "discovery-named-single": cmd_discovery_named_single,
    "discovery-named-multiple": cmd_discovery_named_multiple,
    "discovery-named-mixed": cmd_discovery_named_mixed,
    "discovery-named-config-name-attr": cmd_discovery_named_config_name_attr,
}


def main() -> None:
    """Dispatch subcommand from argv."""
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <command>", file=sys.stderr)
        sys.exit(1)
    cmd = sys.argv[1]
    func = _COMMANDS.get(cmd)
    if func is None:
        print(f"Unknown command: {cmd}", file=sys.stderr)
        sys.exit(1)
    func()


if __name__ == "__main__":
    main()
