//! Hosting client implementation

use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE};
use serde::{de::DeserializeOwned, Serialize};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;

use super::error::{HostingError, Result};
use super::types::*;

/// Synor Hosting Client
pub struct HostingClient {
    config: HostingConfig,
    http_client: reqwest::Client,
    closed: Arc<AtomicBool>,
}

impl HostingClient {
    /// Create a new hosting client
    pub fn new(config: HostingConfig) -> Self {
        let mut headers = HeaderMap::new();
        headers.insert(
            AUTHORIZATION,
            HeaderValue::from_str(&format!("Bearer {}", config.api_key)).unwrap(),
        );
        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
        headers.insert("X-SDK-Version", HeaderValue::from_static("rust/0.1.0"));

        let http_client = reqwest::Client::builder()
            .default_headers(headers)
            .timeout(Duration::from_secs(config.timeout_secs))
            .build()
            .expect("Failed to create HTTP client");

        Self {
            config,
            http_client,
            closed: Arc::new(AtomicBool::new(false)),
        }
    }

    // ==================== Domain Operations ====================

    /// Check domain availability
    pub async fn check_availability(&self, name: &str) -> Result<DomainAvailability> {
        self.request("GET", &format!("/domains/check/{}", urlencoding::encode(name)), Option::<()>::None).await
    }

    /// Register a new domain
    pub async fn register_domain(&self, name: &str, options: Option<RegisterDomainOptions>) -> Result<Domain> {
        #[derive(Serialize)]
        struct Request {
            name: String,
            #[serde(flatten)]
            options: Option<RegisterDomainOptions>,
        }
        self.request("POST", "/domains", Some(Request { name: name.to_string(), options })).await
    }

    /// Get domain information
    pub async fn get_domain(&self, name: &str) -> Result<Domain> {
        self.request("GET", &format!("/domains/{}", urlencoding::encode(name)), Option::<()>::None).await
    }

    /// List all domains
    pub async fn list_domains(&self) -> Result<Vec<Domain>> {
        #[derive(Deserialize)]
        struct Response {
            domains: Vec<Domain>,
        }
        let resp: Response = self.request("GET", "/domains", Option::<()>::None).await?;
        Ok(resp.domains)
    }

    /// Update domain record
    pub async fn update_domain_record(&self, name: &str, record: &DomainRecord) -> Result<Domain> {
        self.request("PUT", &format!("/domains/{}/record", urlencoding::encode(name)), Some(record)).await
    }

    /// Resolve a domain
    pub async fn resolve_domain(&self, name: &str) -> Result<DomainRecord> {
        self.request("GET", &format!("/domains/{}/resolve", urlencoding::encode(name)), Option::<()>::None).await
    }

    /// Renew a domain
    pub async fn renew_domain(&self, name: &str, years: i32) -> Result<Domain> {
        #[derive(Serialize)]
        struct Request {
            years: i32,
        }
        self.request("POST", &format!("/domains/{}/renew", urlencoding::encode(name)), Some(Request { years })).await
    }

    // ==================== DNS Operations ====================

    /// Get DNS zone for a domain
    pub async fn get_dns_zone(&self, domain: &str) -> Result<DnsZone> {
        self.request("GET", &format!("/dns/{}", urlencoding::encode(domain)), Option::<()>::None).await
    }

    /// Set DNS records for a domain
    pub async fn set_dns_records(&self, domain: &str, records: Vec<DnsRecord>) -> Result<DnsZone> {
        #[derive(Serialize)]
        struct Request {
            records: Vec<DnsRecord>,
        }
        self.request("PUT", &format!("/dns/{}", urlencoding::encode(domain)), Some(Request { records })).await
    }

    /// Add a DNS record
    pub async fn add_dns_record(&self, domain: &str, record: DnsRecord) -> Result<DnsZone> {
        self.request("POST", &format!("/dns/{}/records", urlencoding::encode(domain)), Some(record)).await
    }

