#!/usr/bin/env python3
"""Helper script for security_readonly Robot Framework smoke tests.

Each sub-command exercises one enforcement layer and prints a sentinel
on success so the .robot file can assert via ``Should Contain``.
"""

from __future__ import annotations

import sys
from pathlib import Path

# Ensure local src is importable
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

from cleveragents.domain.models.core.tool import (  # noqa: E402
    Tool,
    ToolCapability,
    ToolSource,
    ToolType,
)
from cleveragents.skills.context import SkillContext, SkillExecutionError  # noqa: E402
from cleveragents.tool.builtins.changeset import (  # noqa: E402
    ChangeSetCapture,
    ReadOnlyViolationError,
)
from cleveragents.tool.context import ToolExecutionContext  # noqa: E402
from cleveragents.tool.lifecycle import ToolAccessDeniedError, ToolRuntime  # noqa: E402
from cleveragents.tool.runtime import ToolSpec  # noqa: E402


def _make_tool(name: str, writes: bool, read_only: bool) -> Tool:
    return Tool(
        name=name,
        description=f"Test {name}",
        source=ToolSource.BUILTIN,
        tool_type=ToolType.TOOL,
        timeout=300,
        capability=ToolCapability(read_only=read_only, writes=writes),
    )


def _make_spec(name: str, writes: bool, read_only: bool) -> ToolSpec:
    return ToolSpec(
        name=name,
        description=f"Test spec {name}",
        capabilities=ToolCapability(read_only=read_only, writes=writes),
        handler=lambda inputs: {"ok": True},
    )


def runtime_blocks_write() -> None:
    tool = _make_tool("builtin/file-write", writes=True, read_only=False)
    ctx = ToolExecutionContext(plan_id="plan-ro", plan_read_only=True)
    try:
        ToolRuntime._enforce_capabilities(tool, ctx)
        print("FAIL: expected ToolAccessDeniedError")
        sys.exit(1)
    except ToolAccessDeniedError:
        print("runtime-blocks-write-ok")


def runtime_allows_readonly() -> None:
    tool = _make_tool("builtin/search", writes=False, read_only=True)
    ctx = ToolExecutionContext(plan_id="plan-ro", plan_read_only=True)
    ToolRuntime._enforce_capabilities(tool, ctx)
    print("runtime-allows-readonly-ok")


def skillctx_blocks_write() -> None:
    sctx = SkillContext(
        plan_id="plan-ro",
        project_id="proj",
        sandbox_path=Path("/tmp/sandbox"),
        read_only=True,
    )
    try:
        sctx.enforce_write_guard("skill/writer")
        print("FAIL: expected SkillExecutionError")
        sys.exit(1)
    except SkillExecutionError:
        print("skillctx-blocks-write-ok")


def changeset_rejects_write() -> None:
    cap = ChangeSetCapture(plan_id="plan-ro", read_only=True)
    spec = _make_spec("test/writer", writes=True, read_only=False)
    try:
        cap.wrap_tool(spec)
        print("FAIL: expected ReadOnlyViolationError")
        sys.exit(1)
    except ReadOnlyViolationError:
        print("changeset-rejects-write-ok")


def changeset_passes_readonly() -> None:
    cap = ChangeSetCapture(plan_id="plan-ro", read_only=True)
    spec = _make_spec("test/reader", writes=False, read_only=True)
    result = cap.wrap_tool(spec)
    assert result.name == "test/reader"
    print("changeset-passes-readonly-ok")


def error_contains_toolname() -> None:
    tool = _make_tool("git/push", writes=True, read_only=False)
    ctx = ToolExecutionContext(plan_id="plan-ro", plan_read_only=True)
    try:
        ToolRuntime._enforce_capabilities(tool, ctx)
        print("FAIL: expected ToolAccessDeniedError")
        sys.exit(1)
    except ToolAccessDeniedError as exc:
        msg = str(exc)
        assert "git/push" in msg, f"Tool name missing from error: {msg}"
        print("error-contains-toolname-ok")


_COMMANDS = {
    "runtime-blocks-write": runtime_blocks_write,
    "runtime-allows-readonly": runtime_allows_readonly,
    "skillctx-blocks-write": skillctx_blocks_write,
    "changeset-rejects-write": changeset_rejects_write,
    "changeset-passes-readonly": changeset_passes_readonly,
    "error-contains-toolname": error_contains_toolname,
}


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