//! HTTP client for machines service (SSH operations).

use std::time::Duration;

use reqwest::Client;
use serde::{Deserialize, Serialize};

use super::error::{ServiceError, ServiceResult};

/// Client for calling machines service (SSH operations)
#[derive(Debug, Clone)]
pub struct MachinesClient {
    base_url: String,
    http_client: Client,
    timeout: Duration,
}

/// Command execution request
#[derive(Debug, Serialize, Deserialize)]
pub struct ExecuteCommandRequest {
    /// Target machine name or hostname
    pub host: String,
    /// Command to execute
    pub command: String,
}

/// Command execution response
#[derive(Debug, Serialize, Deserialize)]
pub struct ExecuteCommandResponse {
    /// Command output (stdout)
    pub output: String,
    /// Error output (stderr) if any
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    /// Process exit code
    pub exit_code: i32,
    /// Execution time in milliseconds
    pub execution_time_ms: u64,
}

impl MachinesClient {
    /// Create a new machines service client
    pub fn new(base_url: impl Into<String>) -> ServiceResult<Self> {
        let url = base_url.into();
        if !url.starts_with("http://") && !url.starts_with("https://") {
            return Err(ServiceError::InvalidUrl(url));
        }

        Ok(Self {
            base_url: url,
            http_client: Client::new(),
            timeout: Duration::from_secs(30),
        })
    }

    /// Execute a command on a remote machine via SSH
    pub async fn execute_command(
        &self,
        host: impl Into<String>,
        command: impl Into<String>,
    ) -> ServiceResult<ExecuteCommandResponse> {
        let request = ExecuteCommandRequest {
            host: host.into(),
            command: command.into(),
        };

        let url = format!("{}/api/v1/machines/execute", self.base_url);

        let response = self
            .http_client
            .post(&url)
            .timeout(self.timeout)
            .json(&request)
            .send()
            .await?;

        if !response.status().is_success() {
            let status = response.status().as_u16();
            let body = response.text().await.unwrap_or_default();
            return Err(ServiceError::from_status(status, body));
        }

        Ok(response.json().await?)
    }

    /// Health check for machines service
    pub async fn health_check(&self) -> ServiceResult<bool> {
        let url = format!("{}/health", self.base_url);

        match self
            .http_client
            .get(&url)
            .timeout(Duration::from_secs(5))
            .send()
            .await
        {
            Ok(response) => Ok(response.status().is_success()),
            Err(_) => Err(ServiceError::ServiceUnavailable(
                "Machines service unavailable".to_string(),
            )),
        }
    }
}
