//! FireCracker REST API client over Unix socket

use plasmavmc_types::{Error, Result};
use serde::{Deserialize, Serialize};
use std::path::Path;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::UnixStream;

/// FireCracker REST API client
pub struct FireCrackerClient {
    stream: UnixStream,
}

impl FireCrackerClient {
    /// Connect to FireCracker API socket
    pub async fn connect(path: impl AsRef<Path>) -> Result<Self> {
        let stream = UnixStream::connect(path.as_ref())
            .await
            .map_err(|e| Error::HypervisorError(format!("Failed to connect FireCracker API: {e}")))?;
        Ok(Self { stream })
    }

    /// Send HTTP request over Unix socket
    async fn request(
        &mut self,
        method: &str,
        path: &str,
        body: Option<&[u8]>,
    ) -> Result<Vec<u8>> {
        let mut request = format!("{} {} HTTP/1.1\r\n", method, path);
        request.push_str("Host: localhost\r\n");
        request.push_str("Content-Type: application/json\r\n");

        if let Some(body) = body {
            request.push_str(&format!("Content-Length: {}\r\n", body.len()));
        } else {
            request.push_str("Content-Length: 0\r\n");
        }
        request.push_str("\r\n");

        if let Some(body) = body {
            request.push_str(&String::from_utf8_lossy(body));
        }

        self.stream
            .write_all(request.as_bytes())
            .await
            .map_err(|e| Error::HypervisorError(format!("Failed to send request: {e}")))?;
        self.stream
            .flush()
            .await
            .map_err(|e| Error::HypervisorError(format!("Failed to flush request: {e}")))?;

        // Read response
        let mut response = Vec::new();
        self.stream
            .read_to_end(&mut response)
            .await
            .map_err(|e| Error::HypervisorError(format!("Failed to read response: {e}")))?;

        // Parse HTTP response
        let response_str = String::from_utf8_lossy(&response);
        let mut lines = response_str.lines();

        // Parse status line
        let status_line = lines.next().ok_or_else(|| {
            Error::HypervisorError("Empty response from FireCracker API".to_string())
        })?;
        if !status_line.contains("200") && !status_line.contains("204") {
            return Err(Error::HypervisorError(format!(
                "FireCracker API error: {status_line}"
            )));
        }

        // Skip headers
        let mut body_start = 0;
        for (idx, line) in response_str.lines().enumerate() {
            if line.is_empty() {
                body_start = response_str
                    .lines()
                    .take(idx + 1)
                    .map(|l| l.len() + 2) // +2 for \r\n
                    .sum();
                break;
            }
        }

        Ok(response[body_start..].to_vec())
    }

    /// PUT /machine-config
    pub async fn put_machine_config(&mut self, vcpu_count: u32, mem_size_mib: u64) -> Result<()> {
        #[derive(Serialize)]
        struct MachineConfig {
            vcpu_count: u32,
            mem_size_mib: u64,
            ht_enabled: bool,
        }

        let config = MachineConfig {
            vcpu_count,
            mem_size_mib,
            ht_enabled: false,
        };

        let body = serde_json::to_vec(&config)
            .map_err(|e| Error::HypervisorError(format!("Failed to serialize config: {e}")))?;

        self.request("PUT", "/machine-config", Some(&body))
            .await?;
        Ok(())
    }

    /// PUT /boot-source
    pub async fn put_boot_source(
        &mut self,
        kernel_image_path: &str,
        initrd_path: Option<&str>,
        boot_args: &str,
    ) -> Result<()> {
        #[derive(Serialize)]
        struct BootSource {
            kernel_image_path: String,
            #[serde(skip_serializing_if = "Option::is_none")]
            initrd_path: Option<String>,
            boot_args: String,
        }

        let boot_source = BootSource {
            kernel_image_path: kernel_image_path.to_string(),
            initrd_path: initrd_path.map(|s| s.to_string()),
            boot_args: boot_args.to_string(),
        };

        let body = serde_json::to_vec(&boot_source)
            .map_err(|e| Error::HypervisorError(format!("Failed to serialize boot source: {e}")))?;

        self.request("PUT", "/boot-source", Some(&body))
            .await?;
        Ok(())
    }

    /// PUT /drives/{drive_id}
    pub async fn put_drive(
        &mut self,
        drive_id: &str,
        path_on_host: &str,
        is_root_device: bool,
        is_read_only: bool,
    ) -> Result<()> {
        #[derive(Serialize)]
        struct Drive {
            path_on_host: String,
            is_root_device: bool,
            is_read_only: bool,
        }

        let drive = Drive {
            path_on_host: path_on_host.to_string(),
            is_root_device,
            is_read_only,
        };

        let body = serde_json::to_vec(&drive)
            .map_err(|e| Error::HypervisorError(format!("Failed to serialize drive: {e}")))?;

        self.request("PUT", &format!("/drives/{}", drive_id), Some(&body))
            .await?;
        Ok(())
    }

    /// PUT /network-interfaces/{iface_id}
    pub async fn put_network_interface(
        &mut self,
        iface_id: &str,
        guest_mac: &str,
        host_dev_name: &str,
    ) -> Result<()> {
        #[derive(Serialize)]
        struct NetworkInterface {
            guest_mac: String,
            host_dev_name: String,
        }

        let iface = NetworkInterface {
            guest_mac: guest_mac.to_string(),
            host_dev_name: host_dev_name.to_string(),
        };

        let body = serde_json::to_vec(&iface)
            .map_err(|e| Error::HypervisorError(format!("Failed to serialize network interface: {e}")))?;

        self.request(
            "PUT",
            &format!("/network-interfaces/{}", iface_id),
            Some(&body),
        )
        .await?;
        Ok(())
    }

    /// PUT /actions
    pub async fn instance_start(&mut self) -> Result<()> {
        #[derive(Serialize)]
        struct Action {
            action_type: String,
        }

        let action = Action {
            action_type: "InstanceStart".to_string(),
        };

        let body = serde_json::to_vec(&action)
            .map_err(|e| Error::HypervisorError(format!("Failed to serialize action: {e}")))?;

        self.request("PUT", "/actions", Some(&body))
            .await?;
        Ok(())
    }

    /// PUT /actions (SendCtrlAltDel)
    pub async fn send_ctrl_alt_del(&mut self) -> Result<()> {
        #[derive(Serialize)]
        struct Action {
            action_type: String,
        }

        let action = Action {
            action_type: "SendCtrlAltDel".to_string(),
        };

        let body = serde_json::to_vec(&action)
            .map_err(|e| Error::HypervisorError(format!("Failed to serialize action: {e}")))?;

        self.request("PUT", "/actions", Some(&body))
            .await?;
        Ok(())
    }

    /// GET /vm
    pub async fn get_vm_info(&mut self) -> Result<VmInfo> {
        let body = self.request("GET", "/vm", None).await?;
        let info: VmInfo = serde_json::from_slice(&body)
            .map_err(|e| Error::HypervisorError(format!("Failed to parse VM info: {e}")))?;
        Ok(info)
    }
}

#[derive(Debug, Deserialize)]
pub struct VmInfo {
    #[serde(default)]
    #[allow(dead_code)]
    pub state: String,
}
