#!/usr/bin/env python3
"""
Nyash llvmlite harness (internal)

Primary AOT/EXE pipeline is the ny-llvmc crate backend. This script serves as
an internal harness that ny-llvmc delegates to for object emission.

Usage (debugging only):
  - python3 tools/llvmlite_harness.py --out out.o                # dummy ny_main -> object
  - python3 tools/llvmlite_harness.py --in mir.json --out out.o  # MIR(JSON) -> object (partial support)

Notes:
  - Without --in, emits a trivial ny_main that returns 0.
  - With --in, delegates to src/llvm_py/llvm_builder.py.
"""

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

_default_root = Path(__file__).resolve().parents[1]
_env_root = None
try:
    env_root_str = os.environ.get('NYASH_ROOT')
    if env_root_str:
        cand = Path(env_root_str).resolve()
        if (cand / "src" / "llvm_py" / "llvm_builder.py").exists():
            _env_root = cand
except Exception:
    _env_root = None

ROOT = _env_root or _default_root
PY_BUILDER = ROOT / "src" / "llvm_py" / "llvm_builder.py"

_OPT_ENV_KEYS = ("HAKO_LLVM_OPT_LEVEL", "NYASH_LLVM_OPT_LEVEL")

def _parse_opt_level_env() -> int:
    """Parse optimization level from env (0-3, default 2)."""
    for key in _OPT_ENV_KEYS:
        raw = os.environ.get(key)
        if not raw:
            continue
        value = raw.strip()
        if not value:
            continue
        upper = value.upper()
        if upper.startswith("O"):
            value = upper[1:]
        try:
            lvl = int(value)
        except ValueError:
            continue
        if lvl < 0:
            lvl = 0
        if lvl > 3:
            lvl = 3
        return lvl
    return 2

def _resolve_llvm_opt_level():
    level = _parse_opt_level_env()
    try:
        import llvmlite.binding as llvm_binding
        names = {0: "None", 1: "Less", 2: "Default", 3: "Aggressive"}
        attr = names.get(level, "Default")
        enum = getattr(llvm_binding, "CodeGenOptLevel")
        return getattr(enum, attr)
    except Exception:
        return level

def _maybe_trace_opt(source: str) -> None:
    if os.environ.get("NYASH_CLI_VERBOSE") == "1":
        try:
            level = _parse_opt_level_env()
            print(f"[llvmlite harness] opt-level={level} ({source})", file=sys.stderr)
        except Exception:
            pass

def run_dummy(out_path: str) -> None:
    # Minimal llvmlite program: ny_main() -> i32 0
    import llvmlite.ir as ir
    import llvmlite.binding as llvm

    llvm.initialize()
    llvm.initialize_native_target()
    llvm.initialize_native_asmprinter()

    mod = ir.Module(name="nyash_module")
    i32 = ir.IntType(32)
    ny_main_ty = ir.FunctionType(i32, [])
    ny_main = ir.Function(mod, ny_main_ty, name="ny_main")
    entry = ny_main.append_basic_block("entry")
    b = ir.IRBuilder(entry)
    b.ret(ir.Constant(i32, 0))

    # Emit object via target machine
    m = llvm.parse_assembly(str(mod))
    m.verify()
    target = llvm.Target.from_default_triple()
    tm = target.create_target_machine(opt=_resolve_llvm_opt_level())
    _maybe_trace_opt("dummy")
    obj = tm.emit_object(m)
    Path(out_path).parent.mkdir(parents=True, exist_ok=True)
    with open(out_path, "wb") as f:
        f.write(obj)

def run_from_json(in_path: str, out_path: str) -> None:
    # Delegate to python builder to keep code unified
    import runpy
    # Enable safe defaults for prepasses unless explicitly disabled by env
    os.environ.setdefault('NYASH_LLVM_PREPASS_LOOP', os.environ.get('NYASH_LLVM_PREPASS_LOOP', '0'))
    os.environ.setdefault('NYASH_LLVM_PREPASS_IFMERGE', os.environ.get('NYASH_LLVM_PREPASS_IFMERGE', '1'))
    # Ensure src/llvm_py is on sys.path for relative imports
    builder_dir = str(PY_BUILDER.parent)
    if builder_dir not in sys.path:
        sys.path.insert(0, builder_dir)
    # Simulate "python llvm_builder.py <in> -o <out>"
    sys.argv = [str(PY_BUILDER), str(in_path), "-o", str(out_path)]
    runpy.run_path(str(PY_BUILDER), run_name="__main__")

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--in", dest="infile", help="MIR JSON input", default=None)
    ap.add_argument("--out", dest="outfile", help="output object (.o)", required=True)
    args = ap.parse_args()

    if args.infile is None:
        # Dummy path
        run_dummy(args.outfile)
        print(f"[harness] dummy object written: {args.outfile}")
    else:
        run_from_json(args.infile, args.outfile)
        print(f"[harness] object written: {args.outfile}")

if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        import traceback
        print(f"[harness] error: {e}", file=sys.stderr)
        if os.environ.get('NYASH_CLI_VERBOSE') == '1':
            traceback.print_exc()
        sys.exit(1)
