"""
Persistent permission rule management commands.

Manages rules saved to cliver-settings.yaml files (global and local).
Session-scoped grants are managed via /session permission instead.
"""

import click
from rich import box
from rich.table import Table

from cliver.cli import Cliver, pass_cliver
from cliver.commands import click_help, wants_help
from cliver.permissions import PermissionAction, PermissionMode, PermissionRule

# Business logic (plain functions — no Click, no async)


def _show_rules(cliver: Cliver):
    """Display all permission rules with their source file."""
    pm = cliver.permission_manager
    if not pm.rules:
        cliver.output("No permission rules configured.")
        _show_mode_info(cliver)
        return

    table = Table(title="Permission Rules", box=box.SQUARE)
    table.add_column("#", style="dim", max_width=3)
    table.add_column("Tool", style="green")
    table.add_column("Resource", style="cyan")
    table.add_column("Action", style="bold")
    table.add_column("Source", style="dim")

    for i, rule in enumerate(pm.rules):
        source = pm._rule_sources[i] if i < len(pm._rule_sources) else "?"
        # Shorten source path for display
        source_short = _shorten_path(source)
        table.add_row(
            str(i),
            rule.tool,
            rule.resource or "—",
            rule.action.value,
            source_short,
        )

    cliver.output(table)
    _show_mode_info(cliver)


def _set_mode(cliver: Cliver, new_mode: str = None, target: str = None):
    """Show current mode or set a new one (saves to file)."""
    pm = cliver.permission_manager
    if new_mode is None:
        _show_mode_info(cliver)
        return

    mode = PermissionMode(new_mode)
    if target not in ("global", "local"):
        target = _prompt_save_target(cliver)
    if target is None:
        return
    pm.save_mode(mode, target)
    cliver.output(f"Permission mode set to '{new_mode}' ({target})")


def _add_rule(cliver: Cliver):
    """Interactive permission builder."""
    pm = cliver.permission_manager
    console = cliver.console

    console.print("Permission Builder")
    console.print("─────────────────")

    # 1. Tool pattern
    console.print("\n1. Tool pattern")
    console.print("   Examples: Read, github#.*, Bash, .*")
    tool = cliver.ui.ask_input("   > ")
    if not tool:
        console.print("Cancelled.")
        return

    # 2. Resource constraint
    console.print("\n2. Resource constraint (optional, leave empty for all)")
    console.print("   Examples: /data/**, git *, https://api.github.com/**")
    resource = cliver.ui.ask_input("   > ") or None

    # 3. Action
    console.print("\n3. Action")
    action_input = cliver.ui.ask_input("   [a]llow or [d]eny? > ", choices=["a", "allow", "d", "deny"])
    if not action_input:
        console.print("Cancelled.")
        return
    if action_input.lower() in ("a", "allow"):
        action = PermissionAction.ALLOW
    else:
        action = PermissionAction.DENY

    # 4. Save target
    target = _prompt_save_target(cliver)
    if target is None:
        return

    rule = PermissionRule(tool=tool, resource=resource, action=action)
    pm.save_rule(rule, target)

    resource_str = f" on {resource}" if resource else ""
    console.print(f"\n+ Rule added: {action.value} {tool}{resource_str}")
    console.print(f"  Saved to {target} settings.")


def _remove_rule(cliver: Cliver, index: int):
    """Remove a rule by its index (shown in /permissions rules)."""
    pm = cliver.permission_manager
    if index < 0 or index >= len(pm.rules):
        cliver.output(f"Invalid index {index}. Use /permissions rules to see available rules.")
        return

    rule = pm.rules[index]
    source = pm._rule_sources[index] if index < len(pm._rule_sources) else "?"
    resource_str = f" on {rule.resource}" if rule.resource else ""
    cliver.output(f"Removing rule #{index}: {rule.action.value} {rule.tool}{resource_str}")
    cliver.output(f"  Source: {source}")

    response = cliver.ui.ask_input("  Confirm? [y/n] > ", choices=["y", "yes", "n", "no"])
    if response.lower() not in ("y", "yes"):
        cliver.output("Cancelled.")
        return

    pm.remove_rule(index)
    cliver.output("Rule removed.")


# TUI dispatch entry point


