"""Permissions module - permission inference for fire-and-forget execution."""

from __future__ import annotations

import json
import re
from pathlib import Path

# =============================================================================
# PERMISSION BUNDLES - Pre-inferred permissions for fire-and-forget cleave
# =============================================================================

PERMISSION_BUNDLES = {
    "python": {
        "keywords": [
            "python",
            "django",
            "flask",
            "fastapi",
            "pytest",
            "pip",
            "venv",
            "virtualenv",
            "poetry",
            ".py",
        ],
        "permissions": [
            "Bash(python:*)",
            "Bash(python3:*)",
            "Bash(pip:*)",
            "Bash(pip3:*)",
            "Bash(poetry:*)",
            "Bash(pytest:*)",
            "Bash(uv:*)",
        ],
        "description": "Python runtime, package management, and testing",
    },
    "node": {
        "keywords": [
            "node",
            "npm",
            "yarn",
            "pnpm",
            "javascript",
            "typescript",
            "react",
            "vue",
            "angular",
            "express",
            ".js",
            ".ts",
            "package.json",
        ],
        "permissions": [
            "Bash(node:*)",
            "Bash(npm:*)",
            "Bash(npx:*)",
            "Bash(yarn:*)",
            "Bash(pnpm:*)",
            "Bash(tsc:*)",
        ],
        "description": "Node.js runtime, package managers, and TypeScript",
    },
    "rust": {
        "keywords": ["rust", "cargo", "rustc", ".rs", "Cargo.toml"],
        "permissions": [
            "Bash(cargo:*)",
            "Bash(rustc:*)",
            "Bash(rustup:*)",
        ],
        "description": "Rust toolchain",
    },
    "go": {
        "keywords": ["golang", "go mod", "go build", "go test", ".go", "go.mod"],
        "permissions": [
            "Bash(go:*)",
        ],
        "description": "Go toolchain",
    },
    "docker": {
        "keywords": ["docker", "container", "dockerfile", "compose", "image", "registry"],
        "permissions": [
            "Bash(docker:*)",
            "Bash(docker-compose:*)",
        ],
        "description": "Docker container operations",
    },
    "database": {
        "keywords": [
            "postgres",
            "postgresql",
            "mysql",
            "sqlite",
            "mongodb",
            "redis",
            "database",
            "migration",
            "schema",
            "sql",
        ],
        "permissions": [
            "Bash(psql:*)",
            "Bash(mysql:*)",
            "Bash(sqlite3:*)",
            "Bash(mongosh:*)",
            "Bash(redis-cli:*)",
        ],
        "description": "Database CLI tools",
    },
    "git": {
        "keywords": ["git", "commit", "branch", "merge", "push", "pull", "repository"],
        "permissions": [
            "Bash(git:*)",
        ],
        "description": "Git version control",
    },
    "testing": {
        "keywords": [
            "test",
            "spec",
            "jest",
            "mocha",
            "pytest",
            "unittest",
            "coverage",
            "e2e",
            "integration",
        ],
        "permissions": [
            "Bash(jest:*)",
            "Bash(mocha:*)",
            "Bash(pytest:*)",
            "Bash(coverage:*)",
        ],
        "description": "Testing frameworks and coverage",
    },
    "aws": {
        "keywords": ["aws", "s3", "lambda", "ec2", "dynamodb", "cloudformation", "cdk"],
        "permissions": [
            "Bash(aws:*)",
            "Bash(cdk:*)",
            "Bash(sam:*)",
        ],
        "description": "AWS CLI and infrastructure tools",
    },
    "kubernetes": {
        "keywords": ["kubernetes", "k8s", "kubectl", "helm", "pod", "deployment", "service"],
        "permissions": [
            "Bash(kubectl:*)",
            "Bash(helm:*)",
        ],
        "description": "Kubernetes orchestration",
    },
    "cleave": {
        "keywords": ["cleave", "reunify", "child task", "decompos"],
        "permissions": [
            "Bash(python3 ~/.claude/skills/cleave/cleave-cli.py:*)",
        ],
        "description": "Cleave CLI for task decomposition",
    },
    "filesystem": {
        "keywords": ["file", "directory", "folder", "create", "move", "copy", "delete", "path"],
        "permissions": [
            "Bash(mkdir:*)",
            "Bash(cp:*)",
            "Bash(mv:*)",
            "Bash(rm:*)",
            "Bash(chmod:*)",
            "Bash(ln:*)",
        ],
        "description": "Filesystem operations",
    },
}


