from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path
from types import SimpleNamespace

from acl_engine.actions import data as data_actions
from acl_engine.actions import file as file_actions
from acl_engine.engine import ACLEngine

PROJECT_ROOT = Path(__file__).resolve().parents[1]


def build_runtime(tmp_path):
    return SimpleNamespace(workspace_root=tmp_path, timeout=5, memory={})


def run_cli(*args: str) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        [sys.executable, "-m", "cli.main", *args],
        cwd=PROJECT_ROOT,
        capture_output=True,
        text=True,
        check=False,
    )


def test_file_expansion_actions(tmp_path) -> None:
    runtime = build_runtime(tmp_path)

    file_actions.write("notes.txt", "hello", runtime=runtime)
    append_result = file_actions.append("notes.txt", " world", runtime=runtime)
    copy_result = file_actions.copy("notes.txt", "archive/notes-copy.txt", runtime=runtime)
    move_result = file_actions.move("archive/notes-copy.txt", "archive/notes-final.txt", runtime=runtime)
    glob_result = file_actions.glob(".", "**/*.txt", runtime=runtime)

    assert append_result["appended"] is True
    assert copy_result["copied"] is True
    assert move_result["moved"] is True
    assert file_actions.read("notes.txt", runtime=runtime) == "hello world"
    assert "archive/notes-final.txt" in glob_result


def test_data_actions_transform_values(tmp_path) -> None:
    runtime = build_runtime(tmp_path)

    parsed = data_actions.from_json('{"message": {"items": ["a", "b"]}}', runtime=runtime)
    selected = data_actions.select(parsed, 'message.items[1]', runtime=runtime)
    merged = data_actions.merge({"left": 1, "nested": {"a": 1}}, {"right": 2, "nested": {"b": 2}}, deep=True, runtime=runtime)
    rendered = data_actions.template("Hello {name}", {"name": "ACL"}, runtime=runtime)
    serialized = data_actions.to_json(merged, sort_keys=True, runtime=runtime)

    assert selected == "b"
    assert merged == {"left": 1, "right": 2, "nested": {"a": 1, "b": 2}}
    assert rendered == "Hello ACL"
    assert json.loads(serialized)["nested"]["b"] == 2


def test_engine_can_chain_new_integration_actions(tmp_path) -> None:
    engine = ACLEngine(workspace=tmp_path)
    acl_text = "\n".join(
        [
            'util.echo(value="{\\"user\\": {\\"id\\": 7, \\"name\\": \\"Ada\\"}}") -> raw',
            'data.from_json(value=raw) -> payload',
            'data.select(value=payload, path="user.id") -> user_id',
            'data.template(template="user-{id}", values={id=user_id}) -> label',
            'data.to_json(value={label=label, id=user_id}, sort_keys=true) -> output',
        ]
    )

    result = engine.execute(acl_text)

    assert result["status"] == "success"
    assert result["data"]["label"] == "user-7"
    assert json.loads(result["data"]["output"]) == {"id": 7, "label": "user-7"}


def test_engine_can_chain_new_file_actions(tmp_path) -> None:
    engine = ACLEngine(workspace=tmp_path)
    acl_text = "\n".join(
        [
            'file.write(path="docs/source.txt", content="hello")',
            'file.append(path="docs/source.txt", content=" world")',
            'file.copy(source="docs/source.txt", destination="docs/copied.txt")',
            'file.move(source="docs/copied.txt", destination="docs/final.txt")',
            'file.glob(path="docs", pattern="*.txt") -> files',
        ]
    )

    result = engine.execute(acl_text)

    assert result["status"] == "success"
    assert (tmp_path / "docs/source.txt").read_text(encoding="utf-8") == "hello world"
    assert "docs/final.txt" in result["data"]["files"]


def test_engine_filters_action_descriptions(tmp_path) -> None:
    engine = ACLEngine(workspace=tmp_path)

    data_actions_only = engine.describe_actions(namespace="data")
    filesystem_only = engine.describe_actions(capability="filesystem")

    assert "data.from_json" in data_actions_only
    assert all(name.startswith("data.") for name in data_actions_only)
    assert "file.read" in filesystem_only
    assert all("filesystem" in item["capabilities"] for item in filesystem_only.values())


def test_cli_actions_support_filters() -> None:
    completed = run_cli("actions", "--namespace", "data", "--capability", "data")

    payload = json.loads(completed.stdout)
    assert completed.returncode == 0
    assert "data.from_json" in payload["data"]["actions"]
    assert "file.read" not in payload["data"]["actions"]
    assert payload["output_version"] == "1.2"


def test_strict_policy_allows_safe_data_actions(tmp_path) -> None:
    engine = ACLEngine(workspace=tmp_path, policy="strict")
    result = engine.execute('data.template(template="Hello {name}", values={name="ACL"}) -> message')

    assert result["status"] == "success"
    assert result["data"]["message"] == "Hello ACL"


def test_strict_policy_blocks_file_move(tmp_path) -> None:
    engine = ACLEngine(workspace=tmp_path, policy="strict")
    (tmp_path / "source.txt").write_text("hello", encoding="utf-8")

    result = engine.execute('file.move(source="source.txt", destination="target.txt")')

    assert result["status"] == "error"
    assert result["error"]["code"] == "safety_error"