from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Sequence

from acl_engine.engine import ACLEngine
from acl_engine.executor import format_result


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="acl", description="Run ACL scripts")
    parser.add_argument(
        "--workspace",
        default="./workspace",
        help="Workspace root for file actions (default: ./workspace)",
    )
    parser.add_argument(
        "--policy",
        choices=["standard", "strict", "dev"],
        default="standard",
        help="Execution policy profile (default: standard)",
    )
    parser.add_argument(
        "--allow-domain",
        action="append",
        default=[],
        help="Allowed network domain. Can be provided multiple times.",
    )
    parser.add_argument(
        "--allow-capability",
        action="append",
        default=[],
        help="Restrict execution to the given capabilities. Can be provided multiple times.",
    )
    parser.add_argument(
        "--continue-on-error",
        action="store_true",
        help="Continue executing remaining commands after a step fails.",
    )
    parser.add_argument(
        "--plugin",
        action="append",
        default=[],
        help="Load a Python plugin module that registers custom actions. Can be provided multiple times.",
    )
    parser.add_argument(
        "--trace-id",
        default=None,
        help="Optional trace identifier to include in output.",
    )
    parser.add_argument(
        "--timeout",
        type=int,
        default=30,
        help="Command timeout in seconds (default: 30)",
    )

    subparsers = parser.add_subparsers(dest="command", required=True)

    run_parser = subparsers.add_parser("run", help="Execute an ACL file")
    run_parser.add_argument("path", help="Path to an ACL file")

    exec_parser = subparsers.add_parser("exec", help="Execute inline ACL text")
    exec_parser.add_argument("acl_text", help="Inline ACL to execute")

    validate_parser = subparsers.add_parser("validate", help="Validate ACL without executing it")
    validate_parser.add_argument("source", help="ACL file path, or ACL text with --inline")
    validate_parser.add_argument("--inline", action="store_true", help="Treat source as inline ACL text")

    dry_run_parser = subparsers.add_parser("dry-run", help="Inspect the execution plan without running actions")
    dry_run_parser.add_argument("source", help="ACL file path, or ACL text with --inline")
    dry_run_parser.add_argument("--inline", action="store_true", help="Treat source as inline ACL text")

    actions_parser = subparsers.add_parser("actions", help="List built-in and custom action schemas")
    actions_parser.add_argument("--namespace", help="Filter actions by namespace")
    actions_parser.add_argument("--capability", help="Filter actions by capability")

    return parser


def main(argv: Sequence[str] | None = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    if args.timeout < 1:
        parser.error("--timeout must be at least 1 second")

    try:
        engine = ACLEngine(
            workspace=args.workspace,
            timeout=args.timeout,
            stop_on_error=not args.continue_on_error,
            policy=args.policy,
            allowed_domains=set(args.allow_domain) or None,
            allowed_capabilities=set(args.allow_capability) or None,
            trace_id=args.trace_id,
        )
        for plugin_path in args.plugin:
            engine.load_plugin_module(plugin_path)
    except (ImportError, OSError, ValueError) as error:
        _print_json(format_result("error", {}, [], error, trace_id=args.trace_id))
        return 1

    if args.command == "actions":
        result = format_result(
            "success",
            {
                "actions": engine.describe_actions(
                    namespace=getattr(args, "namespace", None),
                    capability=getattr(args, "capability", None),
                ),
                "policy": engine.describe_policy(),
            },
            [],
            trace_id=engine.trace_id,
            summary={"policy": engine.policy.name, "steps_total": 0, "steps_completed": 0, "steps_failed": 0},
        )
        _print_json(result)
        return 0

    try:
        acl_text = _load_acl_text(args)
    except OSError as error:
        _print_json(format_result("error", {}, [], error))
        return 1

    if args.command in {"run", "exec"}:
        result = engine.execute(acl_text)
    elif args.command == "validate":
        result = engine.validate(acl_text)
    else:
        result = engine.dry_run(acl_text)

    _print_json(result)
    return 0 if result["status"] == "success" else 1


def _load_acl_text(args: argparse.Namespace) -> str:
    if args.command == "exec":
        return args.acl_text
    if getattr(args, "inline", False):
        return args.source
    if args.command in {"validate", "dry-run"}:
        return Path(args.source).read_text(encoding="utf-8")

    return Path(args.path).read_text(encoding="utf-8")


def _print_json(payload: dict) -> None:
    print(json.dumps(payload, indent=2, sort_keys=True))


if __name__ == "__main__":  # pragma: no cover
    raise SystemExit(main())
