use crate::proxmox::{ClusterInfo, ClusterType, ProxmoxClient};
use crate::state::AppState;
use chrono::Utc;
use rusqlite::OptionalExtension;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tauri::State;
use tokio::sync::Mutex;

/// Proxmox cluster connection information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterConnection {
    pub url: String,
    pub port: u16,
}

/// Add a Proxmox cluster
#[tauri::command]
pub async fn add_proxmox_cluster(
    id: String,
    name: String,
    cluster_type: ClusterType,
    connection: ClusterConnection,
    username: String,
    password: &str,
    state: State<'_, AppState>,
) -> Result<ClusterInfo, String> {
    // Create client
    let mut client = ProxmoxClient::new(&connection.url, connection.port, &username);

    // Authenticate and get ticket
    let ticket = client
        .authenticate(password)
        .await
        .map_err(|e| format!("Authentication failed: {}", e))?;

    // Set the ticket on the client
    client.set_ticket(&ticket);

    // Encrypt credentials for storage
    let credentials = serde_json::json!({
        "ticket": ticket,
        "username": username
    });
    let encrypted_credentials = crate::integrations::auth::encrypt_token(
        &serde_json::to_string(&credentials).map_err(|e| e.to_string())?,
    )
    .map_err(|e| format!("Failed to encrypt credentials: {}", e))?;

    // Create cluster info
    let cluster = ClusterInfo {
        id: id.clone(),
        name,
        cluster_type,
        url: connection.url,
        port: connection.port,
        username,
        created_at: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
        updated_at: Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
    };

    // Store in database
    {
        let db = state
            .db
            .lock()
            .map_err(|e| format!("Failed to lock database: {}", e))?;

        db.execute(
            "INSERT INTO proxmox_clusters (id, name, cluster_type, url, port, auth_method, encrypted_credentials, created_at, updated_at)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
            rusqlite::params![
                cluster.id,
                cluster.name,
                match cluster.cluster_type {
                    ClusterType::VE => "ve",
                    ClusterType::PBS => "pbs",
                },
                cluster.url,
                cluster.port,
                "root",
                encrypted_credentials,
                cluster.created_at,
                cluster.updated_at,
            ],
        )
        .map_err(|e| format!("Failed to store cluster: {}", e))?;
    }

    // Store in memory for quick access
    {
        let mut clusters = state.proxmox_clusters.lock().await;
        clusters.insert(id, Arc::new(Mutex::new(client)));
    }

    Ok(cluster)
}

/// Remove a Proxmox cluster
#[tauri::command]
pub async fn remove_proxmox_cluster(id: String, state: State<'_, AppState>) -> Result<(), String> {
    // Remove from database
    {
        let db = state
            .db
            .lock()
            .map_err(|e| format!("Failed to lock database: {}", e))?;

        db.execute("DELETE FROM proxmox_clusters WHERE id = ?1", [id.clone()])
            .map_err(|e| format!("Failed to remove cluster: {}", e))?;
    }

    // Remove from memory
    {
        let mut clusters = state.proxmox_clusters.lock().await;
        clusters.remove(&id);
    }

    Ok(())
}

/// List all Proxmox clusters
#[tauri::command]
pub async fn list_proxmox_clusters(state: State<'_, AppState>) -> Result<Vec<ClusterInfo>, String> {
    let clusters = {
        let db = state
            .db
            .lock()
            .map_err(|e| format!("Failed to lock database: {}", e))?;

        let mut stmt = db
            .prepare(
                "SELECT id, name, cluster_type, url, port, created_at, updated_at FROM proxmox_clusters",
            )
            .map_err(|e| format!("Failed to prepare query: {}", e))?;

        let cluster_iter = stmt
            .query_map([], |row| {
                Ok(ClusterInfo {
                    id: row.get(0)?,
                    name: row.get(1)?,
                    cluster_type: match row.get::<_, String>(2)?.as_str() {
                        "ve" => ClusterType::VE,
                        "pbs" => ClusterType::PBS,
                        _ => ClusterType::VE,
                    },
                    url: row.get(3)?,
                    port: row.get(4)?,
                    username: "".to_string(), // Will be decrypted when needed
                    created_at: row.get(5)?,
                    updated_at: row.get(6)?,
                })
            })
            .map_err(|e| format!("Failed to query clusters: {}", e))?;

        cluster_iter
            .collect::<Result<Vec<_>, _>>()
            .map_err(|e| e.to_string())
    };

    clusters
}

