//! Storage System
//!
//! Provides storage abstraction layer for policies with SurrealDB backend
//! and generic database support for SQLite.

pub mod database;
pub mod surrealdb_storage;

use std::sync::Arc;

use async_trait::async_trait;
pub use database::{Database, DatabaseConfig};
use serde::{Deserialize, Serialize};
use surrealdb::types::SurrealValue;
// TODO: Re-enable when policies module is fixed
// use crate::policies::{PolicyMetadata, PolicyVersion};
// use crate::policies::versioning::RollbackResult;

// Import from surrealdb_storage (temporary placeholders)
pub use surrealdb_storage::{PolicyMetadata, PolicyVersion, RollbackResult};

use crate::error::{infrastructure, ControlCenterError, Result};
use crate::simple_config::Config;

/// Policy storage trait
#[async_trait]
pub trait PolicyStorage: Send + Sync {
    /// Store a policy
    async fn store_policy(&self, id: &str, content: &str, metadata: &PolicyMetadata) -> Result<()>;

    /// Get policy content by ID
    async fn get_policy(&self, id: &str) -> Result<String>;

    /// Get policy metadata by ID
    async fn get_policy_metadata(&self, id: &str) -> Result<PolicyMetadata>;

    /// List all policies
    async fn list_policies(&self) -> Result<Vec<PolicyMetadata>>;

    /// Delete policy by ID
    async fn delete_policy(&self, id: &str) -> Result<()>;

    /// Update policy metadata
    async fn update_policy_metadata(&self, id: &str, metadata: &PolicyMetadata) -> Result<()>;

    /// Search policies by query
    async fn search_policies(&self, query: &PolicySearchQuery) -> Result<Vec<PolicyMetadata>>;

    // Version management methods

    /// Store a policy version
    async fn store_policy_version(&self, version: &PolicyVersion) -> Result<()>;

    /// Get policy version by version number
    async fn get_policy_version(
        &self,
        policy_id: &str,
        version_number: u32,
    ) -> Result<Option<PolicyVersion>>;

    /// Get current active policy version
    async fn get_current_policy_version(&self, policy_id: &str) -> Result<Option<PolicyVersion>>;

    /// List all versions for a policy
    async fn list_policy_versions(&self, policy_id: &str) -> Result<Vec<PolicyVersion>>;

    /// Deactivate a policy version
    async fn deactivate_policy_version(&self, version_id: &str) -> Result<()>;

    /// Delete a policy version
    async fn delete_policy_version(&self, version_id: &str) -> Result<()>;

    /// Tag a policy version
    async fn tag_policy_version(
        &self,
        policy_id: &str,
        version_number: u32,
        tag: &str,
    ) -> Result<()>;

    /// Get policy versions by tag
    async fn get_policy_versions_by_tag(
        &self,
        policy_id: &str,
        tag: &str,
    ) -> Result<Vec<PolicyVersion>>;

    /// Record rollback operation
    async fn record_rollback_operation(
        &self,
        policy_id: &str,
        rollback: &RollbackResult,
    ) -> Result<()>;

    // Audit and metrics methods

    /// Store policy evaluation event
    async fn store_policy_evaluation(&self, evaluation: &PolicyEvaluationEvent) -> Result<()>;

    /// Get policy evaluation history
    async fn get_policy_evaluations(
        &self,
        policy_id: Option<&str>,
        limit: usize,
    ) -> Result<Vec<PolicyEvaluationEvent>>;

    /// Get policy metrics
    async fn get_policy_metrics(&self, policy_id: &str) -> Result<PolicyMetrics>;

    /// Store compliance check result
    async fn store_compliance_check(&self, result: &ComplianceCheckResult) -> Result<()>;

    /// Get compliance history
    async fn get_compliance_history(
        &self,
        framework: Option<&str>,
    ) -> Result<Vec<ComplianceCheckResult>>;
}

/// Policy search query
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicySearchQuery {
    pub text_query: Option<String>,
    pub category: Option<String>,
    pub tags: Vec<String>,
    pub enabled_only: bool,
    pub created_after: Option<chrono::DateTime<chrono::Utc>>,
    pub created_before: Option<chrono::DateTime<chrono::Utc>>,
    pub limit: usize,
    pub offset: usize,
}

impl Default for PolicySearchQuery {
    fn default() -> Self {
        Self {
            text_query: None,
            category: None,
            tags: Vec::new(),
            enabled_only: true,
            created_after: None,
            created_before: None,
            limit: 100,
            offset: 0,
        }
    }
}

/// Policy evaluation event for audit trail
#[derive(Debug, Clone, Serialize, Deserialize, SurrealValue)]
#[surreal(crate = "surrealdb::types")]
pub struct PolicyEvaluationEvent {
    pub id: String,
    pub policy_id: String,
    pub principal_id: String,
    pub action_id: String,
    pub resource_id: String,
    pub decision: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub context: serde_json::Value,
    pub execution_time_ms: u64,
    pub errors: Vec<String>,
}

/// Policy metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicyMetrics {
    pub policy_id: String,
    pub total_evaluations: u64,
    pub allow_decisions: u64,
    pub deny_decisions: u64,
    pub average_execution_time_ms: f64,
    pub error_count: u64,
    pub last_evaluation: Option<chrono::DateTime<chrono::Utc>>,
    pub period_start: chrono::DateTime<chrono::Utc>,
    pub period_end: chrono::DateTime<chrono::Utc>,
}

/// Compliance check result
#[derive(Debug, Clone, Serialize, Deserialize, SurrealValue)]
#[surreal(crate = "surrealdb::types")]
pub struct ComplianceCheckResult {
    pub id: String,
    pub framework: String,
    pub check_type: String,
    pub policy_id: Option<String>,
    pub compliant: bool,
    pub violations: Vec<String>,
    pub recommendations: Vec<String>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub metadata: serde_json::Value,
}

/// Create policy storage based on configuration
pub async fn create_policy_storage(config: &Config) -> Result<Arc<dyn PolicyStorage>> {
    match config.database.url.as_str() {
        url if url.starts_with("surreal://")
            || url.starts_with("ws://")
            || url.starts_with("wss://") =>
        {
            let storage = surrealdb_storage::SurrealDbPolicyStorage::new(config).await?;
            Ok(Arc::new(storage))
        }
        "memory" => {
            // For development/testing - could implement in-memory storage
            let storage = surrealdb_storage::SurrealDbPolicyStorage::new_memory(config).await?;
            Ok(Arc::new(storage))
        }
        _ => Err(ControlCenterError::Infrastructure(
            infrastructure::InfrastructureError::Configuration(format!(
                "Unsupported database URL: {}",
                config.database.url
            )),
        )),
    }
}
