from __future__ import annotations

import argparse
from pathlib import Path

from ..formatting import h16, parse_int
from .eeprom_image import write_eeprom_snapshot
from .memory import describe_regions
from .runner import H8536Emulator


def discover_rom_path(root: Path) -> Path | None:
    candidates = [
        root / "ROM" / "M27C512@DIP28_1.BIN",
        root / "rom.bin",
    ]
    candidates.extend(sorted((root / "ROM").glob("*.BIN")) if (root / "ROM").exists() else [])
    candidates.extend(sorted((root / "ROM").glob("*.bin")) if (root / "ROM").exists() else [])
    for candidate in candidates:
        if candidate.is_file():
            return candidate
    return None


def load_rom(path: Path | None = None, root: Path | None = None) -> tuple[bytes, Path]:
    root = root if root is not None else Path.cwd()
    rom_path = path if path is not None else discover_rom_path(root)
    if rom_path is None:
        raise FileNotFoundError(
            "could not discover ROM bytes; pass --rom PATH, expected ROM/M27C512@DIP28_1.BIN or another ROM/*.BIN"
        )
    return rom_path.read_bytes(), rom_path


def build_arg_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Minimal H8/536 emulation harness scaffold")
    parser.add_argument("--rom", type=Path, help="ROM image path; defaults to ROM/M27C512@DIP28_1.BIN when present")
    parser.add_argument("--max-steps", type=int, default=64, help="maximum CPU steps to execute")
    parser.add_argument("--trace", action="store_true", help="print decoded/executed instruction trace")
    parser.add_argument("--stop-on-heartbeat", action="store_true", help="stop only when 00 00 00 00 80 DA is emitted through SCI1 TDR")
    parser.add_argument("--memory-map", action="store_true", help="print the scaffolded memory map before running")
    parser.add_argument("--interval-steps", type=int, default=2048, help="rough step period for the scaffolded timer interrupt")
    parser.add_argument("--clock-hz", type=parse_int, default=10_000_000, help="CPU/phi clock in Hz for calibrated FRT timing")
    parser.add_argument("--frt1-ocia-steps", type=int, default=None, help="legacy step-period override for FRT1 OCIA")
    parser.add_argument("--frt2-ocia-steps", type=int, default=None, help="legacy step-period override for FRT2 OCIA")
    parser.add_argument("--p9-fast-path", action="store_true", help="shortcut known P9 bit-banged transfer routines for exploration")
    parser.add_argument("--p9-fast-input", type=parse_int, default=0xFF, help="default byte returned by the P9 fast-path read routine")
    parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="legacy fallback for older wrapper experiments; known BFE0/BFFE wrappers use the X24164 model")
    parser.add_argument("--p7-input", type=parse_int, default=0xFF, help="external P7 pin state for input bits; DIP-off board default is 0xFF")
    parser.add_argument("--eeprom-seed", choices=("blank", "factory"), default="blank", help="initial X24164/shadow state before reset")
    parser.add_argument("--eeprom-load", type=Path, help="load a 0x1000-byte logical EEPROM image before running")
    parser.add_argument("--eeprom-save", type=Path, help="save the final 0x1000-byte logical EEPROM image after running")
    parser.add_argument("--eeprom-report", type=Path, help="write a readable EEPROM snapshot report after running")
    parser.add_argument("--eeprom-report-json", type=Path, help="write a structured EEPROM snapshot report after running")
    parser.add_argument("--eeprom-report-include-image", action="store_true", help="include the full EEPROM image as hex in JSON reports")
    return parser


def main(argv: list[str] | None = None) -> int:
    args = build_arg_parser().parse_args(argv)
    try:
        rom_bytes, rom_path = load_rom(args.rom)
    except FileNotFoundError as exc:
        print(str(exc))
        return 2

    emulator = H8536Emulator(
        rom_bytes,
        interval_steps=args.interval_steps,
        frt1_ocia_steps=args.frt1_ocia_steps,
        frt2_ocia_steps=args.frt2_ocia_steps,
        clock_hz=args.clock_hz,
        p9_fast_path_enabled=args.p9_fast_path,
        p9_fast_default_input_byte=args.p9_fast_input,
        p9_fast_default_wrapper_success=args.p9_fast_optimistic_wrapper,
        p7_input=args.p7_input,
        eeprom_seed=args.eeprom_seed,
    )
    if args.eeprom_load:
        emulator.memory.load_eeprom_image(args.eeprom_load.read_bytes())
        print(f"eeprom_loaded={args.eeprom_load}")
    print(f"rom={rom_path}")
    print(f"reset_vector={h16(emulator.reset_vector())}")
    if args.memory_map:
        print(describe_regions())
    report = emulator.run(args.max_steps, trace=args.trace, stop_on_heartbeat=args.stop_on_heartbeat)
    if args.trace:
        for line in report.trace:
            print(line)
    for line in report.summary_lines():
        print(line)
    if not report.heartbeat_seen:
        print("heartbeat_status=not reached; no heartbeat is reported unless bytes are emitted via SCI1_TDR")
    if args.eeprom_save:
        args.eeprom_save.parent.mkdir(parents=True, exist_ok=True)
        args.eeprom_save.write_bytes(emulator.memory.dump_eeprom_image())
        print(f"eeprom_saved={args.eeprom_save}")
    if args.eeprom_report:
        write_eeprom_snapshot(emulator.memory, args.eeprom_report, rom_bytes=rom_bytes)
        print(f"eeprom_report={args.eeprom_report}")
    if args.eeprom_report_json:
        write_eeprom_snapshot(
            emulator.memory,
            args.eeprom_report_json,
            rom_bytes=rom_bytes,
            as_json=True,
            include_image_hex=args.eeprom_report_include_image,
        )
        print(f"eeprom_report_json={args.eeprom_report_json}")
    return 0
