#!/usr/bin/env python3
import json
import subprocess
from pathlib import Path
from datetime import datetime, timezone

ROOT = Path("/home/yeff/public_html/devon")
CANON_EXEC = ROOT / "canon" / "execution"
CATEGORY_ID = "panel_canonical_tree"

CATEGORY_CONTRACT = CANON_EXEC / "categories" / f"{CATEGORY_ID}.contract.json"
STATE_DIR = CANON_EXEC / "state"
STATE_FILE = STATE_DIR / f"{CATEGORY_ID}.resolved.json"

ITEM_FILES = [
    CANON_EXEC / "items" / "panel_canonical_tree_001_verify_master_binding.contract.json",
    CANON_EXEC / "items" / "panel_canonical_tree_002_verify_category_contract.contract.json",
    CANON_EXEC / "items" / "panel_canonical_tree_003_verify_legacy_panel_absence.contract.json",
]

SCAN_FILE = CANON_EXEC / "scans" / "panel_canonical_tree_category_scan.contract.json"
REFRESH_FILE = CANON_EXEC / "refresh" / "panel_canonical_tree_refresh.contract.json"
PROGRESS_FILE = CANON_EXEC / "progress" / "panel_canonical_tree_category_readiness.contract.json"

FORBIDDEN_ABSOLUTE_PATHS = [
    "/home/yeff/public_html/devon/canon/execution",
    "/home/yeff/public_html/devon/canon/execution/state/dh_sources",
]

def utc_now():
    return datetime.now(timezone.utc).isoformat()

def read_json(path):
    return json.loads(path.read_text(encoding="utf-8"))

def run_command(command, timeout=30):
    if not command.strip():
        return {
            "status": "MISSING",
            "exit_code": None,
            "stdout": "",
            "stderr": "missing command"
        }

    try:
        result = subprocess.run(
            command,
            shell=True,
            cwd=str(ROOT),
            text=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            timeout=timeout,
        )
        stdout = (result.stdout or "").strip()
        stderr = (result.stderr or "").strip()

        if result.returncode != 0:
            status = "FAIL"
        elif "FAIL" in stdout:
            status = "FAIL"
        else:
            status = "PASS"

        return {
            "status": status,
            "exit_code": result.returncode,
            "stdout": stdout[-4000:],
            "stderr": stderr[-4000:]
        }
    except subprocess.TimeoutExpired as exc:
        return {
            "status": "FAIL",
            "exit_code": None,
            "stdout": (exc.stdout or "")[-4000:] if isinstance(exc.stdout, str) else "",
            "stderr": "command timeout"
        }
    except Exception as exc:
        return {
            "status": "FAIL",
            "exit_code": None,
            "stdout": "",
            "stderr": str(exc)
        }

def resolve_item(path):
    data = read_json(path)
    item_id = data.get("item_id", path.stem)

    evidence_command = data.get("evidence", {}).get("evidence_command", "")
    validation_command = data.get("validation", {}).get("validation_command", "")

    evidence_result = run_command(evidence_command, data.get("validation", {}).get("validation_timeout_seconds", 30))
    validation_result = run_command(validation_command, data.get("validation", {}).get("validation_timeout_seconds", 30))

    final_status = "PASS" if evidence_result["status"] == "PASS" and validation_result["status"] == "PASS" else "FAIL"

    progress_percent = 0
    checkpoints = data.get("progress", {}).get("checkpoints", [])
    checkpoint_results = []

    for checkpoint in checkpoints:
        checkpoint_command = checkpoint.get("evidence_command", "")
        if checkpoint_command.startswith("query "):
            checkpoint_result = {
                "status": final_status,
                "exit_code": 0 if final_status == "PASS" else 1,
                "stdout": final_status,
                "stderr": ""
            }
        else:
            checkpoint_result = run_command(checkpoint_command, 30)

        checkpoint_results.append({
            "percent": checkpoint.get("percent", 0),
            "condition": checkpoint.get("condition", ""),
            "status": checkpoint_result["status"],
            "stdout": checkpoint_result["stdout"],
            "stderr": checkpoint_result["stderr"]
        })

        if checkpoint_result["status"] == "PASS":
            progress_percent = max(progress_percent, int(checkpoint.get("percent", 0)))

    return {
        "item_id": item_id,
        "item_contract_path": str(path),
        "evidence_status": evidence_result["status"],
        "validation_status": validation_result["status"],
        "final_status": final_status,
        "progress_percent": progress_percent,
        "evidence_stdout": evidence_result["stdout"],
        "evidence_stderr": evidence_result["stderr"],
        "validation_stdout": validation_result["stdout"],
        "validation_stderr": validation_result["stderr"],
        "checkpoint_results": checkpoint_results,
        "missing_to_100": [] if final_status == "PASS" and progress_percent == 100 else data.get("progress", {}).get("missing_to_100", [])
    }

