import json
import os
import shutil
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent
PROJECTS = ROOT / "projects"

results = []


def which_cmd(*names: str):
    candidates = []
    for name in names:
        candidates.append(name)
        if os.name == "nt" and not name.lower().endswith((".cmd", ".exe", ".bat", ".com")):
            candidates.extend([name + ".cmd", name + ".exe", name + ".bat", name + ".com"])
    seen = set()
    for cand in candidates:
        if cand in seen:
            continue
        seen.add(cand)
        found = shutil.which(cand)
        if found:
            return found
    return None


def run(cmd, cwd, timeout=120, env=None):
    merged_env = os.environ.copy()
    if env:
        merged_env.update(env)
    try:
        return subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout, env=merged_env)
    except FileNotFoundError as exc:
        class Dummy:
            returncode = 127
            stdout = ""
            stderr = str(exc)
        return Dummy()


def record(status, name, detail):
    results.append({"status": status, "project": name, "detail": detail})
    print(f"[{status}] {name} - {detail}")


def check_dotnet():
    proj = PROJECTS / "opsdesk-career-suite-dotnet"
    dotnet = which_cmd("dotnet")
    if not dotnet:
        record("SKIP", "opsdesk-career-suite-dotnet", "dotnet tidak ditemukan")
        return
    cp = run([dotnet, "build", ".\\OpsDeskPro.csproj" if os.name == "nt" else "./OpsDeskPro.csproj"], proj, timeout=240)
    record("PASS" if cp.returncode == 0 else "FAIL", "opsdesk-career-suite-dotnet", "build OK" if cp.returncode == 0 else cp.stderr.strip()[:220])


def check_python_app(name):
    proj = PROJECTS / name
    py = which_cmd("py", "python", "python3")
    if not py:
        record("SKIP", name, "python tidak ditemukan")
        return
    cp = run([py, "app.py", "--smoke-test"], proj, timeout=120)
    record("PASS" if cp.returncode == 0 else "FAIL", name, "smoke OK" if cp.returncode == 0 else cp.stderr.strip()[:220])


def check_go():
    proj = PROJECTS / "service-watch-dashboard-go"
    go = which_cmd("go")
    if not go:
        record("SKIP", "service-watch-dashboard-go", "go tidak ditemukan")
        return
    cp = run([go, "build", "."], proj, timeout=180)
    record("PASS" if cp.returncode == 0 else "FAIL", "service-watch-dashboard-go", "build OK" if cp.returncode == 0 else cp.stderr.strip()[:220])


def check_java():
    proj = PROJECTS / "inventory-manager-java"
    javac = which_cmd("javac")
    java = which_cmd("java")
    if not javac or not java:
        record("SKIP", "inventory-manager-java", "javac/java tidak ditemukan")
        return
    cp1 = run([javac, "InventoryManagerApp.java"], proj, timeout=180)
    if cp1.returncode != 0:
        record("FAIL", "inventory-manager-java", cp1.stderr.strip()[:220])
        return
    cp2 = run([java, "InventoryManagerApp", "--smoke-test"], proj, timeout=120)
    record("PASS" if cp2.returncode == 0 else "FAIL", "inventory-manager-java", "compile + smoke OK" if cp2.returncode == 0 else cp2.stderr.strip()[:220])


def check_typescript():
    proj = PROJECTS / "helpdesk-ticket-api-typescript"
    node = which_cmd("node")
    npm = which_cmd("npm")
    if not node or not npm:
        record("SKIP", "helpdesk-ticket-api-typescript", "node/npm tidak ditemukan")
        return

    node_modules = proj / "node_modules"
    if not node_modules.exists():
        cp1 = run([npm, "install", "--no-fund", "--no-audit"], proj, timeout=300)
        if cp1.returncode != 0:
            record("FAIL", "helpdesk-ticket-api-typescript", cp1.stderr.strip()[:220] or cp1.stdout.strip()[:220])
            return

    tsc_local = proj / "node_modules" / ".bin" / ("tsc.cmd" if os.name == "nt" else "tsc")
    tsc = str(tsc_local) if tsc_local.exists() else which_cmd("tsc")
    if not tsc:
        record("FAIL", "helpdesk-ticket-api-typescript", "tsc tidak ditemukan, npm install mungkin gagal")
        return

    cp2 = run([tsc, "-p", "."], proj, timeout=180)
    if cp2.returncode != 0:
        record("FAIL", "helpdesk-ticket-api-typescript", cp2.stderr.strip()[:220] or cp2.stdout.strip()[:220])
        return

    cp3 = run([node, "dist/index.js", "--smoke-test"], proj, timeout=120)
    record("PASS" if cp3.returncode == 0 else "FAIL", "helpdesk-ticket-api-typescript", "build + smoke OK" if cp3.returncode == 0 else cp3.stderr.strip()[:220])


def check_ruby():
    proj = PROJECTS / "incident-report-builder-ruby"
    ruby = which_cmd("ruby")
    if not ruby:
        record("SKIP", "incident-report-builder-ruby", "ruby tidak ditemukan")
        return
    cp = run([ruby, "app.rb", "--smoke-test"], proj, timeout=120)
    record("PASS" if cp.returncode == 0 else "FAIL", "incident-report-builder-ruby", "smoke OK" if cp.returncode == 0 else cp.stderr.strip()[:220])


def check_shell():
    proj = PROJECTS / "diagnostics-toolkit-shell"
    bash = which_cmd("bash")
    if not bash:
        record("SKIP", "diagnostics-toolkit-shell", "bash tidak ditemukan")
        return
    cp = run([bash, "linux_diagnostics.sh", "--smoke-test"], proj, timeout=120)
    record("PASS" if cp.returncode == 0 else "FAIL", "diagnostics-toolkit-shell", "smoke OK" if cp.returncode == 0 else cp.stderr.strip()[:220])


def check_rust():
    proj = PROJECTS / "log-audit-console-rust"
    cargo = which_cmd("cargo")
    if not cargo:
        record("SKIP", "log-audit-console-rust", "cargo tidak ditemukan")
        return
    cp = run([cargo, "run", "--", "--smoke-test"], proj, timeout=240)
    record("PASS" if cp.returncode == 0 else "FAIL", "log-audit-console-rust", "build + smoke OK" if cp.returncode == 0 else cp.stderr.strip()[:220])


def main():
    check_dotnet()
    check_python_app("netops-monitor-python")
    check_go()
    check_java()
    check_typescript()
    check_ruby()
    check_python_app("sqlite-reporting-workbench-python")
    check_shell()
    check_rust()

    out = ROOT / "validation_results.json"
    out.write_text(json.dumps(results, indent=2), encoding="utf-8")
    failed = [r for r in results if r["status"] == "FAIL"]
    sys.exit(1 if failed else 0)


if __name__ == "__main__":
    main()