def infer_permissions(directive: str, pattern_id: str | None = None) -> dict:
    """Infer required permissions from directive and matched pattern.

    Returns:
        dict with:
        - bundles: list of matched bundle names
        - permissions: deduplicated list of permission strings
        - descriptions: human-readable bundle descriptions
    """
    directive_lower = directive.lower()
    matched_bundles = []
    all_permissions = []
    descriptions = []

    for bundle_id, bundle in PERMISSION_BUNDLES.items():
        if any(kw in directive_lower for kw in bundle["keywords"]):
            matched_bundles.append(bundle_id)
            all_permissions.extend(bundle["permissions"])
            descriptions.append(f"{bundle_id}: {bundle['description']}")

    # Pattern-based inference (patterns often imply certain bundles)
    pattern_bundle_map = {
        "full_stack_crud": ["database", "testing", "filesystem"],
        "authentication": ["testing", "database"],
        "external_integration": ["testing"],
        "database_migration": ["database", "testing"],
        "performance_optimization": ["database", "testing"],
        "breaking_api_change": ["testing", "git"],
        "simple_refactor": ["testing", "git"],
    }

    if pattern_id and pattern_id in pattern_bundle_map:
        for bundle_id in pattern_bundle_map[pattern_id]:
            if bundle_id not in matched_bundles:
                bundle = PERMISSION_BUNDLES[bundle_id]
                matched_bundles.append(bundle_id)
                all_permissions.extend(bundle["permissions"])
                descriptions.append(f"{bundle_id}: {bundle['description']} (pattern-inferred)")

    # Deduplicate permissions while preserving order
    seen: set[str] = set()
    unique_permissions = []
    for perm in all_permissions:
        if perm not in seen:
            seen.add(perm)
            unique_permissions.append(perm)

    return {
        "bundles": matched_bundles,
        "permissions": unique_permissions,
        "descriptions": descriptions,
    }


def get_settings_path() -> Path:
    """Get path to Claude Code settings.local.json."""
    return Path.home() / ".claude" / "settings.local.json"


def load_current_permissions() -> list[str]:
    """Load currently allowed permissions from settings.local.json.

    Returns:
        List of permission strings from settings.permissions.allow
    """
    settings_path = get_settings_path()
    if not settings_path.exists():
        return []

    try:
        with open(settings_path, "r") as f:
            settings = json.load(f)
        return settings.get("permissions", {}).get("allow", [])
    except (json.JSONDecodeError, OSError):
        return []


def check_missing_permissions(inferred: dict) -> dict:
    """Compare inferred permissions against settings.local.json.

    Args:
        inferred: Result from infer_permissions()

    Returns:
        dict with:
        - current: list of currently allowed permissions
        - missing: list of inferred permissions not in settings
        - covered: list of inferred permissions already in settings
        - recommendations: formatted strings for adding to settings
    """
    current = load_current_permissions()
    inferred_perms = inferred.get("permissions", [])

    # Check which inferred permissions are missing
    missing = []
    covered = []

    for perm in inferred_perms:
        # Check if this permission is covered by current settings
        is_covered = False
        for current_perm in current:
            if perm == current_perm:
                is_covered = True
                break
            # Check if current_perm is a broader wildcard that covers this
            if current_perm.endswith(":*)"):
                current_match = re.match(r"Bash\(([^:]+):\*\)", current_perm)
                perm_match = re.match(r"Bash\(([^:]+):\*\)", perm)
                if current_match and perm_match:
                    current_cmd = current_match.group(1)
                    perm_cmd = perm_match.group(1)
                    # perm_cmd must be current_cmd exactly OR start with "current_cmd "
                    if perm_cmd == current_cmd or perm_cmd.startswith(current_cmd + " "):
                        is_covered = True
                        break

        if is_covered:
            covered.append(perm)
        else:
            missing.append(perm)

    # Format recommendations
    recommendations = []
    if missing:
        recommendations.append("Add to ~/.claude/settings.local.json under permissions.allow:")
        for perm in missing:
            recommendations.append(f'  "{perm}"')

    return {
        "current_count": len(current),
        "missing": missing,
        "covered": covered,
        "missing_count": len(missing),
        "covered_count": len(covered),
        "recommendations": recommendations,
        "all_covered": len(missing) == 0,
    }


def format_settings_snippet(missing_perms: list[str]) -> str:
    """Format missing permissions as a JSON snippet for easy copy-paste."""
    if not missing_perms:
        return ""

    lines = ['"permissions": {', '  "allow": [']
    for i, perm in enumerate(missing_perms):
        comma = "," if i < len(missing_perms) - 1 else ""
        lines.append(f'    "{perm}"{comma}')
    lines.extend(["  ]", "}"])
    return "\n".join(lines)