def resolve_progress(path):
    data = read_json(path)
    checkpoints = data.get("checkpoints", [])
    current_percent = 0
    results = []

    for checkpoint in checkpoints:
        command = checkpoint.get("evidence_command", "")
        result = run_command(command, 30)
        status = result["status"]

        results.append({
            "percent": checkpoint.get("percent", 0),
            "condition": checkpoint.get("condition", ""),
            "status": status,
            "stdout": result["stdout"],
            "stderr": result["stderr"]
        })

        if status == "PASS":
            current_percent = max(current_percent, int(checkpoint.get("percent", 0)))

    final_status = "PASS" if current_percent == 100 else ("MISSING" if current_percent == 0 else "FAIL")

    return {
        "progress_contract_path": str(path),
        "current_percent": current_percent,
        "final_status": final_status,
        "checkpoint_results": results,
        "missing_to_100": [] if current_percent == 100 else data.get("missing_to_100", [])
    }

def main():
    STATE_DIR.mkdir(parents=True, exist_ok=True)

    required_files = [CATEGORY_CONTRACT, SCAN_FILE, REFRESH_FILE, PROGRESS_FILE] + ITEM_FILES
    missing_files = [str(p) for p in required_files if not p.exists()]

    serialized_contracts = ""
    json_errors = []

    for path in required_files:
        if not path.exists():
            continue
        try:
            serialized_contracts += path.read_text(encoding="utf-8")
            json.loads(path.read_text(encoding="utf-8"))
        except Exception as exc:
            json_errors.append({"path": str(path), "error": str(exc)})

    forbidden_found = [x for x in FORBIDDEN_ABSOLUTE_PATHS if x in serialized_contracts]

    item_results = [resolve_item(path) for path in ITEM_FILES if path.exists()]
    progress_result = resolve_progress(PROGRESS_FILE) if PROGRESS_FILE.exists() else {
        "current_percent": 0,
        "final_status": "MISSING",
        "checkpoint_results": [],
        "missing_to_100": []
    }

    scan_contract = read_json(SCAN_FILE)
    scan_result = run_command(scan_contract.get("scan_command", ""), 30)

    refresh_contract = read_json(REFRESH_FILE)
    refresh_checks = []
    for command in refresh_contract.get("checks_to_run", []):
        result = run_command(command, 30)
        refresh_checks.append({
            "command": command,
            "status": result["status"],
            "stdout": result["stdout"],
            "stderr": result["stderr"]
        })

    item_pass_count = sum(1 for item in item_results if item["final_status"] == "PASS")
    item_total = len(item_results)

    final_status = "PASS"
    blocking_reasons = []

    if missing_files:
        final_status = "MISSING"
        blocking_reasons.append("required_contract_file_missing")

    if json_errors:
        final_status = "FAIL"
        blocking_reasons.append("json_parse_error")

    if forbidden_found:
        final_status = "FAIL"
        blocking_reasons.append("forbidden_absolute_path_found")

    if item_total == 0 or item_pass_count < item_total:
        final_status = "FAIL"
        blocking_reasons.append("one_or_more_items_not_pass")

    if progress_result["current_percent"] < 100:
        final_status = "FAIL"
        blocking_reasons.append("progress_not_100")

    if scan_result["status"] != "PASS":
        final_status = "FAIL"
        blocking_reasons.append("category_scan_failed")

    if any(x["status"] != "PASS" for x in refresh_checks):
        final_status = "FAIL"
        blocking_reasons.append("refresh_check_failed")

    state = {
        "state_id": f"resolved_state.{CATEGORY_ID}",
        "version": "v1",
        "resolved_at": utc_now(),
        "category_id": CATEGORY_ID,
        "category_contract_path": str(CATEGORY_CONTRACT),
        "state_source": "minimal_state_resolver_v1",
        "final_status": final_status,
        "progress_percent": progress_result["current_percent"],
        "item_pass_count": item_pass_count,
        "item_total": item_total,
        "scan_status": scan_result["status"],
        "refresh_status": "PASS" if refresh_checks and all(x["status"] == "PASS" for x in refresh_checks) else "FAIL",
        "missing_files": missing_files,
        "json_errors": json_errors,
        "forbidden_absolute_paths_found": forbidden_found,
        "blocking_reasons": blocking_reasons,
        "item_results": item_results,
        "scan_result": scan_result,
        "refresh_checks": refresh_checks,
        "progress_result": progress_result,
        "next_required_action": "create_next_category_execution_contract" if final_status == "PASS" else "fix_failed_contract_or_evidence"
    }

    STATE_FILE.write_text(json.dumps(state, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")

    print("STATE_RESOLVER_STATUS=PASS")
    print("STATE_FILE=", STATE_FILE)
    print("CATEGORY_ID=", CATEGORY_ID)
    print("FINAL_STATUS=", final_status)
    print("PROGRESS_PERCENT=", progress_result["current_percent"])
    print("ITEM_PASS_COUNT=", item_pass_count)
    print("ITEM_TOTAL=", item_total)
    print("SCAN_STATUS=", state["scan_status"])
    print("REFRESH_STATUS=", state["refresh_status"])
    print("BLOCKING_REASONS=", ",".join(blocking_reasons))

if __name__ == "__main__":
    main()
