use std::{path::PathBuf, process::Command};

use crate::IntoArgs;

#[derive(Debug)]
pub enum IntelGigabitRev {
    I82574L,
    I82544GC,
    I82545EM,
    I82540EM,
}

#[derive(Debug)]
pub enum QemuNic {
    VirtioPci {
        mac: Option<String>,
    },
    Rtl8139 {
        mac: Option<String>,
    },
    IntelGigabit {
        mac: Option<String>,
        rev: Option<IntelGigabitRev>,
    },
}

#[derive(Debug, PartialEq, Eq)]
pub enum QemuDrive {
    Nvme,
    Sata,
    VirtioBlk,
}

#[derive(Debug)]
pub enum QemuDevice {
    NetworkTap {
        nic: QemuNic,
        script: Option<String>,
        ifname: Option<String>,
    },
    Drive {
        ty: QemuDrive,
        file: PathBuf,
        serial: Option<String>,
    },
}

#[derive(Debug)]
pub enum QemuSerialTarget {
    MonStdio,
    Stdio,
    None,
}

impl IntoArgs for QemuNic {
    fn add_args(&self, command: &mut Command) {
        match self {
            Self::VirtioPci { mac } => {
                command.arg("-device");
                let mut val = "virtio-net-pci,netdev=net0".to_owned();
                if let Some(mac) = mac {
                    val.push_str(",mac=");
                    val.push_str(mac);
                }
                command.arg(val);
            }
            Self::Rtl8139 { mac } => {
                command.arg("-device");
                let mut val = "rtl8139,netdev=net0".to_owned();
                if let Some(mac) = mac {
                    val.push_str(",mac=");
                    val.push_str(mac);
                }
                command.arg(val);
            }
            Self::IntelGigabit { mac, rev } => {
                let name = match rev {
                    Some(IntelGigabitRev::I82574L) => "e1000e",
                    Some(IntelGigabitRev::I82544GC) => "e1000-82544gc",
                    Some(IntelGigabitRev::I82545EM) => "e1000-82545em",
                    Some(IntelGigabitRev::I82540EM) => "e1000",
                    None => "igb",
                };
                command.arg("-device");
                let mut val = format!("{name},netdev=net0");
                if let Some(mac) = mac {
                    val.push_str(",mac=");
                    val.push_str(mac);
                }
                command.arg(val);
            }
        }
    }
}

// TODO separate handling for devices
impl IntoArgs for QemuDevice {
    fn add_args(&self, command: &mut Command) {
        match self {
            Self::NetworkTap {
                nic,
                script,
                ifname,
            } => {
                command.arg("-netdev");
                let mut val = "tap,id=net0".to_owned();
                if let Some(script) = script {
                    val.push_str(",script=");
                    val.push_str(script);
                }
                if let Some(ifname) = ifname {
                    val.push_str(",ifname=");
                    val.push_str(ifname);
                }
                command.arg(val);

                nic.add_args(command);
            }
            Self::Drive { ty, file, serial } => {
                match ty {
                    QemuDrive::Nvme => {
                        command.arg("-drive");
                        command.arg(format!("file={},if=none,id=drive0", file.display()));

                        command.arg("-device");
                        let mut device = "nvme".to_owned();
                        if let Some(serial) = serial {
                            device.push_str(",serial=");
                            device.push_str(serial);
                        }
                        device.push_str(",drive=drive0");
                        command.arg(device);
                    }
                    QemuDrive::Sata => {
                        command.arg("-drive");
                        command.arg(format!(
                            "file={},if=ide,bus=0,index=1,id=drive0",
                            file.display()
                        ));
                    }
                    QemuDrive::VirtioBlk => {
                        command.arg("-drive");
                        command.arg(format!("file={},if=none,id=drive0", file.display()));
                        command.arg("-device");
                        let mut device = "virtio-blk-pci".to_owned();
                        if let Some(serial) = serial {
                            device.push_str(",serial=");
                            device.push_str(serial);
                        }
                        device.push_str(",drive=drive0");
                        command.arg(device);
                    }
                }
                // command.arg("-drive");
                // command.arg(format!("file={},if=none,id=drive0", file.display()));

                // command.arg("-device");
                // let mut val = match ty {
                //     QemuDrive::Nvme => "nvme".to_owned(),
                //     QemuDrive::Sata => "ahci".to_owned(),
                // };
                // if *ty == QemuDrive::Nvme
                //     && let Some(serial) = serial
                // {
                //     val.push_str(",serial=");
                //     val.push_str(serial);
                // }
                // val.push_str(",drive=drive0");
                // command.arg(val);
            }
        }
    }
}

impl IntoArgs for QemuSerialTarget {
    fn add_args(&self, command: &mut Command) {
        command.arg("-serial");
        match self {
            Self::MonStdio => {
                command.arg("mon:stdio");
            }
            Self::Stdio => {
                command.arg("stdio");
            }
            Self::None => {
                command.arg("none");
            }
        }
    }
}
