//! n8n API Key Integration
//!
//! Manages n8n API Keys through the API Key system

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

/// Request to create an n8n API key
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateN8nApiKeyRequest {
    pub label: String,
    pub expires_at: Option<DateTime<Utc>>,
}

/// Response from creating an n8n API key
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct N8nApiKeyResponse {
    pub id: String,
    pub api_key: String,
    pub label: String,
    pub created_at: Option<DateTime<Utc>>,
    pub expires_at: Option<DateTime<Utc>>,
}

/// n8n API key info (without raw key)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct N8nApiKeyInfo {
    pub id: String,
    pub label: String,
    pub created_at: Option<DateTime<Utc>>,
    pub updated_at: Option<DateTime<Utc>>,
}

/// n8n API client
pub struct N8nClient {
    client: Client,
    base_url: String,
    api_key: String,
}

impl N8nClient {
    pub fn new(api_key: String) -> Result<Self> {
        let base_url = std::env::var("N8N_URL")
            .unwrap_or_else(|_| "https://n8n.momentry.ddns.net".to_string());

        Ok(Self {
            client: Client::new(),
            base_url,
            api_key,
        })
    }

    pub fn with_url(base_url: String, api_key: String) -> Self {
        Self {
            client: Client::new(),
            base_url,
            api_key,
        }
    }

    /// Create a new API key
    pub async fn create_api_key(
        &self,
        request: &CreateN8nApiKeyRequest,
    ) -> Result<N8nApiKeyResponse> {
        let url = format!("{}/api/v1/me/api-keys", self.base_url);

        let mut body = serde_json::json!({
            "label": request.label,
        });

        if let Some(expires_at) = request.expires_at {
            body["expiresAt"] = serde_json::json!(expires_at.to_rfc3339());
        }

        let response = self
            .client
            .post(&url)
            .header("X-N8N-API-KEY", &self.api_key)
            .header("Content-Type", "application/json")
            .json(&body)
            .send()
            .await
            .context("Failed to send create API key request")?;

        if !response.status().is_success() {
            let status = response.status();
            let text = response.text().await.unwrap_or_default();
            anyhow::bail!("Failed to create n8n API key: {} - {}", status, text);
        }

        let api_key: N8nApiKeyResponse = response
            .json()
            .await
            .context("Failed to parse API key response")?;

        Ok(api_key)
    }

    /// List all API keys for the authenticated user
    pub async fn list_api_keys(&self) -> Result<Vec<N8nApiKeyInfo>> {
        let url = format!("{}/api/v1/me/api-keys", self.base_url);

        let response = self
            .client
            .get(&url)
            .header("X-N8N-API-KEY", &self.api_key)
            .send()
            .await
            .context("Failed to send list API keys request")?;

        if !response.status().is_success() {
            let status = response.status();
            let text = response.text().await.unwrap_or_default();
            anyhow::bail!("Failed to list n8n API keys: {} - {}", status, text);
        }

        let data: serde_json::Value = response
            .json()
            .await
            .context("Failed to parse API keys response")?;

        // n8n returns { data: [...] } format
        let keys: Vec<N8nApiKeyInfo> =
            serde_json::from_value(data["data"].clone()).unwrap_or_default();

        Ok(keys)
    }

    /// Delete an API key by ID
    pub async fn delete_api_key(&self, key_id: &str) -> Result<()> {
        let url = format!("{}/api/v1/me/api-keys/{}", self.base_url, key_id);

        let response = self
            .client
            .delete(&url)
            .header("X-N8N-API-KEY", &self.api_key)
            .send()
            .await
            .context("Failed to send delete API key request")?;

        if !response.status().is_success() {
            let status = response.status();
            let text = response.text().await.unwrap_or_default();
            anyhow::bail!("Failed to delete n8n API key: {} - {}", status, text);
        }

        Ok(())
    }

    /// Verify an API key is valid by making a test API call
    pub async fn verify_api_key(&self, api_key: &str) -> Result<bool> {
        let url = format!("{}/api/v1/workflows", self.base_url);

        let response = self
            .client
            .get(&url)
            .header("X-N8N-API-KEY", api_key)
            .send()
            .await
            .context("Failed to send verify API key request")?;

        Ok(response.status().is_success())
    }

    /// Get base URL
    pub fn base_url(&self) -> &str {
        &self.base_url
    }
}

/// Integrated n8n API key record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct N8nApiKeyRecord {
    pub n8n_key_id: String,
    pub label: String,
    pub api_key_last_eight: String,
    pub momentry_api_key_id: Option<String>,
    pub created_at: DateTime<Utc>,
    pub last_verified: Option<DateTime<Utc>>,
}

/// Extract last 8 characters from API key for display
pub fn extract_last_eight(api_key: &str) -> String {
    if api_key.len() <= 8 {
        api_key.to_string()
    } else {
        api_key[api_key.len() - 8..].to_string()
    }
}

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

    #[test]
    fn test_extract_last_eight() {
        assert_eq!(extract_last_eight("n8n_api_1234567890abcdef"), "90abcdef");
        assert_eq!(extract_last_eight("short"), "short");
        assert_eq!(extract_last_eight("12345678"), "12345678");
    }

    #[test]
    fn test_n8n_client_with_url() {
        let client =
            N8nClient::with_url("http://localhost:5678".to_string(), "test_key".to_string());
        assert_eq!(client.base_url(), "http://localhost:5678");
    }
}
