#!/usr/bin/env python3
"""Local Blast Radius Calculator for Muse2API.
Analyzes cross-module AST dependencies and manages safety acknowledgments (--ack).
"""
from __future__ import annotations

import argparse
import ast
import json
import os
import sys
import time
from pathlib import Path

ACK_FILE = Path(__file__).resolve().parent.parent.parent / ".blast_radius_ack.json"
ACK_WINDOW_SECONDS = 1800  # 30 minutes


def load_acks() -> dict[str, float]:
    if ACK_FILE.exists():
        try:
            with open(ACK_FILE, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception:
            return {}
    return {}


def save_ack(target_path: str) -> None:
    acks = load_acks()
    # prune expired
    now = time.time()
    acks = {k: v for k, v in acks.items() if now - v < ACK_WINDOW_SECONDS}
    acks[os.path.abspath(target_path)] = now
    with open(ACK_FILE, "w", encoding="utf-8") as f:
        json.dump(acks, f, indent=2, ensure_ascii=True)


def is_acknowledged(target_path: str) -> bool:
    acks = load_acks()
    now = time.time()
    ts = acks.get(os.path.abspath(target_path), 0)
    return (now - ts) < ACK_WINDOW_SECONDS


def find_dependents(workspace_root: str, target_file: str) -> list[str]:
    target_abs = os.path.abspath(target_file)
    stem = os.path.splitext(os.path.basename(target_file))[0]
    dependents = []

    for root, dirs, files in os.walk(workspace_root):
        dirs[:] = [d for d in dirs if d not in [".git", ".gemini", ".venv", "venv", "__pycache__", "data", "dist", "build"]]
        for f in files:
            if not f.endswith(".py"):
                continue
            full_p = os.path.join(root, f)
            if os.path.abspath(full_p) == target_abs:
                continue
            try:
                with open(full_p, "r", encoding="utf-8", errors="ignore") as src:
                    content = src.read()
                if stem in content:
                    tree = ast.parse(content, filename=full_p)
                    for node in ast.walk(tree):
                        if isinstance(node, (ast.Import, ast.ImportFrom)):
                            dependents.append(os.path.relpath(full_p, workspace_root))
                            break
            except Exception:
                continue
    return sorted(list(set(dependents)))


def main() -> None:
    parser = argparse.ArgumentParser(description="Muse2API Blast Radius Calculator")
    parser.add_argument("--target", required=True, help="Target file path")
    parser.add_argument("--ack", action="store_true", help="Acknowledge impact and unlock for 30 minutes")
    parser.add_argument("--format", choices=["text", "json"], default="text", help="Output format")

    args = parser.parse_args()
    workspace_root = str(Path(__file__).resolve().parent.parent.parent)
    target_path = os.path.abspath(args.target)

    if args.ack:
        save_ack(target_path)
        dependents = find_dependents(workspace_root, target_path)
        out = {
            "target": os.path.relpath(target_path, workspace_root),
            "acknowledged": True,
            "blocked": False,
            "dependents_count": len(dependents),
            "dependents": dependents,
            "reason": f"Target unlocked for 30 minutes via --ack. Impacted dependents: {len(dependents)}.",
        }
        if args.format == "json":
            print(json.dumps(out, indent=2, ensure_ascii=True))
        else:
            print(f"[ACKNOWLEDGED] {out['reason']}")
            for d in dependents:
                print(f"  - {d}")
        sys.exit(0)

    # Check ack
    if is_acknowledged(target_path):
        dependents = find_dependents(workspace_root, target_path)
        out = {
            "target": os.path.relpath(target_path, workspace_root),
            "acknowledged": True,
            "blocked": False,
            "dependents_count": len(dependents),
            "dependents": dependents,
            "reason": f"Target cleared by prior acknowledgment. Impacted dependents: {len(dependents)}.",
        }
        if args.format == "json":
            print(json.dumps(out, indent=2, ensure_ascii=True))
        else:
            print(f"[ALLOWED] {out['reason']}")
        sys.exit(0)

    dependents = find_dependents(workspace_root, target_path)
    blocked = len(dependents) >= 6
    out = {
        "target": os.path.relpath(target_path, workspace_root),
        "acknowledged": False,
        "blocked": blocked,
        "dependents_count": len(dependents),
        "dependents": dependents,
        "reason": (
            f"File '{os.path.basename(target_path)}' has {len(dependents)} dependents across workspace. Run with --ack to confirm impact and proceed."
            if blocked else f"Safe to edit: {len(dependents)} dependents."
        ),
    }

    if args.format == "json":
        print(json.dumps(out, indent=2, ensure_ascii=True))
    else:
        status = "[BLOCKED]" if blocked else "[ALLOWED]"
        print(f"{status} {out['reason']}")
        for d in dependents:
            print(f"  - {d}")

    sys.exit(1 if blocked else 0)


if __name__ == "__main__":
    main()