def dispatch(cliver: Cliver, args: str):
    """Manage tool permission rules — rules, mode, add, remove."""
    parts = args.strip().split(None, 1) if args.strip() else []
    sub = parts[0] if parts else "rules"  # default subcommand
    rest = parts[1] if len(parts) > 1 else ""

    if sub in ("--help", "-h", "help"):
        cliver.output(click_help(permissions, "/permissions"))
        return

    if sub in _SUBCOMMANDS and wants_help(rest):
        cliver.output(click_help(_SUBCOMMANDS[sub], f"/permissions {sub}"))
        return

    if sub == "rules":
        _show_rules(cliver)
    elif sub == "mode":
        mode_parts = rest.strip().split() if rest else []
        mode_val = mode_parts[0] if mode_parts else None
        target = None
        for i, p in enumerate(mode_parts):
            if p == "--target" and i + 1 < len(mode_parts):
                target = mode_parts[i + 1]
                break
        if mode_val in ("default", "auto-edit", "yolo"):
            _set_mode(cliver, mode_val, target=target)
        else:
            _set_mode(cliver)
    elif sub == "add":
        _add_rule(cliver)
    elif sub == "remove":
        if not rest:
            cliver.output(click_help(_SUBCOMMANDS["remove"], "/permissions remove"))
            return
        try:
            index = int(rest.strip())
            _remove_rule(cliver, index)
        except ValueError:
            cliver.output("Index must be a number")
    else:
        cliver.output(f"Unknown: /permissions {sub}")


# --- Helpers ---


def _show_mode_info(cliver: Cliver):
    pm = cliver.permission_manager
    source = pm._mode_source or "default"
    cliver.output(f"\nPermission mode: {pm.mode.value} ({source})")


def _prompt_save_target(cliver: Cliver) -> str | None:
    """Prompt user to choose global or local save target."""
    pm = cliver.permission_manager
    console = cliver.console

    global_path = pm._global_settings_path or "N/A"
    local_path = pm._local_settings_path or "N/A"

    console.print("\nSave to:")
    console.print(f"  \\[g]lobal ({global_path})")
    console.print(f"  \\[l]ocal  ({local_path})")

    choice = cliver.ui.ask_input("  > ", choices=["g", "global", "l", "local"])
    if not choice:
        console.print("Cancelled.")
        return None

    if choice.lower() in ("g", "global"):
        return "global"
    else:
        return "local"


def _shorten_path(path: str) -> str:
    """Shorten a file path for display."""
    from pathlib import Path

    home = str(Path.home())
    if path.startswith(home) and "/.cliver/" in path:
        return "global"
    elif "/.cliver/" in path:
        return "local"
    return path


# Click wrappers (thin — just call logic functions)


@click.group(
    name="permissions",
    help="Manage persistent permission rules saved to cliver-settings.yaml",
    invoke_without_command=True,
)
@pass_cliver
@click.pass_context
def permissions(ctx, cliver: Cliver):
    """View or manage persistent permission rules."""
    if ctx.invoked_subcommand is None:
        # Default: show rules
        _show_rules(cliver)


@permissions.command(name="rules", help="Display all loaded permission rules with source file and current mode")
@pass_cliver
def show_rules(cliver: Cliver):
    _show_rules(cliver)


@permissions.command(name="mode", help="Show or set permission mode (default, auto-edit, or yolo)")
@click.argument("new_mode", required=False, type=click.Choice(["default", "auto-edit", "yolo"]))
@click.option("--target", type=click.Choice(["global", "local"]), default=None, help="Save target (skip prompt)")
@pass_cliver
def set_mode(cliver: Cliver, new_mode: str, target: str):
    _set_mode(cliver, new_mode, target=target)


@permissions.command(
    name="add",
    help="Start interactive wizard to create a permission rule (tool, resource, action)",
)
@pass_cliver
def add_rule(cliver: Cliver):
    _add_rule(cliver)


@permissions.command(name="remove", help="Remove a permission rule by its 0-based index (shown in 'permissions rules')")
@click.argument("index", type=int)
@pass_cliver
def remove_rule(cliver: Cliver, index: int):
    _remove_rule(cliver, index)


# Subcommand lookup for dispatch help
_SUBCOMMANDS = {
    "rules": show_rules,
    "mode": set_mode,
    "add": add_rule,
    "remove": remove_rule,
}
