use anyhow::{Context, Result};
use reqwest::Client;
use serde::{Deserialize, Serialize};

const CF_API_BASE: &str = "https://api.cloudflare.com/client/v4";

#[derive(Debug, Serialize)]
struct CreateDnsRecord {
    #[serde(rename = "type")]
    record_type: String,
    name: String,
    content: String,
    proxied: bool,
    ttl: u32,
}

#[derive(Debug, Deserialize)]
struct CfApiResponse<T> {
    success: bool,
    result: Option<T>,
    errors: Vec<CfError>,
}

#[derive(Debug, Deserialize)]
struct CfError {
    message: String,
}

#[derive(Debug, Deserialize)]
struct DnsRecord {
    id: String,
}

/// Cloudflare DNS management service.
///
/// Manages DNS records for tenant subdomains (e.g., myserver.corrosionmgmt.com)
/// and custom domains. Uses the Cloudflare API v4 to create and manage
/// CNAME records pointing to the platform's load balancer.
pub struct CloudflareService {
    http: Client,
    api_token: String,
    zone_id: String,
    base_domain: String,
}

impl CloudflareService {
    pub fn new(api_token: String, zone_id: String, base_domain: String) -> Self {
        Self {
            http: Client::new(),
            api_token,
            zone_id,
            base_domain,
        }
    }

    /// Create a subdomain CNAME record for a new license.
    ///
    /// Creates: {subdomain}.{base_domain} -> panel.{base_domain}
    pub async fn create_subdomain(&self, subdomain: &str) -> Result<String> {
        let record = CreateDnsRecord {
            record_type: "CNAME".to_string(),
            name: format!("{}.{}", subdomain, self.base_domain),
            content: format!("panel.{}", self.base_domain),
            proxied: true,
            ttl: 1, // Auto TTL when proxied
        };

        let url = format!("{}/zones/{}/dns_records", CF_API_BASE, self.zone_id);

        let response = self
            .http
            .post(&url)
            .bearer_auth(&self.api_token)
            .json(&record)
            .send()
            .await
            .context("Failed to call Cloudflare API")?;

        let body: CfApiResponse<DnsRecord> = response
            .json()
            .await
            .context("Failed to parse Cloudflare response")?;

        if !body.success {
            let errors: Vec<String> = body.errors.iter().map(|e| e.message.clone()).collect();
            anyhow::bail!("Cloudflare DNS creation failed: {}", errors.join(", "));
        }

        let record_id = body
            .result
            .map(|r| r.id)
            .unwrap_or_default();

        tracing::info!(
            "Created DNS record for {}.{} (ID: {})",
            subdomain,
            self.base_domain,
            record_id
        );

        Ok(record_id)
    }

    /// Delete a subdomain CNAME record.
    ///
    /// Called when a license is deactivated or transferred.
    pub async fn delete_subdomain(&self, dns_record_id: &str) -> Result<()> {
        let url = format!(
            "{}/zones/{}/dns_records/{}",
            CF_API_BASE, self.zone_id, dns_record_id
        );

        let response = self
            .http
            .delete(&url)
            .bearer_auth(&self.api_token)
            .send()
            .await
            .context("Failed to delete Cloudflare DNS record")?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            anyhow::bail!("Cloudflare DNS deletion failed: {} — {}", status, body);
        }

        tracing::info!("Deleted DNS record: {}", dns_record_id);
        Ok(())
    }

    /// Add a custom domain with CNAME verification.
    ///
    /// Returns the CNAME target that the customer needs to configure
    /// on their own DNS provider.
    pub async fn add_custom_domain(&self, custom_domain: &str) -> Result<String> {
        // The customer needs to point their domain to their subdomain
        let cname_target = format!("panel.{}", self.base_domain);

        // Create an A/CNAME record in our zone for the custom domain
        // Note: For full custom domain support, Cloudflare for SaaS (SSL for SaaS)
        // would be needed. For now, return the CNAME target for the customer.
        tracing::info!(
            "Custom domain {} should CNAME to {}",
            custom_domain,
            cname_target
        );

        Ok(cname_target)
    }

    /// Check if a subdomain is available (no existing DNS record).
    pub async fn check_subdomain_available(&self, subdomain: &str) -> Result<bool> {
        let full_name = format!("{}.{}", subdomain, self.base_domain);
        let url = format!(
            "{}/zones/{}/dns_records?name={}",
            CF_API_BASE, self.zone_id, full_name
        );

        let response = self
            .http
            .get(&url)
            .bearer_auth(&self.api_token)
            .send()
            .await
            .context("Failed to query Cloudflare DNS records")?;

        let body: CfApiResponse<Vec<DnsRecord>> = response
            .json()
            .await
            .context("Failed to parse Cloudflare response")?;

        // Available if no records exist for this name
        Ok(body.result.map_or(true, |records| records.is_empty()))
    }
}