/// Get a specific Proxmox cluster
#[tauri::command]
pub async fn get_proxmox_cluster(
    id: String,
    state: State<'_, AppState>,
) -> Result<Option<ClusterInfo>, String> {
    let cluster = {
        let db = state
            .db
            .lock()
            .map_err(|e| format!("Failed to lock database: {}", e))?;

        let mut stmt = db
            .prepare(
                "SELECT id, name, cluster_type, url, port, created_at, updated_at FROM proxmox_clusters WHERE id = ?1",
            )
            .map_err(|e| format!("Failed to prepare query: {}", e))?;

        stmt.query_row([id], |row| {
            Ok(ClusterInfo {
                id: row.get(0)?,
                name: row.get(1)?,
                cluster_type: match row.get::<_, String>(2)?.as_str() {
                    "ve" => ClusterType::VE,
                    "pbs" => ClusterType::PBS,
                    _ => ClusterType::VE,
                },
                url: row.get(3)?,
                port: row.get(4)?,
                username: "".to_string(),
                created_at: row.get(5)?,
                updated_at: row.get(6)?,
            })
        })
        .optional()
        .map_err(|e| format!("Failed to query cluster: {}", e))?
    };

    Ok(cluster)
}

/// List all Proxmox VMs
#[tauri::command]
pub async fn list_proxmox_vms(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let vms =
        crate::proxmox::vm::list_vms(&client_guard, client_guard.ticket.as_deref().unwrap_or(""))
            .await
            .map_err(|e| format!("Failed to list VMs: {}", e))?;

    // Convert VmInfo structs to JSON
    let json_vms: Vec<serde_json::Value> = vms
        .into_iter()
        .map(|vm| serde_json::to_value(vm).map_err(|e| format!("Failed to serialize VM: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_vms)
}

/// Get Proxmox VM details
#[tauri::command]
pub async fn get_proxmox_vm(
    cluster_id: String,
    node: String,
    vm_id: u32,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let vm = crate::proxmox::vm::get_vm(
        &client_guard,
        &node,
        vm_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to get VM {}: {}", vm_id, e))?;

    serde_json::to_value(vm).map_err(|e| format!("Failed to serialize VM: {}", e))
}

/// Start a Proxmox VM
#[tauri::command]
pub async fn start_proxmox_vm(
    cluster_id: String,
    node: String,
    vm_id: u32,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::vm::start_vm(
        &client_guard,
        &node,
        vm_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to start VM {}: {}", vm_id, e))
}

/// Stop a Proxmox VM
#[tauri::command]
pub async fn stop_proxmox_vm(
    cluster_id: String,
    node: String,
    vm_id: u32,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::vm::stop_vm(
        &client_guard,
        &node,
        vm_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to stop VM {}: {}", vm_id, e))
}

/// Reboot a Proxmox VM
#[tauri::command]
pub async fn reboot_proxmox_vm(
    cluster_id: String,
    node: String,
    vm_id: u32,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::vm::reboot_vm(
        &client_guard,
        &node,
        vm_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to reboot VM {}: {}", vm_id, e))
}

/// Shutdown a Proxmox VM
#[tauri::command]
pub async fn shutdown_proxmox_vm(
    cluster_id: String,
    node: String,
    vm_id: u32,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::vm::shutdown_vm(
        &client_guard,
        &node,
        vm_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to shutdown VM {}: {}", vm_id, e))
}

/// List Proxmox Backup Jobs
#[tauri::command]
pub async fn list_proxmox_backup_jobs(
    cluster_id: String,
    node: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let jobs = crate::proxmox::backup::list_backup_jobs(
        &client_guard,
        &node,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list backup jobs: {}", e))?;

    let json_jobs: Vec<serde_json::Value> = jobs
        .into_iter()
        .map(|job| {
            serde_json::to_value(job).map_err(|e| format!("Failed to serialize backup job: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_jobs)
}

/// List Proxmox Datastores
#[tauri::command]
pub async fn list_proxmox_datastores(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let datastores = crate::proxmox::backup::list_datastores(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list datastores: {}", e))?;

    let json_datastores: Vec<serde_json::Value> = datastores
        .into_iter()
        .map(|ds| {
            serde_json::to_value(ds).map_err(|e| format!("Failed to serialize datastore: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_datastores)
}

/// Trigger Proxmox Backup Job
#[tauri::command]
pub async fn trigger_proxmox_backup_job(
    cluster_id: String,
    node: String,
    job_id: u32,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::backup::trigger_backup_job(
        &client_guard,
        &node,
        job_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to trigger backup job {}: {}", job_id, e))
}

/// List Ceph Pools
#[tauri::command]
pub async fn list_ceph_pools(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let pools = crate::proxmox::ceph::list_pools(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list Ceph pools: {}", e))?;

    let json_pools: Vec<serde_json::Value> = pools
        .into_iter()
        .map(|pool| {
            serde_json::to_value(pool).map_err(|e| format!("Failed to serialize Ceph pool: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_pools)
}

/// List Ceph OSDs
#[tauri::command]
pub async fn list_ceph_osd(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let osds = crate::proxmox::ceph::list_osds(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list Ceph OSDs: {}", e))?;

    let json_osds: Vec<serde_json::Value> = osds
        .into_iter()
        .map(|osd| {
            serde_json::to_value(osd).map_err(|e| format!("Failed to serialize Ceph OSD: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_osds)
}

/// Get Ceph Health
#[tauri::command]
pub async fn get_ceph_health(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let health = crate::proxmox::ceph::get_ceph_health(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to get Ceph health: {}", e))?;

    serde_json::to_value(health).map_err(|e| format!("Failed to serialize Ceph health: {}", e))
}

// ─── Phase 1 - Core Management Features ───────────────────────────────────────

/// List authentication realms (LDAP/AD/OpenID)
#[tauri::command]
pub async fn list_auth_realms(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let realms = crate::proxmox::auth_realm::list_auth_realms(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list auth realms: {}", e))?;

    let json_realms: Vec<serde_json::Value> = realms
        .into_iter()
        .map(|r| serde_json::to_value(r).map_err(|e| format!("Failed to serialize realm: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_realms)
}

/// Add LDAP realm
#[tauri::command]
#[allow(clippy::too_many_arguments)]
pub async fn add_ldap_realm(
    cluster_id: String,
    realm_id: String,
    server: String,
    port: u16,
    base_dn: String,
    bind_dn: String,
    bind_password: String,
    filter: String,
    scope: String,
    start_tls: bool,
    certificate: String,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let config = crate::proxmox::auth_realm::LdapRealmConfig {
        server,
        port,
        base_dn,
        bind_dn,
        bind_password,
        filter,
        scope,
        start_tls,
        certificate,
    };

    crate::proxmox::auth_realm::add_ldap_realm(
        &client_guard,
        &realm_id,
        &config,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to add LDAP realm: {}", e))
}

/// Add AD realm
#[tauri::command]
#[allow(clippy::too_many_arguments)]
pub async fn add_ad_realm(
    cluster_id: String,
    realm_id: String,
    server: String,
    port: u16,
    base_dn: String,
    bind_dn: String,
    bind_password: String,
    filter: String,
    scope: String,
    use_ssl: bool,
    certificate: String,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let config = crate::proxmox::auth_realm::AdRealmConfig {
        server,
        port,
        base_dn,
        bind_dn,
        bind_password,
        filter,
        scope,
        use_ssl,
        certificate,
    };

    crate::proxmox::auth_realm::add_ad_realm(
        &client_guard,
        &realm_id,
        &config,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to add AD realm: {}", e))
}

/// Add OpenID realm
#[tauri::command]
#[allow(clippy::too_many_arguments)]
pub async fn add_openid_realm(
    cluster_id: String,
    realm_id: String,
    issuer: String,
    client_id: String,
    client_secret: String,
    redirect_url: String,
    scopes: Vec<String>,
    mapping: String,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let config = crate::proxmox::auth_realm::OpenidRealmConfig {
        issuer,
        client_id,
        client_secret,
        redirect_url,
        scopes,
        mapping,
    };

    crate::proxmox::auth_realm::add_openid_realm(
        &client_guard,
        &realm_id,
        &config,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to add OpenID realm: {}", e))
}

/// List ACME accounts
#[tauri::command]
pub async fn list_acme_accounts(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let accounts = crate::proxmox::acme::list_acme_accounts(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list ACME accounts: {}", e))?;

    let json_accounts: Vec<serde_json::Value> = accounts
        .into_iter()
        .map(|a| serde_json::to_value(a).map_err(|e| format!("Failed to serialize account: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_accounts)
}

/// Register ACME account
#[tauri::command]
pub async fn register_acme_account(
    cluster_id: String,
    email: String,
    terms_of_service_agreed: bool,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let account = crate::proxmox::acme::register_acme_account(
        &client_guard,
        &email,
        terms_of_service_agreed,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to register ACME account: {}", e))?;

    serde_json::to_value(account).map_err(|e| format!("Failed to serialize account: {}", e))
}

/// Get ACME challenges
#[tauri::command]
pub async fn get_acme_challenges(
    cluster_id: String,
    domain: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let challenges = crate::proxmox::acme::get_acme_challenges(
        &client_guard,
        &domain,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to get ACME challenges: {}", e))?;

    let json_challenges: Vec<serde_json::Value> = challenges
        .into_iter()
        .map(|c| {
            serde_json::to_value(c).map_err(|e| format!("Failed to serialize challenge: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_challenges)
}

/// List APT updates
#[tauri::command]
pub async fn list_apt_updates(
    cluster_id: String,
    node: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let updates = crate::proxmox::apt::list_apt_updates(
        &client_guard,
        &node,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list APT updates: {}", e))?;

    let json_updates: Vec<serde_json::Value> = updates
        .into_iter()
        .map(|u| serde_json::to_value(u).map_err(|e| format!("Failed to serialize update: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_updates)
}

/// Update APT repositories
#[tauri::command]
pub async fn update_apt_repos(
    cluster_id: String,
    node: String,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::apt::update_apt_repos(
        &client_guard,
        &node,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to update APT repos: {}", e))
}

/// List APT repositories
#[tauri::command]
pub async fn list_apt_repositories(
    cluster_id: String,
    node: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let repos = crate::proxmox::apt::list_apt_repositories(
        &client_guard,
        &node,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list APT repos: {}", e))?;

    let json_repos: Vec<serde_json::Value> = repos
        .into_iter()
        .map(|r| serde_json::to_value(r).map_err(|e| format!("Failed to serialize repo: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_repos)
}

/// Get shell ticket
#[tauri::command]
pub async fn get_shell_ticket(
    cluster_id: String,
    remote: String,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let ticket = crate::proxmox::shell::get_shell_ticket(
        &client_guard,
        &remote,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to get shell ticket: {}", e))?;

    serde_json::to_value(ticket).map_err(|e| format!("Failed to serialize ticket: {}", e))
}

/// List dashboard views
#[tauri::command]
pub async fn list_views(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let views = crate::proxmox::views::list_views(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list views: {}", e))?;

    let json_views: Vec<serde_json::Value> = views
        .into_iter()
        .map(|v| serde_json::to_value(v).map_err(|e| format!("Failed to serialize view: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_views)
}

/// Add dashboard view
#[tauri::command]
#[allow(clippy::too_many_arguments)]
pub async fn add_view(
    cluster_id: String,
    view_id: String,
    name: String,
    description: String,
    layout: String,
    widgets: Vec<serde_json::Value>,
    enabled: bool,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let widgets: Vec<crate::proxmox::views::Widget> = widgets
        .into_iter()
        .map(|w| {
            serde_json::from_value(w).map_err(|e| format!("Failed to deserialize widget: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    let view = crate::proxmox::views::DashboardView {
        view_id,
        name,
        description,
        layout,
        widgets,
        enabled,
        created_at: chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
        updated_at: chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
    };

    crate::proxmox::views::add_view(
        &client_guard,
        &view,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to add view: {}", e))
}

/// Update dashboard view
#[tauri::command]
#[allow(clippy::too_many_arguments)]
pub async fn update_view(
    cluster_id: String,
    view_id: String,
    name: String,
    description: String,
    layout: String,
    widgets: Vec<serde_json::Value>,
    enabled: bool,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let widgets: Vec<crate::proxmox::views::Widget> = widgets
        .into_iter()
        .map(|w| {
            serde_json::from_value(w).map_err(|e| format!("Failed to deserialize widget: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    let view = crate::proxmox::views::DashboardView {
        view_id: view_id.clone(),
        name,
        description,
        layout,
        widgets,
        enabled,
        created_at: chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
        updated_at: chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
    };

    crate::proxmox::views::update_view(
        &client_guard,
        &view_id,
        &view,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to update view: {}", e))
}

/// Delete dashboard view
#[tauri::command]
pub async fn delete_view(
    cluster_id: String,
    view_id: String,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::views::delete_view(
        &client_guard,
        &view_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to delete view: {}", e))
}

/// List certificates
#[tauri::command]
pub async fn list_certificates(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let certs = crate::proxmox::certificates::list_certificates(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list certificates: {}", e))?;

    let json_certs: Vec<serde_json::Value> = certs
        .into_iter()
        .map(|c| serde_json::to_value(c).map_err(|e| format!("Failed to serialize cert: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_certs)
}

/// Upload certificate
#[tauri::command]
pub async fn upload_certificate(
    cluster_id: String,
    certificate: String,
    private_key: String,
    name: Option<String>,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let cert = crate::proxmox::certificates::upload_certificate(
        &client_guard,
        &certificate,
        &private_key,
        name.as_deref(),
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to upload certificate: {}", e))?;

    serde_json::to_value(cert).map_err(|e| format!("Failed to serialize cert: {}", e))
}

/// Get certificate
#[tauri::command]
pub async fn get_certificate(
    cluster_id: String,
    cert_id: String,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let cert = crate::proxmox::certificates::get_certificate(
        &client_guard,
        &cert_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to get certificate {}: {}", cert_id, e))?;

    serde_json::to_value(cert).map_err(|e| format!("Failed to serialize cert: {}", e))
}

// ─── Phase 2 - Advanced Management ────────────────────────────────────────────

// Firewall commands (extended from existing)
/// List firewall rules
#[tauri::command]
pub async fn list_firewall_rules(
    cluster_id: String,
    node: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let rules = crate::proxmox::firewall::list_firewall_rules(
        &client_guard,
        &node,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list firewall rules: {}", e))?;

    let json_rules: Vec<serde_json::Value> = rules
        .into_iter()
        .map(|r| serde_json::to_value(r).map_err(|e| format!("Failed to serialize rule: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_rules)
}

/// Add firewall rule
#[tauri::command]
#[allow(clippy::too_many_arguments)]
pub async fn add_firewall_rule(
    cluster_id: String,
    node: String,
    action: String,
    protocol: String,
    source: String,
    destination: String,
    port: Option<String>,
    enabled: bool,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let rule = crate::proxmox::firewall::FirewallRule {
        rule_num: 0,
        action,
        protocol,
        source,
        destination,
        port,
        enabled,
    };

    crate::proxmox::firewall::add_rule(
        &client_guard,
        &node,
        &rule,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to add firewall rule: {}", e))
}

/// Delete firewall rule
#[tauri::command]
pub async fn delete_firewall_rule(
    cluster_id: String,
    node: String,
    rule_num: u32,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::firewall::delete_rule(
        &client_guard,
        &node,
        rule_num,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to delete firewall rule {}: {}", rule_num, e))
}

// SDN commands (extended from existing)
/// List SDN controllers
#[tauri::command]
pub async fn list_sdn_controllers(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let controllers = crate::proxmox::sdn::list_evpn_zones(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list SDN controllers: {}", e))?;

    let json_controllers: Vec<serde_json::Value> = controllers
        .into_iter()
        .map(|c| {
            serde_json::to_value(c).map_err(|e| format!("Failed to serialize controller: {}", e))
        })
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_controllers)
}

/// List SDN virtual networks
#[tauri::command]
pub async fn list_sdn_vnets(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let vnets = crate::proxmox::sdn::list_vnets(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list SDN virtual networks: {}", e))?;

    let json_vnets: Vec<serde_json::Value> = vnets
        .into_iter()
        .map(|v| serde_json::to_value(v).map_err(|e| format!("Failed to serialize vnet: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_vnets)
}

/// List SDN zones
#[tauri::command]
pub async fn list_sdn_zones(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let zones = crate::proxmox::sdn::list_evpn_zones(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list SDN zones: {}", e))?;

    let json_zones: Vec<serde_json::Value> = zones
        .into_iter()
        .map(|z| serde_json::to_value(z).map_err(|e| format!("Failed to serialize zone: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_zones)
}

// ─── Phase 3 - Network Management ─────────────────────────────────────────────

// Ceph Cluster Management
/// List Ceph clusters
#[tauri::command]
pub async fn list_ceph_clusters(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let ceph_clusters = crate::proxmox::ceph_cluster::list_ceph_clusters(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list Ceph clusters: {}", e))?;

    let json_clusters: Vec<serde_json::Value> = ceph_clusters
        .into_iter()
        .map(|c| serde_json::to_value(c).map_err(|e| format!("Failed to serialize cluster: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_clusters)
}

/// Get Ceph cluster status
#[tauri::command]
pub async fn get_ceph_cluster_status(
    cluster_id: String,
    ceph_cluster_id: String,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let status = crate::proxmox::ceph_cluster::get_ceph_cluster_status(
        &client_guard,
        &ceph_cluster_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to get Ceph cluster status: {}", e))?;

    serde_json::to_value(status).map_err(|e| format!("Failed to serialize status: {}", e))
}

// ─── Phase 4 - Advanced Operations ────────────────────────────────────────────

// Remote Migration
/// Migrate VM
#[tauri::command]
pub async fn migrate_vm(
    cluster_id: String,
    node: String,
    vm_id: u32,
    target_node: String,
    target_cluster: String,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let task = crate::proxmox::migration::migrate_vm(
        &client_guard,
        &node,
        vm_id,
        &target_node,
        &target_cluster,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to migrate VM {}: {}", vm_id, e))?;

    serde_json::to_value(task).map_err(|e| format!("Failed to serialize migration task: {}", e))
}

/// List migration status
#[tauri::command]
pub async fn list_migration_status(
    cluster_id: String,
    node: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let tasks = crate::proxmox::migration::list_migration_status(
        &client_guard,
        &node,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list migration status: {}", e))?;

    let json_tasks: Vec<serde_json::Value> = tasks
        .into_iter()
        .map(|t| serde_json::to_value(t).map_err(|e| format!("Failed to serialize task: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_tasks)
}

// System Updates (extended)
/// List updates
#[tauri::command]
pub async fn list_updates(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let updates = crate::proxmox::updates_ext::list_updates_all_remotes(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list updates: {}", e))?;

    let json_updates: Vec<serde_json::Value> = updates
        .into_iter()
        .map(|u| serde_json::to_value(u).map_err(|e| format!("Failed to serialize update: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_updates)
}

/// Refresh updates
#[tauri::command]
pub async fn refresh_updates(cluster_id: String, state: State<'_, AppState>) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::updates_ext::refresh_updates_all(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to refresh updates: {}", e))
}

/// Install updates
#[tauri::command]
pub async fn install_updates(
    cluster_id: String,
    packages: Vec<String>,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let package_refs: Vec<&str> = packages.iter().map(|s| s.as_str()).collect();
    crate::proxmox::updates_ext::install_updates_remotes(
        &client_guard,
        &package_refs,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to install updates: {}", e))
}

// Task Management
/// List tasks
#[tauri::command]
pub async fn list_tasks(
    cluster_id: String,
    node: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let tasks = crate::proxmox::tasks::list_tasks(
        &client_guard,
        &node,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list tasks: {}", e))?;

    let json_tasks: Vec<serde_json::Value> = tasks
        .into_iter()
        .map(|t| serde_json::to_value(t).map_err(|e| format!("Failed to serialize task: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(json_tasks)
}

/// Get task status
#[tauri::command]
pub async fn get_task_status(
    cluster_id: String,
    node: String,
    task_id: String,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let task = crate::proxmox::tasks::get_task_status(
        &client_guard,
        &node,
        &task_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to get task {}: {}", task_id, e))?;

    serde_json::to_value(task).map_err(|e| format!("Failed to serialize task: {}", e))
}

/// Stop task
#[tauri::command]
pub async fn stop_task(
    cluster_id: String,
    node: String,
    task_id: String,
    state: State<'_, AppState>,
) -> Result<(), String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    crate::proxmox::tasks::stop_task(
        &client_guard,
        &node,
        &task_id,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to stop task {}: {}", task_id, e))
}

// ─── Phase 5 - Infrastructure ─────────────────────────────────────────────────

// Metric Collection (extended from existing)
/// Get metrics summary
#[tauri::command]
pub async fn get_metrics_summary(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<serde_json::Value, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let nodes = crate::proxmox::metrics::list_nodes(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list nodes: {}", e))?;

    let summary = serde_json::json!({
        "timestamp": chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(),
        "node_count": nodes.len(),
        "nodes": nodes
    });

    Ok(summary)
}

/// List metric collections
#[tauri::command]
pub async fn list_metric_collections(
    cluster_id: String,
    state: State<'_, AppState>,
) -> Result<Vec<serde_json::Value>, String> {
    let clusters = state.proxmox_clusters.lock().await;
    let client = clusters
        .get(&cluster_id)
        .ok_or_else(|| format!("Cluster {} not found", cluster_id))?;
    let client_guard = client.lock().await;

    let nodes = crate::proxmox::metrics::list_nodes(
        &client_guard,
        client_guard.ticket.as_deref().unwrap_or(""),
    )
    .await
    .map_err(|e| format!("Failed to list nodes: {}", e))?;

    let collections: Vec<serde_json::Value> = nodes
        .into_iter()
        .map(|n| serde_json::to_value(n).map_err(|e| format!("Failed to serialize node: {}", e)))
        .collect::<Result<Vec<_>, _>>()?;

    Ok(collections)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_cluster_type_serialization() {
        let json = serde_json::to_string(&ClusterType::VE).unwrap();
        assert_eq!(json, "\"ve\"");

        let ve: ClusterType = serde_json::from_str("\"ve\"").unwrap();
        assert_eq!(ve, ClusterType::VE);

        let pbs: ClusterType = serde_json::from_str("\"pbs\"").unwrap();
        assert_eq!(pbs, ClusterType::PBS);
    }

    #[test]
    fn test_cluster_info_serialization() {
        let cluster = ClusterInfo {
            id: "proxmox-1".to_string(),
            name: "Production".to_string(),
            cluster_type: ClusterType::VE,
            url: "https://pve.example.com".to_string(),
            port: 8006,
            username: "root@pam".to_string(),
            created_at: "2026-06-10 12:00:00".to_string(),
            updated_at: "2026-06-10 12:00:00".to_string(),
        };

        let json = serde_json::to_string(&cluster).unwrap();
        let deserialized: ClusterInfo = serde_json::from_str(&json).unwrap();

        assert_eq!(cluster.id, deserialized.id);
        assert_eq!(cluster.name, deserialized.name);
    }
}
