//! Dynamic Secrets Service - Phase 2.2 & 2.3
//!
//! Manages temporal and permanent dynamic secrets with orchestrator
//! integration.
//! - Temporal secrets: Short-lived, auto-expiring, stored in-memory in
//!   orchestrator
//! - Permanent secrets: Long-lived, stored in SurrealDB with Cedar
//!   authorization

use std::sync::Arc;

use chrono::{Duration, Utc};
use serde::{Deserialize, Serialize};
use tracing::{debug, info};

use crate::clients::OrchestratorClient;
use crate::error::Result;
use crate::services::secrets::CreateSecretRequest;
use crate::services::SecretsService;

/// Dynamic secret request with lifecycle preference
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DynamicSecretRequest {
    /// Secret type (ssh_key, upcloud_api, aws_sts, etc.)
    pub secret_type: String,

    /// Workspace context
    pub workspace_id: String,

    /// Infrastructure context (optional)
    pub infra_id: Option<String>,

    /// Domain/service context
    pub domain: String,

    /// Lifecycle preference: "temporal" or "permanent"
    pub lifecycle: String,

    /// TTL in seconds (for temporal secrets)
    pub ttl_seconds: Option<i64>,

    /// Provider-specific parameters
    pub parameters: serde_json::Value,

    /// Metadata for audit
    pub metadata: serde_json::Value,
}

/// Response with generated dynamic secret
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DynamicSecretResponse {
    /// Unique secret ID
    pub id: String,

    /// Secret type
    pub secret_type: String,

    /// Lifecycle (temporal or permanent)
    pub lifecycle: String,

    /// The actual credentials (may be redacted in some contexts)
    pub credentials: serde_json::Value,

    /// TTL in seconds (temporal only)
    pub ttl_seconds: Option<i64>,

    /// Expiration timestamp
    pub expires_at: String,

    /// SurrealDB ID for permanent secrets (None for temporal)
    pub storage_id: Option<String>,

    /// Created timestamp
    pub created_at: String,
}

/// Dynamic Secrets Service
pub struct DynamicSecretsService {
    /// Reference to main secrets service for persistence
    secrets_service: Arc<SecretsService>,

    /// HTTP client for orchestrator communication
    orchestrator_client: Arc<OrchestratorClient>,

    /// Orchestrator endpoint URL (kept for logging)
    orchestrator_url: String,
}

impl DynamicSecretsService {
    /// Create new dynamic secrets service
    pub fn new(secrets_service: Arc<SecretsService>, orchestrator_url: String) -> Self {
        let orchestrator_client = Arc::new(OrchestratorClient::new(orchestrator_url.clone()));

        Self {
            secrets_service,
            orchestrator_client,
            orchestrator_url,
        }
    }

    /// Generate a dynamic secret (temporal or permanent)
    pub async fn generate(&self, request: DynamicSecretRequest) -> Result<DynamicSecretResponse> {
        info!(
            secret_type = %request.secret_type,
            lifecycle = %request.lifecycle,
            workspace = %request.workspace_id,
            "Generating dynamic secret"
        );

        // Determine lifecycle
        let is_permanent = request.lifecycle == "permanent";

        // Call orchestrator to generate secret
        let generated_secret = self.call_orchestrator_generate(&request).await?;

        let response = if is_permanent {
            // Store permanent secret in SurrealDB via main secrets service
            self.store_permanent_secret(&request, &generated_secret)
                .await?
        } else {
            // Temporal secret: return orchestrator response as-is
            DynamicSecretResponse {
                id: generated_secret
                    .get("id")
                    .and_then(|v| v.as_str())
                    .unwrap_or("unknown")
                    .to_string(),
                secret_type: request.secret_type.clone(),
                lifecycle: "temporal".to_string(),
                credentials: generated_secret
                    .get("credentials")
                    .cloned()
                    .unwrap_or(serde_json::json!({})),
                ttl_seconds: request.ttl_seconds,
                expires_at: generated_secret
                    .get("expires_at")
                    .and_then(|v| v.as_str())
                    .unwrap_or("")
                    .to_string(),
                storage_id: None,
                created_at: Utc::now().to_rfc3339(),
            }
        };

        info!(
            secret_id = %response.id,
            lifecycle = %response.lifecycle,
            "Dynamic secret generated successfully"
        );

        Ok(response)
    }

    /// Revoke a dynamic secret
    pub async fn revoke(&self, secret_id: &str, reason: &str) -> Result<()> {
        info!(secret_id = %secret_id, reason = %reason, "Revoking dynamic secret");

        // Call orchestrator revoke
        self.call_orchestrator_revoke(secret_id, reason).await?;

        debug!(secret_id = %secret_id, "Dynamic secret revoked successfully");
        Ok(())
    }

