import argparse
import asyncio
import json
import os
import shlex
import sys
import tempfile
from pathlib import Path
from typing import Iterator
from uuid import UUID

from ..dirs import get_clan_flake_toplevel
from ..nix import nix_build, nix_config, nix_shell
from ..task_manager import BaseTask, Command, create_task
from .inspect import VmConfig, inspect_vm


class BuildVmTask(BaseTask):
    def __init__(self, uuid: UUID, vm: VmConfig) -> None:
        super().__init__(uuid, num_cmds=6)
        self.vm = vm

    def get_vm_create_info(self, cmds: Iterator[Command]) -> dict:
        config = nix_config()
        system = config["system"]

        clan_dir = self.vm.flake_url
        machine = self.vm.flake_attr
        cmd = next(cmds)
        cmd.run(
            nix_build(
                [
                    f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.system.clan.vm.create'
                ]
            )
        )
        vm_json = "".join(cmd.stdout)
        self.log.debug(f"VM JSON path: {vm_json}")
        with open(vm_json.strip()) as f:
            return json.load(f)

    def run(self) -> None:
        cmds = self.commands()

        machine = self.vm.flake_attr
        self.log.debug(f"Creating VM for {machine}")

        # TODO: We should get this from the vm argument
        vm_config = self.get_vm_create_info(cmds)

        with tempfile.TemporaryDirectory() as tmpdir_:
            tmpdir = Path(tmpdir_)
            xchg_dir = tmpdir / "xchg"
            xchg_dir.mkdir()
            secrets_dir = tmpdir / "secrets"
            secrets_dir.mkdir()
            disk_img = f"{tmpdir_}/disk.img"

            env = os.environ.copy()
            env["CLAN_DIR"] = str(self.vm.flake_url)
            env["PYTHONPATH"] = str(
                ":".join(sys.path)
            )  # TODO do this in the clanCore module
            env["SECRETS_DIR"] = str(secrets_dir)

            cmd = next(cmds)
            cmd.run(
                [vm_config["generateSecrets"]],
                env=env,
            )

            cmd = next(cmds)
            cmd.run(
                [vm_config["uploadSecrets"]],
                env=env,
            )

            cmd = next(cmds)
            cmd.run(
                nix_shell(
                    ["qemu"],
                    [
                        "qemu-img",
                        "create",
                        "-f",
                        "raw",
                        disk_img,
                        "1024M",
                    ],
                )
            )

            cmd = next(cmds)
            cmd.run(
                nix_shell(
                    ["e2fsprogs"],
                    [
                        "mkfs.ext4",
                        "-L",
                        "nixos",
                        disk_img,
                    ],
                )
            )

            cmd = next(cmds)
            cmdline = [
                (Path(vm_config["toplevel"]) / "kernel-params").read_text(),
                f'init={vm_config["toplevel"]}/init',
                f'regInfo={vm_config["regInfo"]}/registration',
                "console=ttyS0,115200n8",
                "console=tty0",
            ]
            qemu_command = [
                # fmt: off
                "qemu-kvm",
                "-name", machine,
                "-m", f'{vm_config["memorySize"]}M',
                "-smp", str(vm_config["cores"]),
                "-device", "virtio-rng-pci",
                "-net", "nic,netdev=user.0,model=virtio", "-netdev", "user,id=user.0",
                "-virtfs", "local,path=/nix/store,security_model=none,mount_tag=nix-store",
                "-virtfs", f"local,path={xchg_dir},security_model=none,mount_tag=shared",
                "-virtfs", f"local,path={xchg_dir},security_model=none,mount_tag=xchg",
                "-virtfs", f"local,path={secrets_dir},security_model=none,mount_tag=secrets",
                "-drive", f'cache=writeback,file={disk_img},format=raw,id=drive1,if=none,index=1,werror=report',
                "-device", "virtio-blk-pci,bootindex=1,drive=drive1,serial=root",
                "-device", "virtio-keyboard",
                "-usb",
                "-device", "usb-tablet,bus=usb-bus.0",
                "-kernel", f'{vm_config["toplevel"]}/kernel',
                "-initrd", vm_config["initrd"],
                "-append", " ".join(cmdline),
                # fmt: on
            ]
            if not self.vm.graphics:
                qemu_command.append("-nographic")
            print("$ " + shlex.join(qemu_command))
            cmd.run(nix_shell(["qemu"], qemu_command))


def create_vm(vm: VmConfig) -> BuildVmTask:
    return create_task(BuildVmTask, vm)


def create_command(args: argparse.Namespace) -> None:
    clan_dir = get_clan_flake_toplevel().as_posix()
    vm = asyncio.run(inspect_vm(flake_url=clan_dir, flake_attr=args.machine))

    task = create_vm(vm)
    for line in task.log_lines():
        print(line, end="")


def register_create_parser(parser: argparse.ArgumentParser) -> None:
    parser.add_argument("machine", type=str)
    parser.set_defaults(func=create_command)
