"""Robot Framework helper for resource handler sandbox/checkpoint tests.

Exercises the create_sandbox -> create_checkpoint -> modify -> rollback
cycle on real temp directories and git repos.

Issue #836: ResourceHandler sandbox and checkpoint methods.
"""

from __future__ import annotations

import subprocess
import sys
import tempfile
from collections.abc import Callable
from pathlib import Path
from typing import NoReturn

_ROOT = Path(__file__).resolve().parents[1]
_SRC = str(_ROOT / "src")
sys.path.insert(0, _SRC)
for _mod in list(sys.modules):
    if _mod.startswith("cleveragents"):
        del sys.modules[_mod]

from cleveragents.domain.models.core.resource import (  # noqa: E402
    PhysVirt,
    Resource,
    ResourceCapabilities,
)
from cleveragents.infrastructure.sandbox.factory import SandboxFactory  # noqa: E402
from cleveragents.infrastructure.sandbox.manager import SandboxManager  # noqa: E402
from cleveragents.resource.handlers.fs_directory import FsDirectoryHandler  # noqa: E402
from cleveragents.resource.handlers.git_checkout import GitCheckoutHandler  # noqa: E402

_CB32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
_ID_CTR = 0


def _next_id() -> str:
    global _ID_CTR
    _ID_CTR += 1
    n = _ID_CTR + 0x836000
    chars: list[str] = []
    for _ in range(26):
        chars.append(_CB32[n % 32])
        n //= 32
    return "".join(reversed(chars))


def _fail(msg: str) -> NoReturn:
    print(msg, file=sys.stderr)
    sys.exit(1)


def _make_resource(rtype: str, location: str) -> Resource:
    return Resource(
        resource_id=_next_id(),
        resource_type_name=rtype,
        classification=PhysVirt.PHYSICAL,
        location=location,
        capabilities=ResourceCapabilities(
            readable=True, writable=True, sandboxable=True
        ),
    )


def fs_checkpoint_rollback() -> None:
    """FsDirectory: sandbox -> checkpoint -> modify -> rollback cycle."""
    handler = FsDirectoryHandler()
    factory = SandboxFactory()
    mgr = SandboxManager(factory=factory, cleanup_on_exit=False)

    tmpdir = tempfile.mkdtemp(prefix="rh836_robot_fs_")
    Path(tmpdir, "file.txt").write_text("original\n", encoding="utf-8")

    resource = _make_resource("fs-directory", tmpdir)
    plan_id = _next_id()

    # 1. Create sandbox
    sb = handler.create_sandbox(resource=resource, plan_id=plan_id, sandbox_manager=mgr)
    if not sb.created:
        _fail("Expected sandbox to be created")
    if not Path(sb.sandbox_path).exists():
        _fail(f"Sandbox path does not exist: {sb.sandbox_path}")

    # 2. Create checkpoint
    ckpt = handler.create_checkpoint(
        resource=resource, plan_id=plan_id, sandbox_manager=mgr
    )
    if not ckpt.checkpoint_id.startswith("checkpoint-"):
        _fail(f"Unexpected checkpoint_id: {ckpt.checkpoint_id}")

    # 3. Modify
    Path(sb.sandbox_path, "file.txt").write_text("modified\n", encoding="utf-8")
    actual = Path(sb.sandbox_path, "file.txt").read_text(encoding="utf-8").strip()
    if actual != "modified":
        _fail(f"Modification failed: got '{actual}'")

    # 4. Rollback
    rb = handler.rollback_to(
        resource=resource,
        plan_id=plan_id,
        checkpoint_id=ckpt.checkpoint_id,
        sandbox_manager=mgr,
    )
    if not rb.success:
        _fail(f"Rollback failed: {rb.message}")

    # 5. Verify restored
    restored = Path(sb.sandbox_path, "file.txt").read_text(encoding="utf-8").strip()
    if restored != "original":
        _fail(f"Rollback did not restore: expected 'original', got '{restored}'")

    print("fs-checkpoint-rollback-ok")


def git_checkpoint_rollback() -> None:
    """GitCheckout: sandbox -> checkpoint -> modify -> rollback cycle."""
    handler = GitCheckoutHandler()
    factory = SandboxFactory()
    mgr = SandboxManager(factory=factory, cleanup_on_exit=False)

    repo = tempfile.mkdtemp(prefix="rh836_robot_git_")
    subprocess.run(["git", "init"], cwd=repo, capture_output=True, check=True)
    subprocess.run(
        ["git", "config", "user.email", "test@test.com"],
        cwd=repo,
        capture_output=True,
        check=True,
    )
    subprocess.run(
        ["git", "config", "user.name", "Test"],
        cwd=repo,
        capture_output=True,
        check=True,
    )
    Path(repo, "code.py").write_text("# hello\n", encoding="utf-8")
    subprocess.run(["git", "add", "."], cwd=repo, capture_output=True, check=True)
    subprocess.run(
        ["git", "commit", "-m", "init"],
        cwd=repo,
        capture_output=True,
        check=True,
    )

    resource = _make_resource("git-checkout", repo)
    plan_id = _next_id()

    # 1. Create sandbox
    sb = handler.create_sandbox(resource=resource, plan_id=plan_id, sandbox_manager=mgr)
    if not sb.created:
        _fail("Expected sandbox to be created")

    # 2. Create checkpoint (git tag)
    ckpt = handler.create_checkpoint(
        resource=resource, plan_id=plan_id, sandbox_manager=mgr
    )
    if not ckpt.checkpoint_id.startswith("checkpoint-"):
        _fail(f"Unexpected checkpoint_id: {ckpt.checkpoint_id}")

    # 3. Modify file in sandbox
    Path(sb.sandbox_path, "code.py").write_text("# changed\n", encoding="utf-8")
    subprocess.run(
        ["git", "add", "."], cwd=sb.sandbox_path, capture_output=True, check=True
    )
    subprocess.run(
        ["git", "commit", "-m", "modify"],
        cwd=sb.sandbox_path,
        capture_output=True,
        check=True,
    )

    # 4. Rollback
    rb = handler.rollback_to(
        resource=resource,
        plan_id=plan_id,
        checkpoint_id=ckpt.checkpoint_id,
        sandbox_manager=mgr,
    )
    if not rb.success:
        _fail(f"Rollback failed: {rb.message}")

    print("git-checkpoint-rollback-ok")


_COMMANDS: dict[str, Callable[[], None]] = {
    "fs-checkpoint-rollback": fs_checkpoint_rollback,
    "git-checkpoint-rollback": git_checkpoint_rollback,
}

if __name__ == "__main__":
    if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
        print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr)
        sys.exit(1)
    _COMMANDS[sys.argv[1]]()