    /// Delete a DNS record
    pub async fn delete_dns_record(&self, domain: &str, record_type: &str, name: &str) -> Result<DnsZone> {
        self.request(
            "DELETE",
            &format!("/dns/{}/records/{}/{}", urlencoding::encode(domain), record_type, urlencoding::encode(name)),
            Option::<()>::None,
        ).await
    }

    // ==================== Deployment Operations ====================

    /// Deploy a site from CID
    pub async fn deploy(&self, cid: &str, options: Option<DeployOptions>) -> Result<Deployment> {
        #[derive(Serialize)]
        struct Request {
            cid: String,
            #[serde(flatten)]
            options: Option<DeployOptions>,
        }
        self.request("POST", "/deployments", Some(Request { cid: cid.to_string(), options })).await
    }

    /// Get deployment by ID
    pub async fn get_deployment(&self, id: &str) -> Result<Deployment> {
        self.request("GET", &format!("/deployments/{}", urlencoding::encode(id)), Option::<()>::None).await
    }

    /// List deployments
    pub async fn list_deployments(&self, domain: Option<&str>) -> Result<Vec<Deployment>> {
        let path = match domain {
            Some(d) => format!("/deployments?domain={}", urlencoding::encode(d)),
            None => "/deployments".to_string(),
        };
        #[derive(Deserialize)]
        struct Response {
            deployments: Vec<Deployment>,
        }
        let resp: Response = self.request("GET", &path, Option::<()>::None).await?;
        Ok(resp.deployments)
    }

    /// Rollback to a previous deployment
    pub async fn rollback(&self, domain: &str, deployment_id: &str) -> Result<Deployment> {
        #[derive(Serialize)]
        struct Request {
            domain: String,
        }
        self.request(
            "POST",
            &format!("/deployments/{}/rollback", urlencoding::encode(deployment_id)),
            Some(Request { domain: domain.to_string() }),
        ).await
    }

    /// Delete a deployment
    pub async fn delete_deployment(&self, id: &str) -> Result<()> {
        self.request::<_, serde_json::Value>("DELETE", &format!("/deployments/{}", urlencoding::encode(id)), Option::<()>::None).await?;
        Ok(())
    }

    /// Get deployment stats
    pub async fn get_deployment_stats(&self, id: &str, period: &str) -> Result<DeploymentStats> {
        self.request(
            "GET",
            &format!("/deployments/{}/stats?period={}", urlencoding::encode(id), urlencoding::encode(period)),
            Option::<()>::None,
        ).await
    }

    // ==================== SSL Operations ====================

    /// Provision SSL certificate
    pub async fn provision_ssl(&self, domain: &str, options: Option<ProvisionSslOptions>) -> Result<Certificate> {
        self.request("POST", &format!("/ssl/{}", urlencoding::encode(domain)), options.or(Some(ProvisionSslOptions::default()))).await
    }

    /// Get certificate status
    pub async fn get_certificate(&self, domain: &str) -> Result<Certificate> {
        self.request("GET", &format!("/ssl/{}", urlencoding::encode(domain)), Option::<()>::None).await
    }

    /// Renew SSL certificate
    pub async fn renew_certificate(&self, domain: &str) -> Result<Certificate> {
        self.request("POST", &format!("/ssl/{}/renew", urlencoding::encode(domain)), Option::<()>::None).await
    }

    /// Delete/revoke SSL certificate
    pub async fn delete_certificate(&self, domain: &str) -> Result<()> {
        self.request::<_, serde_json::Value>("DELETE", &format!("/ssl/{}", urlencoding::encode(domain)), Option::<()>::None).await?;
        Ok(())
    }

    // ==================== Site Configuration ====================

    /// Get site configuration
    pub async fn get_site_config(&self, domain: &str) -> Result<SiteConfig> {
        self.request("GET", &format!("/sites/{}/config", urlencoding::encode(domain)), Option::<()>::None).await
    }

    /// Update site configuration
    pub async fn update_site_config(&self, domain: &str, config: serde_json::Value) -> Result<SiteConfig> {
        self.request("PATCH", &format!("/sites/{}/config", urlencoding::encode(domain)), Some(config)).await
    }

