# Copyright 2024 Caleb Connolly
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import annotations

import pmb.config
from pmb.commands.repo_missing import repo_missing
from pmb.core.context import get_context
from pmb.helpers import frontend
from pmb.types import PmbArgs

from .apkbuild_parse import apkbuild_parse
from .apkindex_parse import apkindex_parse
from .aportgen import aportgen
from .bootimg_analyze import bootimg_analyze
from .build import build
from .build_init import build_init
from .checksum import checksum
from .chroot import chroot
from .ci import ci
from .config import config
from .export import export
from .flasher import flasher
from .index import index
from .initfs import initfs
from .kconfig import KConfigCheck, KConfigEdit, KConfigGenerate, KConfigMigrate
from .log import log
from .netboot import netboot
from .newapkbuild import newapkbuild
from .pkgrel_bump import pkgrel_bump
from .pkgver_bump import pkgver_bump
from .pull import pull
from .qemu import qemu
from .shutdown import shutdown
from .sideload import sideload
from .stats import stats
from .status import status
from .test import test
from .update import update
from .work_migrate import work_migrate
from .zap import zap

"""New way to model pmbootstrap subcommands that can be invoked without PmbArgs."""

# Commands that are still invoked via pmb/helpers/frontend.py
unmigrated_commands = [
    "init",
    "install",
]


def run_command(args: PmbArgs) -> None:
    # Handle deprecated command format
    if args.action in unmigrated_commands:
        getattr(frontend, args.action)(args)
        return

    match args.action:
        case "apkbuild_parse":
            apkbuild_parse(args.packages)
        case "apkindex_parse":
            apkindex_parse(args.apkindex_path, args.package)
        case "aportgen":
            aportgen(args.packages, args.fork_alpine, args.fork_alpine_retain_branch)
        case "bootimg_analyze":
            bootimg_analyze(args.path)
        case "build":
            build(args.packages, args.arch, args.src, args.envkernel, args.strict)
        case "build_init":
            build_init(frontend._parse_suffix(args))
        case "chroot":
            chroot(
                args.add,
                frontend._parse_suffix(args),
                args.chroot_usb,
                args.command,
                args.install_blockdev,
                args.output,
                getattr(args, "sector_size", None),
                args.user,
                args.xauth,
            )
        case "checksum":
            checksum(args.packages, args.changed, args.verify)
        case "ci":
            ci(args.scripts, args.all, args.fast)
        case "config":
            config(args.name, args.value, args.reset, args.config)
        case "export":
            export(args.export_folder, args.autoinstall, args.odin_flashable_tar)
        case "flasher":
            flasher(
                args.action_flasher,
                # FIXME: defaults copied from pmb/helpers/arguments.py
                # we should have these defaults defined in one place!
                getattr(args, "autoinstall", True),
                getattr(args, "cmdline", None),
                args.flash_method,
                getattr(args, "no_reboot", None),
                getattr(args, "partition", None),
                getattr(args, "resume", None),
            )
        case "log":
            log(args.clear_log, args.lines)
        case "index":
            # FIXME: should index support --arch?
            index()
        case "initfs":
            initfs(args.action_initfs, args.hook if "hook" in args else None)
        case "shutdown":
            shutdown()
        case "sideload":
            sideload(
                args.user or get_context().config.user,
                args.host,
                str(args.port),
                args.arch,
                args.install_key,
                args.packages,
            )
        case "test":
            test(args.action_test)
        case "netboot":
            netboot(args.action_netboot, args.replace)
        case "newapkbuild":
            # Passthrough: Strings (e.g. -d "my description")
            pass_through = []
            for entry in pmb.config.newapkbuild_arguments_strings:
                value = getattr(args, entry[1])
                if value:
                    pass_through += [entry[0], value]

            # Passthrough: Switches (e.g. -C for CMake)
            for entry in (
                pmb.config.newapkbuild_arguments_switches_pkgtypes
                + pmb.config.newapkbuild_arguments_switches_other
            ):
                if getattr(args, entry[1]) is True:
                    pass_through.append(entry[0])

            # Passthrough: PKGNAME[-PKGVER] | SRCURL
            pass_through.append(args.pkgname_pkgver_srcurl)
            newapkbuild(args.folder, pass_through, args.pkgname, args.pkgname_pkgver_srcurl)
        case "pkgrel_bump":
            pkgrel_bump(args.packages, args.dry, args.auto)
        case "pkgver_bump":
            pkgver_bump(args.packages)
        case "pull":
            pull()
        case "qemu":
            qemu(
                args.cmdline,
                args.qemu_audio,
                args.qemu_cpu,
                args.qemu_display,
                args.qemu_video,
                args.memory,
                args.image_size,
                args.second_storage,
                args.port,
                args.efi,
                args.host_qemu,
                args.qemu_gl,
                args.qemu_kvm,
                args.qemu_tablet,
            )
        case "kconfig":
            match args.action_kconfig:
                case "check":
                    categories = args.categories.split(",") if args.categories else []
                    KConfigCheck(
                        args.kconfig_check_details,
                        args.file,
                        args.package,
                        args.keep_going,
                        categories,
                    ).run()
                case "edit":
                    KConfigEdit(
                        args.package[0], args.arch, args.xconfig, args.nconfig, args.fragment
                    ).run()
                case "migrate":
                    KConfigMigrate(args.package, args.arch).run()
                case "generate":
                    KConfigGenerate(args.package, args.arch).run()
        case "repo_missing":
            repo_missing(args.arch, args.built)
        case "stats":
            stats(args.arch)
        case "status":
            status()
        case "update":
            update(args.arch, args.non_existing)
        case "work_migrate":
            work_migrate()
        case "zap":
            zap(
                args.dry,
                args.http,
                args.distfiles,
                args.pkgs_local,
                args.pkgs_local_mismatch,
                args.pkgs_online_mismatch,
                args.rust,
                args.netboot,
            )
        case _:
            raise NotImplementedError(f"Command '{args.action}' is not implemented.")