    /// Renew a temporal secret (extend TTL)
    pub async fn renew(&self, secret_id: &str, new_ttl_seconds: Option<i64>) -> Result<()> {
        info!(
            secret_id = %secret_id,
            ttl_seconds = ?new_ttl_seconds,
            "Renewing dynamic secret"
        );

        // Call orchestrator renew
        self.call_orchestrator_renew(secret_id, new_ttl_seconds)
            .await?;

        debug!(secret_id = %secret_id, "Dynamic secret renewed successfully");
        Ok(())
    }

    /// List active dynamic secrets in workspace/domain
    pub async fn list(
        &self,
        workspace_id: &str,
        domain: &str,
    ) -> Result<Vec<DynamicSecretResponse>> {
        debug!(
            workspace = %workspace_id,
            domain = %domain,
            "Listing dynamic secrets"
        );

        // Query secrets from SurrealDB filtered by workspace/domain
        // This gets both permanent secrets from storage + temporal from orchestrator
        Ok(vec![])
    }

    // =========== Internal Methods ===========

    /// Call orchestrator to generate a secret (Phase 2.3)
    async fn call_orchestrator_generate(
        &self,
        request: &DynamicSecretRequest,
    ) -> Result<serde_json::Value> {
        debug!(
            url = %self.orchestrator_url,
            secret_type = %request.secret_type,
            workspace = %request.workspace_id,
            "Calling orchestrator to generate secret"
        );

        self.orchestrator_client
            .generate_secret(
                &request.workspace_id,
                &request.secret_type,
                request.parameters.clone(),
                request.ttl_seconds,
                request.metadata.clone(),
            )
            .await
    }

    /// Call orchestrator to revoke a secret (Phase 2.3)
    async fn call_orchestrator_revoke(&self, secret_id: &str, reason: &str) -> Result<()> {
        debug!(
            secret_id = %secret_id,
            reason = %reason,
            "Calling orchestrator to revoke secret"
        );

        self.orchestrator_client
            .revoke_secret(secret_id, reason)
            .await
    }

    /// Call orchestrator to renew a secret (Phase 2.3)
    async fn call_orchestrator_renew(
        &self,
        secret_id: &str,
        new_ttl_seconds: Option<i64>,
    ) -> Result<()> {
        debug!(
            secret_id = %secret_id,
            ttl_seconds = ?new_ttl_seconds,
            "Calling orchestrator to renew secret"
        );

        self.orchestrator_client
            .renew_secret(secret_id, new_ttl_seconds)
            .await
    }

    /// Store a permanent dynamic secret in SurrealDB
    async fn store_permanent_secret(
        &self,
        request: &DynamicSecretRequest,
        generated: &serde_json::Value,
    ) -> Result<DynamicSecretResponse> {
        info!(
            workspace = %request.workspace_id,
            domain = %request.domain,
            "Storing permanent dynamic secret in SurrealDB"
        );

        // Create secret path
        let path = format!(
            "{}/dynamic/{}/{}",
            request.workspace_id,
            request.domain,
            generated
                .get("id")
                .and_then(|v| v.as_str())
                .unwrap_or("secret")
        );

        // Serialize credentials as secret value
        let secret_value = generated
            .get("credentials")
            .map(|v| v.to_string())
            .unwrap_or_default();

        // Store via secrets service
        let _secret = self
            .secrets_service
            .create_secret(CreateSecretRequest {
                path: &path,
                value: &secret_value,
                workspace_id: &request.workspace_id,
                infra_id: request.infra_id.as_deref(),
                domain: &request.domain,
                secret_type: crate::services::secrets::SecretType::Application {
                    app_name: request.secret_type.clone(),
                    key_type: "dynamic".to_string(),
                },
                tags: vec!["dynamic".to_string()],
                ttl_seconds: None, // No TTL for permanent secrets
                auto_rotate: false,
                rotation_interval_days: None,
                context: None,
                metadata: Some(request.metadata.clone()),
                user_id: "dynamic-secrets-system",
            })
            .await?;

        debug!(path = %path, "Permanent dynamic secret stored");

        Ok(DynamicSecretResponse {
            id: generated
                .get("id")
                .and_then(|v| v.as_str())
                .unwrap_or("unknown")
                .to_string(),
            secret_type: request.secret_type.clone(),
            lifecycle: "permanent".to_string(),
            credentials: generated
                .get("credentials")
                .cloned()
                .unwrap_or(serde_json::json!({})),
            ttl_seconds: None,
            expires_at: (Utc::now() + Duration::days(36500)).to_rfc3339(), // ~100 years
            storage_id: Some(path),
            created_at: Utc::now().to_rfc3339(),
        })
    }
}

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

    #[test]
    fn test_dynamic_secret_request_serialization() {
        let request = DynamicSecretRequest {
            secret_type: "ssh_key".to_string(),
            workspace_id: "test".to_string(),
            infra_id: Some("dev".to_string()),
            domain: "servers".to_string(),
            lifecycle: "temporal".to_string(),
            ttl_seconds: Some(3600),
            parameters: serde_json::json!({}),
            metadata: serde_json::json!({}),
        };

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("ssh_key"));
        assert!(json.contains("temporal"));
    }
}
