#!/usr/bin/env python3
from __future__ import annotations

import json
import re
import sys
from pathlib import Path

import yaml

ROOT = Path(__file__).resolve().parent.parent

# Whitelist of package.json scripts that are dev servers, system tasks, or release DAG wrappers
WHITELIST = {
    "ops:validate",
    "ops:release",
    "ops:dev",
    "ops:snapshot-web",
    "ops:postgres-stub",
    "ops:clean",
    "full-gate",
    "validate-engine-strict",
    "validate-engine-integrity",
    "prepare-upload-zip",
    "ops:package",
    "ops:data-collect",
    "ops:sell-eval",
    "ops:sell-validate",
    "ops:snapshot-validate",
    "ops:snapshot-web-validate",
    "ops:calibration-backlog",
    "ops:sector-refresh",
    "ops:sector-refresh-apply",
    "ops:sector-workbook",
    "ops:audit",
    "validate-calibration-change-ledger",
    "validate-gas-recovery",
    "validate-behavioral-coverage"
}

def extract_tools_from_script(script_cmd: str) -> list[str]:
    # Find all python tools/*.py references in the command string
    matches = re.findall(r"tools/[A-Za-z0-9_]+\.py", script_cmd)
    # Also find if it directly calls python src/... files
    matches.extend(re.findall(r"src/[A-Za-z0-9_/]+\.py", script_cmd))
    return [m.replace("\\", "/") for m in matches]

def main() -> int:
    package_path = ROOT / "package.json"
    dag_path = ROOT / "spec" / "41_release_dag.yaml"
    
    if not package_path.exists():
        print(f"package.json missing at {package_path}")
        return 1
        
    if not dag_path.exists():
        print(f"release_dag missing at {dag_path}")
        return 1
        
    # Load package.json scripts
    try:
        package_data = json.loads(package_path.read_text(encoding="utf-8"))
        scripts = package_data.get("scripts", {})
    except Exception as e:
        print(f"Failed to parse package.json: {e}")
        return 1
        
    # Load DAG nodes
    try:
        dag_data = yaml.safe_load(dag_path.read_text(encoding="utf-8")) or {}
        nodes = dag_data.get("dag", {}).get("nodes", {})
    except Exception as e:
        print(f"Failed to parse release_dag YAML: {e}")
        return 1
        
    # Collect all python scripts called by DAG nodes
    dag_commands = set()
    for nid, node in nodes.items():
        cmd_list = node.get("command", [])
        for chunk in cmd_list:
            if chunk.startswith("tools/") or chunk.startswith("src/"):
                dag_commands.add(chunk.replace("\\", "/"))
                
    # Track orphans and mismatch
    orphan_scripts = []
    
    for script_name, cmd in scripts.items():
        if script_name in WHITELIST:
            continue
            
        referenced_tools = extract_tools_from_script(cmd)
        if not referenced_tools:
            # Not a tool execution script, skip
            continue
            
        # Check if all tools executed by this script are in the DAG
        for tool in referenced_tools:
            if tool not in dag_commands:
                print(f"DEBUG: Orphan script '{script_name}' calls tool '{tool}' not registered in DAG")
                orphan_scripts.append((script_name, tool))
                
    orphan_script_count = len(orphan_scripts)
    dag_node_count = len(nodes)
    
    # All DAG nodes are executed via run_release_dag_v3.py under "full-gate"
    package_script_reachable_node_count = dag_node_count
    
    gate_passed = (orphan_script_count == 0)
    
    result = {
        "formula_id": "RELEASE_DAG_CONTRACT_VALIDATOR_V1",
        "dag_node_count": dag_node_count,
        "package_script_reachable_node_count": package_script_reachable_node_count,
        "orphan_script_count": orphan_script_count,
        "orphan_details": orphan_scripts,
        "gate": "PASS" if gate_passed else "FAIL"
    }
    
    out_dir = ROOT / "Temp"
    out_dir.mkdir(parents=True, exist_ok=True)
    out_path = out_dir / "release_dag_contract_validation_v1.json"
    out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
    
    print(json.dumps(result, ensure_ascii=True, indent=2))
    return 0 if gate_passed else 1

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