//! HTTP client for communicating with the Orchestrator service
//!
//! Handles all HTTP communication between Control Center and Orchestrator
//! for dynamic secret generation, revocation, and renewal operations.

use std::time::Duration;

use reqwest::Client;
use serde_json::json;
use tracing::{debug, error, info, warn};

use crate::error::{http, infrastructure, ControlCenterError, Result};

/// Timeout for orchestrator requests (30 seconds)
const ORCHESTRATOR_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);

/// HTTP client for orchestrator communication
#[derive(Clone)]
pub struct OrchestratorClient {
    http_client: Client,
    base_url: String,
}

impl OrchestratorClient {
    /// Create a new orchestrator client
    pub fn new(base_url: String) -> Self {
        let http_client = Client::builder()
            .timeout(ORCHESTRATOR_REQUEST_TIMEOUT)
            .build()
            .expect("Failed to build HTTP client");

        Self {
            http_client,
            base_url,
        }
    }

    /// Generate a dynamic secret via orchestrator
    ///
    /// # Arguments
    ///
    /// * `workspace_id` - Workspace context
    /// * `secret_type` - Type of secret (ssh_key, upcloud_api, aws_sts, etc.)
    /// * `parameters` - Provider-specific parameters
    /// * `ttl_seconds` - Time-to-live in seconds (optional)
    /// * `metadata` - Audit metadata
    ///
    /// # Returns
    ///
    /// Returns the generated secret response containing ID, credentials, and
    /// expiration
    pub async fn generate_secret(
        &self,
        workspace_id: &str,
        secret_type: &str,
        parameters: serde_json::Value,
        ttl_seconds: Option<i64>,
        metadata: serde_json::Value,
    ) -> Result<serde_json::Value> {
        let url = format!("{}/api/v1/secrets/dynamic/generate", self.base_url);

        let request_body = json!({
            "workspace_id": workspace_id,
            "secret_type": secret_type,
            "parameters": parameters,
            "ttl_seconds": ttl_seconds.unwrap_or(3600),
            "metadata": metadata,
        });

        debug!(
            url = %url,
            workspace = %workspace_id,
            secret_type = %secret_type,
            "Generating dynamic secret via orchestrator"
        );

        let response = self
            .http_client
            .post(&url)
            .json(&request_body)
            .send()
            .await
            .map_err(|e| {
                error!("Failed to generate secret via orchestrator: {}", e);
                ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
                    format!("Orchestrator communication failed: {}", e),
                ))
            })?;

        let status = response.status();
        if !status.is_success() {
            let error_text: String = response.text().await.unwrap_or_default();
            error!(
                status = %status,
                error = %error_text,
                "Orchestrator returned error for secret generation"
            );
            return Err(ControlCenterError::Infrastructure(
                infrastructure::InfrastructureError::Internal(format!(
                    "Orchestrator error ({}): {}",
                    status, error_text
                )),
            ));
        }

        let result = response.json::<serde_json::Value>().await.map_err(|e| {
            error!("Failed to parse orchestrator response: {}", e);
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
                format!("Failed to parse orchestrator response: {}", e),
            ))
        })?;

        info!(
            secret_id = ?result.get("id"),
            "Dynamic secret generated successfully via orchestrator"
        );

        Ok(result)
    }

    /// Revoke a dynamic secret via orchestrator
    ///
    /// # Arguments
    ///
    /// * `secret_id` - ID of the secret to revoke
    /// * `reason` - Reason for revocation (for audit trail)
    pub async fn revoke_secret(&self, secret_id: &str, reason: &str) -> Result<()> {
        let url = format!(
            "{}/api/v1/secrets/dynamic/{}/revoke",
            self.base_url, secret_id
        );

        let request_body = json!({
            "reason": reason,
        });

        debug!(
            url = %url,
            secret_id = %secret_id,
            reason = %reason,
            "Revoking dynamic secret via orchestrator"
        );

        let response = self
            .http_client
            .post(&url)
            .json(&request_body)
            .send()
            .await
            .map_err(|e| {
                error!("Failed to revoke secret via orchestrator: {}", e);
                ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
                    format!("Orchestrator communication failed: {}", e),
                ))
            })?;

        let status = response.status();
        if !status.is_success() {
            let error_text: String = response.text().await.unwrap_or_default();
            warn!(
                status = %status,
                error = %error_text,
                "Orchestrator returned error for secret revocation"
            );
            return Err(ControlCenterError::Infrastructure(
                infrastructure::InfrastructureError::Internal(format!(
                    "Orchestrator error ({}): {}",
                    status, error_text
                )),
            ));
        }

        info!(
            secret_id = %secret_id,
            "Dynamic secret revoked successfully via orchestrator"
        );

        Ok(())
    }

    /// Renew a dynamic secret via orchestrator
    ///
    /// # Arguments
    ///
    /// * `secret_id` - ID of the secret to renew
    /// * `new_ttl_seconds` - New TTL in seconds (optional, uses default if not
    ///   provided)
    pub async fn renew_secret(&self, secret_id: &str, new_ttl_seconds: Option<i64>) -> Result<()> {
        let url = format!(
            "{}/api/v1/secrets/dynamic/{}/renew",
            self.base_url, secret_id
        );

        let request_body = json!({
            "ttl_seconds": new_ttl_seconds.unwrap_or(3600),
        });

        debug!(
            url = %url,
            secret_id = %secret_id,
            ttl_seconds = ?new_ttl_seconds,
            "Renewing dynamic secret via orchestrator"
        );

        let response = self
            .http_client
            .post(&url)
            .json(&request_body)
            .send()
            .await
            .map_err(|e| {
                error!("Failed to renew secret via orchestrator: {}", e);
                ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
                    format!("Orchestrator communication failed: {}", e),
                ))
            })?;

        let status = response.status();
        if !status.is_success() {
            let error_text: String = response.text().await.unwrap_or_default();
            warn!(
                status = %status,
                error = %error_text,
                "Orchestrator returned error for secret renewal"
            );
            return Err(ControlCenterError::Infrastructure(
                infrastructure::InfrastructureError::Internal(format!(
                    "Orchestrator error ({}): {}",
                    status, error_text
                )),
            ));
        }

        info!(
            secret_id = %secret_id,
            "Dynamic secret renewed successfully via orchestrator"
        );

        Ok(())
    }

    /// Get the status of a dynamic secret
    ///
    /// # Arguments
    ///
    /// * `secret_id` - ID of the secret to check
    pub async fn get_secret_status(&self, secret_id: &str) -> Result<serde_json::Value> {
        let url = format!("{}/api/v1/secrets/dynamic/{}", self.base_url, secret_id);

        debug!(
            url = %url,
            secret_id = %secret_id,
            "Getting dynamic secret status via orchestrator"
        );

        let response = self.http_client.get(&url).send().await.map_err(|e| {
            error!("Failed to get secret status via orchestrator: {}", e);
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
                format!("Orchestrator communication failed: {}", e),
            ))
        })?;

        let status = response.status();
        if !status.is_success() {
            if status == reqwest::StatusCode::NOT_FOUND {
                warn!(
                    secret_id = %secret_id,
                    "Dynamic secret not found in orchestrator"
                );
                return Err(ControlCenterError::Http(http::HttpError::NotFound(
                    format!("Secret {} not found", secret_id),
                )));
            }

            let error_text: String = response.text().await.unwrap_or_default();
            error!(
                status = %status,
                error = %error_text,
                "Orchestrator returned error for secret status"
            );
            return Err(ControlCenterError::Infrastructure(
                infrastructure::InfrastructureError::Internal(format!(
                    "Orchestrator error ({}): {}",
                    status, error_text
                )),
            ));
        }

        let result = response.json::<serde_json::Value>().await.map_err(|e| {
            error!("Failed to parse orchestrator response: {}", e);
            ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
                format!("Failed to parse orchestrator response: {}", e),
            ))
        })?;

        debug!(
            secret_id = %secret_id,
            "Retrieved dynamic secret status"
        );

        Ok(result)
    }

    /// Check orchestrator health
    pub async fn health_check(&self) -> Result<bool> {
        let url = format!("{}/health", self.base_url);

        debug!(url = %url, "Checking orchestrator health");

        match self.http_client.get(&url).send().await {
            Ok(response) => {
                let is_healthy = response.status().is_success();
                if is_healthy {
                    debug!("Orchestrator is healthy");
                } else {
                    warn!(status = %response.status(), "Orchestrator health check failed");
                }
                Ok(is_healthy)
            }
            Err(e) => {
                warn!("Failed to reach orchestrator: {}", e);
                Ok(false)
            }
        }
    }
}

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

    #[test]
    fn test_orchestrator_client_creation() {
        let client = OrchestratorClient::new("http://localhost:9090".to_string());
        assert_eq!(client.base_url, "http://localhost:9090");
    }

    #[tokio::test]
    async fn test_orchestrator_url_building() {
        let client = OrchestratorClient::new("http://localhost:9090".to_string());
        // Verify URLs are built correctly
        let url = format!("{}/api/v1/secrets/dynamic/generate", client.base_url);
        assert_eq!(url, "http://localhost:9090/api/v1/secrets/dynamic/generate");
    }
}