    /// Purge CDN cache
    pub async fn purge_cache(&self, domain: &str, paths: Option<Vec<String>>) -> Result<u64> {
        #[derive(Serialize)]
        struct Request {
            #[serde(skip_serializing_if = "Option::is_none")]
            paths: Option<Vec<String>>,
        }
        #[derive(Deserialize)]
        struct Response {
            purged: u64,
        }
        let resp: Response = self.request("DELETE", &format!("/sites/{}/cache", urlencoding::encode(domain)), Some(Request { paths })).await?;
        Ok(resp.purged)
    }

    // ==================== Analytics ====================

    /// Get site analytics
    pub async fn get_analytics(&self, domain: &str, options: Option<AnalyticsOptions>) -> Result<AnalyticsData> {
        let mut path = format!("/sites/{}/analytics", urlencoding::encode(domain));
        if let Some(opts) = options {
            let mut params = vec![];
            if let Some(period) = opts.period {
                params.push(format!("period={}", urlencoding::encode(&period)));
            }
            if let Some(start) = opts.start {
                params.push(format!("start={}", urlencoding::encode(&start)));
            }
            if let Some(end) = opts.end {
                params.push(format!("end={}", urlencoding::encode(&end)));
            }
            if !params.is_empty() {
                path = format!("{}?{}", path, params.join("&"));
            }
        }
        self.request("GET", &path, Option::<()>::None).await
    }

    // ==================== Lifecycle ====================

    /// Close the client
    pub fn close(&self) {
        self.closed.store(true, Ordering::SeqCst);
    }

    /// Check if client is closed
    pub fn is_closed(&self) -> bool {
        self.closed.load(Ordering::SeqCst)
    }

    /// Health check
    pub async fn health_check(&self) -> bool {
        #[derive(Deserialize)]
        struct Response {
            status: String,
        }
        match self.request::<_, Response>("GET", "/health", Option::<()>::None).await {
            Ok(resp) => resp.status == "healthy",
            Err(_) => false,
        }
    }

    async fn request<B: Serialize, R: DeserializeOwned>(
        &self,
        method: &str,
        path: &str,
        body: Option<B>,
    ) -> Result<R> {
        if self.is_closed() {
            return Err(HostingError::new("Client has been closed"));
        }

        let mut last_error = None;

        for attempt in 0..self.config.retries {
            match self.do_request(method, path, &body).await {
                Ok(result) => return Ok(result),
                Err(e) => {
                    if self.config.debug {
                        eprintln!("Attempt {} failed: {}", attempt + 1, e);
                    }
                    last_error = Some(e);
                    if attempt < self.config.retries - 1 {
                        tokio::time::sleep(Duration::from_secs((attempt + 1) as u64)).await;
                    }
                }
            }
        }

        Err(last_error.unwrap_or_else(|| HostingError::new("Unknown error after retries")))
    }

    async fn do_request<B: Serialize, R: DeserializeOwned>(
        &self,
        method: &str,
        path: &str,
        body: &Option<B>,
    ) -> Result<R> {
        let url = format!("{}{}", self.config.endpoint, path);

        let mut request = match method {
            "GET" => self.http_client.get(&url),
            "POST" => self.http_client.post(&url),
            "PUT" => self.http_client.put(&url),
            "PATCH" => self.http_client.patch(&url),
            "DELETE" => self.http_client.delete(&url),
            _ => return Err(HostingError::new(format!("Unknown method: {}", method))),
        };

        if let Some(b) = body {
            request = request.json(b);
        }

        let response = request.send().await?;
        let status = response.status();

        if !status.is_success() {
            let error_body: serde_json::Value = response.json().await.unwrap_or_default();
            let message = error_body["message"]
                .as_str()
                .or_else(|| error_body["error"].as_str())
                .unwrap_or(&format!("HTTP {}", status.as_u16()));

            return Err(HostingError::new(message)
                .with_status(status.as_u16())
                .with_code(error_body["code"].as_str().unwrap_or("").to_string()));
        }

        let result = response.json().await?;
        Ok(result)
    }
}
