use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::path::PathBuf;

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ServerMode {
    #[default]
    Combined,
    Controller,
    Node,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MetadataBackend {
    #[default]
    Filesystem,
    Chainfire,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ServerConfig {
    pub mode: ServerMode,
    pub metadata_backend: MetadataBackend,
    pub chainfire_api_url: Option<String>,
    pub chainfire_key_prefix: String,
    pub listen_addr: SocketAddr,
    pub advertise_host: String,
    pub data_dir: PathBuf,
    pub export_bind_addr: String,
    pub export_base_port: u16,
    pub export_port_count: u16,
    pub export_shared_clients: u16,
    pub export_cache_mode: String,
    pub export_aio_mode: String,
    pub export_discard_mode: String,
    pub export_detect_zeroes_mode: String,
    pub preallocate: bool,
    pub sync_on_write: bool,
    pub qemu_nbd_path: PathBuf,
    pub qemu_img_path: PathBuf,
    pub log_level: String,
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            mode: ServerMode::Combined,
            metadata_backend: MetadataBackend::Filesystem,
            chainfire_api_url: None,
            chainfire_key_prefix: "/coronafs/volumes".to_string(),
            listen_addr: "0.0.0.0:50088".parse().expect("valid listen addr"),
            advertise_host: "127.0.0.1".to_string(),
            data_dir: PathBuf::from("/var/lib/coronafs"),
            export_bind_addr: "0.0.0.0".to_string(),
            export_base_port: 11000,
            export_port_count: 512,
            export_shared_clients: 32,
            export_cache_mode: "none".to_string(),
            export_aio_mode: "threads".to_string(),
            export_discard_mode: "unmap".to_string(),
            export_detect_zeroes_mode: "unmap".to_string(),
            preallocate: true,
            sync_on_write: false,
            qemu_nbd_path: PathBuf::from("qemu-nbd"),
            qemu_img_path: PathBuf::from("qemu-img"),
            log_level: "info".to_string(),
        }
    }
}

impl ServerConfig {
    pub fn supports_controller_api(&self) -> bool {
        matches!(self.mode, ServerMode::Combined | ServerMode::Controller)
    }

    pub fn supports_node_api(&self) -> bool {
        matches!(self.mode, ServerMode::Combined | ServerMode::Node)
    }

    pub fn volume_dir(&self) -> PathBuf {
        self.data_dir.join("volumes")
    }

    pub fn metadata_dir(&self) -> PathBuf {
        self.data_dir.join("metadata")
    }

    pub fn pid_dir(&self) -> PathBuf {
        self.data_dir.join("pids")
    }
}
