use reqwest::Client;
use serde_json::Value;

use crate::error::ControllerError;

/// Thin HTTP client for the orchestrator API at port 9011.
pub struct OrchestratorClient {
    client: Client,
    base_url: String,
    auth_header: Option<String>,
}

impl OrchestratorClient {
    pub fn new(base_url: String, auth_token: Option<String>) -> Result<Self, ControllerError> {
        let client = Client::builder()
            .timeout(std::time::Duration::from_secs(60))
            .build()
            .map_err(|e| ControllerError::Orchestrator(format!("HTTP client init: {e}")))?;
        let auth_header = auth_token.map(|t| format!("Bearer {t}"));
        Ok(Self { client, base_url, auth_header })
    }

    pub async fn deploy(&self, target: &str, image: &str, params: &Value) -> Result<(), ControllerError> {
        self.post(
            "/api/v1/workloads",
            serde_json::json!({ "target": target, "image": image, "params": params }),
        ).await
    }

    pub async fn scale(&self, target: &str, params: &Value) -> Result<(), ControllerError> {
        self.patch(
            &format!("/api/v1/workloads/{target}/scale"),
            serde_json::json!({ "params": params }),
        ).await
    }

    pub async fn restart(&self, target: &str) -> Result<(), ControllerError> {
        self.post(
            &format!("/api/v1/workloads/{target}/restart"),
            serde_json::json!({}),
        ).await
    }

    pub async fn secret_update(&self, target: &str, params: &Value) -> Result<(), ControllerError> {
        self.patch(
            &format!("/api/v1/workloads/{target}/secrets"),
            serde_json::json!({ "params": params }),
        ).await
    }

    pub async fn drain(&self, target: &str) -> Result<(), ControllerError> {
        self.post(
            &format!("/api/v1/workloads/{target}/drain"),
            serde_json::json!({}),
        ).await
    }

    pub async fn rollback(&self, target: &str, params: &Value) -> Result<(), ControllerError> {
        self.post(
            &format!("/api/v1/workloads/{target}/rollback"),
            serde_json::json!({ "params": params }),
        ).await
    }

    async fn post(&self, path: &str, body: Value) -> Result<(), ControllerError> {
        let url = format!("{}{path}", self.base_url);
        let mut req = self.client.post(&url).json(&body);
        if let Some(auth) = &self.auth_header {
            req = req.header("Authorization", auth);
        }
        let resp = req.send().await
            .map_err(|e| ControllerError::Orchestrator(format!("POST {path}: {e}")))?;
        check_status(path, resp).await
    }

    async fn patch(&self, path: &str, body: Value) -> Result<(), ControllerError> {
        let url = format!("{}{path}", self.base_url);
        let mut req = self.client.patch(&url).json(&body);
        if let Some(auth) = &self.auth_header {
            req = req.header("Authorization", auth);
        }
        let resp = req.send().await
            .map_err(|e| ControllerError::Orchestrator(format!("PATCH {path}: {e}")))?;
        check_status(path, resp).await
    }
}

async fn check_status(path: &str, resp: reqwest::Response) -> Result<(), ControllerError> {
    let status = resp.status();
    if status.is_success() {
        return Ok(());
    }
    let body = resp.text().await.unwrap_or_default();
    Err(ControllerError::Orchestrator(format!(
        "{path} returned HTTP {status}: {body}"
    )))
}
