import os
import subprocess
import sys
from pathlib import Path

from ci.paths import BUILD
from ci.tools import load_tools


def _list_builds() -> list[Path]:
    str_paths = os.listdir(BUILD)
    paths = [BUILD / p for p in str_paths]
    dirs = [p for p in paths if p.is_dir()]
    return dirs


def _check_build(build: Path) -> bool:
    # 1. should contain a build_info.json file
    # 2. should contain a .pio/build directory
    has_build_info = (build / "build_info.json").exists()
    has_pio_build = (build / ".pio" / "build").exists()
    return has_build_info and has_pio_build


def _prompt_build() -> Path:
    builds = _list_builds()
    if not builds:
        print("Error: No builds found", file=sys.stderr)
        sys.exit(1)
    print("Select a build:")
    for i, build in enumerate(builds):
        print(f"  [{i}]: {build}")
    while True:
        try:
            which = int(input("Enter the number of the build to use: "))
            if 0 <= which < len(builds):
                valid = _check_build(BUILD / builds[which])
                if valid:
                    return BUILD / builds[which]
                print("Error: Invalid build", file=sys.stderr)
            else:
                print("Error: Invalid selection", file=sys.stderr)
                continue
        except ValueError:
            print("Error: Invalid input", file=sys.stderr)
            continue


def _prompt_object_file(build: Path) -> Path:
    # Look for object files in .pio/build directory
    build_dir = build / ".pio" / "build"
    object_files = []

    # Walk through build directory to find .o files
    for root, _, files in os.walk(build_dir):
        for file in files:
            if file.endswith(".o") and "FrameworkArduino" not in file:
                full_path = Path(root) / file
                if "FrameworkArduino" not in full_path.parts:
                    object_files.append(full_path)

    if not object_files:
        print("Error: No object files found", file=sys.stderr)
        sys.exit(1)

    print("\nSelect an object file:")
    for i, obj_file in enumerate(object_files):
        print(f"  [{i}]: {obj_file.relative_to(build_dir)}")

    while True:
        try:
            which = int(input("Enter the number of the object file to use: "))
            if 0 <= which < len(object_files):
                return object_files[which]
            print("Error: Invalid selection", file=sys.stderr)
        except ValueError:
            print("Error: Invalid input", file=sys.stderr)
            continue


def cli() -> None:
    import argparse

    parser = argparse.ArgumentParser(
        description="Dump object file information using build tools"
    )

    parser.add_argument(
        "build_path",
        type=Path,
        nargs="?",
        help="Path to build directory containing build info JSON file",
    )

    args = parser.parse_args()
    build_path = args.build_path

    # Check if object file was provided and exists
    if build_path is None:
        build_path = _prompt_build()
    else:
        if not _check_build(build_path):
            print("Error: Invalid build directory", file=sys.stderr)
            sys.exit(1)

    assert build_path is not None
    assert build_path

    build_info_path = build_path / "build_info.json"
    assert build_info_path.exists(), f"File not found: {build_info_path}"

    tools = load_tools(build_info_path)

    object_file = _prompt_object_file(build_path)

    cmd = [str(tools.objdump_path), "--syms", str(object_file)]
    if sys.platform == "win32":
        cmd = ["cmd", "/c"] + cmd
    cmd_str = subprocess.list2cmdline(cmd)
    subprocess.run(cmd, check=True)
    print("\nDone. Command used:", cmd_str)


if __name__ == "__main__":
    try:
        cli()
    except KeyboardInterrupt:
        print("Exiting...")
        sys.exit(1)
