//! Hypervisor backend trait

use async_trait::async_trait;
use plasmavmc_types::{
    AttachedDisk, DiskBus, HypervisorType, NetworkSpec, NicModel, Result, VirtualMachine, VmHandle,
    VmSpec, VmStatus,
};
use std::time::Duration;

/// Console type for VM access
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConsoleType {
    /// VNC graphical console
    Vnc,
    /// Serial text console
    Serial,
}

/// Reason a feature is not supported
#[derive(Debug, Clone)]
pub enum UnsupportedReason {
    /// Disk bus type not supported
    DiskBus(DiskBus),
    /// NIC model not supported
    NicModel(NicModel),
    /// Feature not available
    Feature(String),
}

/// Backend capabilities
#[derive(Debug, Clone)]
pub struct BackendCapabilities {
    /// Supports live migration
    pub live_migration: bool,
    /// Supports CPU hot-plug
    pub hot_plug_cpu: bool,
    /// Supports memory hot-plug
    pub hot_plug_memory: bool,
    /// Supports disk hot-plug
    pub hot_plug_disk: bool,
    /// Supports NIC hot-plug
    pub hot_plug_nic: bool,
    /// Supports VNC console
    pub vnc_console: bool,
    /// Supports serial console
    pub serial_console: bool,
    /// Supports nested virtualization
    pub nested_virtualization: bool,
    /// Supports GPU passthrough
    pub gpu_passthrough: bool,
    /// Maximum vCPUs
    pub max_vcpus: u32,
    /// Maximum memory in GiB
    pub max_memory_gib: u64,
    /// Supported disk bus types
    pub supported_disk_buses: Vec<DiskBus>,
    /// Supported NIC models
    pub supported_nic_models: Vec<NicModel>,
}

impl Default for BackendCapabilities {
    fn default() -> Self {
        Self {
            live_migration: false,
            hot_plug_cpu: false,
            hot_plug_memory: false,
            hot_plug_disk: false,
            hot_plug_nic: false,
            vnc_console: false,
            serial_console: true,
            nested_virtualization: false,
            gpu_passthrough: false,
            max_vcpus: 128,
            max_memory_gib: 1024,
            supported_disk_buses: vec![DiskBus::Virtio],
            supported_nic_models: vec![NicModel::VirtioNet],
        }
    }
}

/// Hypervisor backend trait
///
/// This trait defines the interface that all hypervisor backends must implement.
/// Each backend (KVM, Firecracker, mvisor) provides its own implementation.
#[async_trait]
pub trait HypervisorBackend: Send + Sync {
    /// Get the backend type identifier
    fn backend_type(&self) -> HypervisorType;

    /// Get backend capabilities
    fn capabilities(&self) -> BackendCapabilities;

    /// Check if this backend supports the given VM spec
    fn supports(&self, spec: &VmSpec) -> std::result::Result<(), UnsupportedReason>;

    /// Create VM resources (disk, network) without starting
    async fn create(&self, vm: &VirtualMachine, disks: &[AttachedDisk]) -> Result<VmHandle>;

    /// Start the VM
    async fn start(&self, handle: &VmHandle) -> Result<()>;

    /// Stop the VM (graceful shutdown)
    async fn stop(&self, handle: &VmHandle, timeout: Duration) -> Result<()>;

    /// Force stop the VM
    async fn kill(&self, handle: &VmHandle) -> Result<()>;

    /// Reboot the VM
    async fn reboot(&self, handle: &VmHandle) -> Result<()>;

    /// Live migrate the VM to a destination URI
    async fn migrate(
        &self,
        handle: &VmHandle,
        destination_uri: &str,
        timeout: Duration,
        wait: bool,
    ) -> Result<()>;

    /// Prepare an incoming migration listener and return a handle
    async fn prepare_incoming(
        &self,
        vm: &VirtualMachine,
        listen_uri: &str,
        disks: &[AttachedDisk],
    ) -> Result<VmHandle>;

    /// Delete VM and cleanup resources
    async fn delete(&self, handle: &VmHandle) -> Result<()>;

    /// Get current VM status
    async fn status(&self, handle: &VmHandle) -> Result<VmStatus>;

    /// Attach a disk to running VM
    async fn attach_disk(&self, handle: &VmHandle, disk: &AttachedDisk) -> Result<()>;

    /// Detach a disk from running VM
    async fn detach_disk(&self, handle: &VmHandle, disk_id: &str) -> Result<()>;

    /// Attach a network interface
    async fn attach_nic(&self, handle: &VmHandle, nic: &NetworkSpec) -> Result<()>;

    /// Detach a network interface
    async fn detach_nic(&self, handle: &VmHandle, nic_id: &str) -> Result<()>;
}
