#!/usr/bin/env python3
"""
DSCons Local Blast Radius Calculator & Acknowledgment Engine.
Integrates with Global Native Impact Guard Hook (PreToolUse).
Supports --target, --format json, and --ack.
"""

from __future__ import annotations

import argparse
import ast
import json
import os
import sys
import time
from pathlib import Path
from typing import Any, Dict, List

if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8")
if hasattr(sys.stderr, "reconfigure"):
    sys.stderr.reconfigure(encoding="utf-8")

ACK_CACHE_FILE = Path(".gemini/blast_radius_ack.json")
ACK_VALIDITY_SECONDS = 30 * 60  # 30 minutes


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


def _save_acks(acks: Dict[str, float]) -> None:
    ACK_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
    try:
        with open(ACK_CACHE_FILE, "w", encoding="utf-8") as f:
            json.dump(acks, f, ensure_ascii=True, indent=2)
    except Exception:
        pass


def _is_acknowledged(target_file: str) -> bool:
    acks = _load_acks()
    norm = os.path.abspath(target_file).lower()
    now = time.time()
    ts = acks.get(norm, 0)
    return (now - ts) < ACK_VALIDITY_SECONDS


def _record_acknowledgment(target_file: str) -> None:
    acks = _load_acks()
    norm = os.path.abspath(target_file).lower()
    acks[norm] = time.time()
    _save_acks(acks)


def _scan_dependents(workspace_root: str, target_file: str) -> List[str]:
    stem = os.path.splitext(os.path.basename(target_file))[0]
    dependents = []

    for root, dirs, files in os.walk(workspace_root):
        # Prune heavy/irrelevant dirs (< 100ms response)
        dirs[:] = [d for d in dirs if d not in {".git", ".gemini", "node_modules", ".venv", "__pycache__", "dist"}]
        for f in files:
            if not f.endswith(".py"):
                continue
            full_p = os.path.join(root, f)
            if os.path.abspath(full_p) == os.path.abspath(target_file):
                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(full_p)
                            break
            except Exception:
                continue
    return dependents


def main() -> int:
    parser = argparse.ArgumentParser(description="DSCons Blast Radius Calculator")
    parser.add_argument("--target", required=True, help="Target file path")
    parser.add_argument("--format", default="text", choices=["text", "json"], help="Output format")
    parser.add_argument("--ack", action="store_true", help="Acknowledge blast radius and unlock edits for 30m")
    args = parser.parse_args()

    workspace_root = os.path.abspath(".")
    target_abs = os.path.abspath(args.target)

    if args.ack:
        _record_acknowledgment(target_abs)
        dependents = _scan_dependents(workspace_root, target_abs)
        print(f"[BLAST RADIUS ACKNOWLEDGED] Target: {args.target}")
        print(f"Total dependents: {len(dependents)}")
        for d in dependents[:10]:
            print(f"  - {os.path.relpath(d, workspace_root)}")
        if len(dependents) > 10:
            print(f"  ... and {len(dependents) - 10} more.")
        print("[UNLOCKED] Target unlocked for editing for the next 30 minutes.")
        return 0

    is_ack = _is_acknowledged(target_abs)
    dependents = _scan_dependents(workspace_root, target_abs)

    if is_ack:
        data = {
            "target": args.target,
            "blocked": False,
            "dependents_count": len(dependents),
            "reason": f"Target unlocked via active acknowledgment ({len(dependents)} dependents).",
        }
    else:
        # Block if >= 6 dependents
        if len(dependents) >= 6:
            data = {
                "target": args.target,
                "blocked": True,
                "dependents_count": len(dependents),
                "reason": (
                    f"[BLAST RADIUS BLOCKED] File '{os.path.basename(args.target)}' has {len(dependents)} "
                    f"dependents. Run 'python tools/analysis/blast_radius.py --target {args.target} --ack' to review and unlock."
                ),
            }
        else:
            data = {
                "target": args.target,
                "blocked": False,
                "dependents_count": len(dependents),
                "reason": f"Safe blast radius ({len(dependents)} dependents).",
            }

    if args.format == "json":
        print(json.dumps(data, ensure_ascii=True))
    else:
        print(f"Target: {args.target} | Blocked: {data['blocked']} | Dependents: {len(dependents)}")

    return 0


if __name__ == "__main__":
    sys.exit(main())
