use super::{HetznerClient, LoadBalancer, Result};
use crate::config::LoadBalancerConfig;
use serde::{Deserialize, Serialize};
use tracing::info;

pub struct LoadBalancerManager {
    client: HetznerClient,
}

#[derive(Debug, Serialize)]
struct CreateLoadBalancerRequest {
    name: String,
    load_balancer_type: String,
    location: String,
    algorithm: AlgorithmSpec,
    services: Vec<ServiceSpec>,
    #[serde(skip_serializing_if = "Option::is_none")]
    targets: Option<Vec<TargetSpec>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    network: Option<u64>,
}

#[derive(Debug, Serialize)]
struct AlgorithmSpec {
    #[serde(rename = "type")]
    algorithm_type: String,
}

#[derive(Debug, Serialize)]
struct ServiceSpec {
    protocol: String,
    listen_port: u16,
    destination_port: u16,
    proxyprotocol: bool,
    health_check: HealthCheckSpec,
}

#[derive(Debug, Serialize)]
struct HealthCheckSpec {
    protocol: String,
    port: u16,
    interval: u64,
    timeout: u64,
    retries: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    http: Option<HttpHealthCheckSpec>,
}

#[derive(Debug, Serialize)]
struct HttpHealthCheckSpec {
    path: String,
    tls: bool,
}

#[derive(Debug, Serialize)]
struct TargetSpec {
    #[serde(rename = "type")]
    target_type: String,
    server: ServerTarget,
    use_private_ip: bool,
}

#[derive(Debug, Serialize)]
struct ServerTarget {
    id: u64,
}

#[derive(Debug, Deserialize)]
struct CreateLoadBalancerResponse {
    load_balancer: LoadBalancer,
    action: super::Action,
    #[serde(default)]
    next_actions: Vec<super::Action>,
}

#[derive(Debug, Deserialize)]
struct ListLoadBalancersResponse {
    load_balancers: Vec<LoadBalancer>,
}

#[derive(Debug, Serialize)]
struct AddTargetRequest {
    #[serde(rename = "type")]
    target_type: String,
    server: ServerTarget,
    use_private_ip: bool,
}

impl LoadBalancerManager {
    pub fn new(client: HetznerClient) -> Self {
        Self { client }
    }

    /// List all load balancers
    pub async fn list_load_balancers(&self) -> Result<Vec<LoadBalancer>> {
        let response: ListLoadBalancersResponse = self.client.get("/load_balancers").await?;
        Ok(response.load_balancers)
    }

    /// Get load balancer by name
    pub async fn get_load_balancer_by_name(&self, name: &str) -> Result<Option<LoadBalancer>> {
        let lbs = self.list_load_balancers().await?;
        Ok(lbs.into_iter().find(|lb| lb.name == name))
    }

    /// Create a new load balancer
    pub async fn create_load_balancer(
        &self,
        config: &LoadBalancerConfig,
        location: &str,
        network_id: Option<u64>,
    ) -> Result<LoadBalancer> {
        info!("Creating load balancer: {}", config.name);

        let services: Vec<ServiceSpec> = config
            .services
            .iter()
            .map(|s| {
                let health_check = if config.health_check_protocol == "http"
                    || config.health_check_protocol == "https"
                {
                    HealthCheckSpec {
                        protocol: config.health_check_protocol.clone(),
                        port: config.health_check_port,
                        interval: config.health_check_interval,
                        timeout: config.health_check_timeout,
                        retries: 3,
                        http: Some(HttpHealthCheckSpec {
                            path: config.health_check_path.clone(),
                            tls: config.health_check_protocol == "https",
                        }),
                    }
                } else {
                    HealthCheckSpec {
                        protocol: config.health_check_protocol.clone(),
                        port: config.health_check_port,
                        interval: config.health_check_interval,
                        timeout: config.health_check_timeout,
                        retries: 3,
                        http: None,
                    }
                };

                ServiceSpec {
                    protocol: s.protocol.clone(),
                    listen_port: s.listen_port,
                    destination_port: s.destination_port,
                    proxyprotocol: false,
                    health_check,
                }
            })
            .collect();

        let request = CreateLoadBalancerRequest {
            name: config.name.clone(),
            load_balancer_type: config.lb_type.clone(),
            location: location.to_string(),
            algorithm: AlgorithmSpec {
                algorithm_type: config.algorithm.clone(),
            },
            services,
            targets: None,
            network: network_id,
        };

        let response: CreateLoadBalancerResponse =
            self.client.post("/load_balancers", &request).await?;

        info!(
            "Load balancer {} created, waiting for it to be ready...",
            config.name
        );
        self.client.wait_for_action(response.action.id, 300).await?;

        // Wait for any additional actions (like network attachment)
        for action in &response.next_actions {
            self.client.wait_for_action(action.id, 300).await?;
        }

        info!("Load balancer {} is ready", config.name);

        Ok(response.load_balancer)
    }

    /// Add target to load balancer
    pub async fn add_target(&self, lb_id: u64, server_id: u64, use_private_ip: bool) -> Result<()> {
        info!(
            "Adding server {} as target to load balancer {}",
            server_id, lb_id
        );

        let request = AddTargetRequest {
            target_type: "server".to_string(),
            server: ServerTarget { id: server_id },
            use_private_ip,
        };

        #[derive(Debug, Deserialize)]
        struct AddTargetResponse {
            action: super::Action,
        }

        let response: AddTargetResponse = self
            .client
            .post(
                &format!("/load_balancers/{}/actions/add_target", lb_id),
                &request,
            )
            .await?;

        self.client.wait_for_action(response.action.id, 120).await?;

        info!("Target added to load balancer");
        Ok(())
    }

    /// Delete a load balancer
    pub async fn delete_load_balancer(&self, id: u64) -> Result<()> {
        info!("Deleting load balancer with ID: {}", id);
        self.client
            .delete(&format!("/load_balancers/{}", id))
            .await?;
        info!("Load balancer deleted");
        Ok(())
    }

    /// Ensure load balancer exists
    pub async fn ensure_load_balancer(
        &self,
        config: &LoadBalancerConfig,
        location: &str,
        network_id: Option<u64>,
    ) -> Result<LoadBalancer> {
        if let Some(lb) = self.get_load_balancer_by_name(&config.name).await? {
            info!("Load balancer {} already exists", config.name);
            return Ok(lb);
        }

        self.create_load_balancer(config, location, network_id)
            .await
    }
}
