//! Secret Sharing Service - Phase 3.2
//!
//! Manages cross-workspace secret sharing with permission control,
//! access tracking, and Cedar policy enforcement.

use std::collections::HashMap;
use std::sync::Arc;

use chrono::Utc;
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
use tracing::{info, warn};

use crate::error::{http, infrastructure, ControlCenterError, Result};

/// Access permission for a shared secret
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AccessPermission {
    Read,
    ReadWrite,
    Rotate,
}

impl AccessPermission {
    pub fn as_str(&self) -> &'static str {
        match self {
            AccessPermission::Read => "read",
            AccessPermission::ReadWrite => "read_write",
            AccessPermission::Rotate => "rotate",
        }
    }
}

/// Shared secret grant
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecretGrant {
    /// Unique grant ID
    pub id: String,

    /// Secret path in source workspace (source_workspace/domain/name)
    pub secret_path: String,

    /// Source workspace ID
    pub source_workspace: String,

    /// Target workspace ID
    pub target_workspace: String,

    /// Target user/group that can access the secret
    pub grantee: String,

    /// Permission level
    pub permission: AccessPermission,

    /// Grant status (active, revoked, expired)
    pub status: GrantStatus,

    /// Grant creation time
    pub created_at: String,

    /// Grant expiration time (optional)
    pub expires_at: Option<String>,

    /// Who created this grant
    pub granted_by: String,

    /// Reason for grant
    pub reason: Option<String>,

    /// Number of times the shared secret has been accessed
    pub access_count: i32,

    /// Last access time
    pub last_accessed: Option<String>,

    /// Require approval for access (sensitive secrets)
    pub require_approval: bool,

    /// Approvals pending
    pub pending_approvals: Vec<String>,
}

/// Grant status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum GrantStatus {
    Active,
    Revoked,
    Expired,
    Pending,
}

/// Cross-workspace secret access log
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessLog {
    pub id: String,
    pub grant_id: String,
    pub accessor: String,
    pub action: String,
    pub timestamp: String,
    pub success: bool,
    pub error: Option<String>,
}

/// Request to create a secret grant
#[derive(Debug, Clone)]
pub struct CreateGrantRequest<'a> {
    pub secret_path: &'a str,
    pub source_workspace: &'a str,
    pub target_workspace: &'a str,
    pub grantee: &'a str,
    pub permission: AccessPermission,
    pub reason: Option<String>,
    pub require_approval: bool,
    pub granted_by: &'a str,
}

/// Secret Sharing Service
pub struct SecretSharing {
    /// Grants keyed by grant ID
    grants: Arc<RwLock<HashMap<String, SecretGrant>>>,

    /// Access logs for audit trail
    access_logs: Arc<RwLock<Vec<AccessLog>>>,

    /// Workspace trust relationships
    trusted_workspaces: Arc<RwLock<HashMap<String, Vec<String>>>>,
}

impl Default for SecretSharing {
    fn default() -> Self {
        Self::new()
    }
}

impl SecretSharing {
    /// Create new secret sharing service
    pub fn new() -> Self {
        Self {
            grants: Arc::new(RwLock::new(HashMap::new())),
            access_logs: Arc::new(RwLock::new(Vec::new())),
            trusted_workspaces: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Request secret sharing from source to target workspace
    pub async fn create_grant(&self, req: CreateGrantRequest<'_>) -> Result<SecretGrant> {
        let grant = SecretGrant {
            id: uuid::Uuid::new_v4().to_string(),
            secret_path: req.secret_path.to_string(),
            source_workspace: req.source_workspace.to_string(),
            target_workspace: req.target_workspace.to_string(),
            grantee: req.grantee.to_string(),
            permission: req.permission,
            status: if req.require_approval {
                GrantStatus::Pending
            } else {
                GrantStatus::Active
            },
            created_at: Utc::now().to_rfc3339(),
            expires_at: None,
            granted_by: req.granted_by.to_string(),
            reason: req.reason,
            access_count: 0,
            last_accessed: None,
            require_approval: req.require_approval,
            pending_approvals: vec![],
        };

        let mut grants = self.grants.write().await;
        grants.insert(grant.id.clone(), grant.clone());

        info!(
            grant_id = %grant.id,
            secret = %req.secret_path,
            from = %req.source_workspace,
            to = %req.target_workspace,
            grantee = %req.grantee,
            "Secret sharing grant created"
        );

        Ok(grant)
    }

    /// Get a secret grant
    pub async fn get_grant(&self, grant_id: &str) -> Result<Option<SecretGrant>> {
        let grants = self.grants.read().await;
        Ok(grants.get(grant_id).cloned())
    }

    /// List grants for a workspace (as source or target)
    pub async fn list_grants(&self, workspace_id: &str) -> Result<Vec<SecretGrant>> {
        let grants = self.grants.read().await;
        let filtered: Vec<SecretGrant> = grants
            .values()
            .filter(|g| g.source_workspace == workspace_id || g.target_workspace == workspace_id)
            .cloned()
            .collect();
        Ok(filtered)
    }

    /// List active grants for a secret
    pub async fn list_active_grants_for_secret(
        &self,
        secret_path: &str,
    ) -> Result<Vec<SecretGrant>> {
        let grants = self.grants.read().await;
        let active: Vec<SecretGrant> = grants
            .values()
            .filter(|g| g.secret_path == secret_path && g.status == GrantStatus::Active)
            .cloned()
            .collect();
        Ok(active)
    }

    /// Check if a user can access a secret from another workspace
    pub async fn can_access(
        &self,
        secret_path: &str,
        user: &str,
        permission_required: AccessPermission,
    ) -> Result<bool> {
        let grants = self.grants.read().await;

        let has_access = grants.values().any(|g| {
            g.secret_path == secret_path
                    && (g.grantee == user || g.grantee == "*") // "*" means anyone in workspace
                    && g.status == GrantStatus::Active
                    && self.permission_sufficient(g.permission, permission_required)
        });

        Ok(has_access)
    }

    /// Log secret access
    pub async fn log_access(
        &self,
        grant_id: &str,
        accessor: &str,
        action: &str,
        success: bool,
        error: Option<String>,
    ) -> Result<()> {
        let log = AccessLog {
            id: uuid::Uuid::new_v4().to_string(),
            grant_id: grant_id.to_string(),
            accessor: accessor.to_string(),
            action: action.to_string(),
            timestamp: Utc::now().to_rfc3339(),
            success,
            error,
        };

        // Update grant access count and timestamp
        let mut grants = self.grants.write().await;
        if let Some(grant) = grants.get_mut(grant_id) {
            if success {
                grant.access_count += 1;
                grant.last_accessed = Some(Utc::now().to_rfc3339());
            }
        }
        drop(grants);

        let mut logs = self.access_logs.write().await;
        logs.push(log);

        Ok(())
    }

    /// Get access log for a grant
    pub async fn get_access_log(&self, grant_id: &str) -> Result<Vec<AccessLog>> {
        let logs = self.access_logs.read().await;
        Ok(logs
            .iter()
            .filter(|l| l.grant_id == grant_id)
            .cloned()
            .collect())
    }

    /// Approve a pending grant
    pub async fn approve_grant(&self, grant_id: &str, approver: &str) -> Result<()> {
        let mut grants = self.grants.write().await;

        if let Some(grant) = grants.get_mut(grant_id) {
            if grant.status == GrantStatus::Pending {
                grant.status = GrantStatus::Active;
                info!(
                    grant_id = %grant_id,
                    approver = %approver,
                    "Grant approved"
                );
                Ok(())
            } else {
                Err(crate::error::ControlCenterError::from(
                    infrastructure::internal_error("Grant is not pending"),
                ))
            }
        } else {
            let msg = "Grant not found";
            Err(crate::error::ControlCenterError::Http(http::not_found(msg)))
        }
    }

    /// Revoke a secret grant
    pub async fn revoke_grant(&self, grant_id: &str, reason: &str) -> Result<()> {
        let mut grants = self.grants.write().await;

        if let Some(grant) = grants.get_mut(grant_id) {
            grant.status = GrantStatus::Revoked;
            warn!(
                grant_id = %grant_id,
                reason = %reason,
                "Grant revoked"
            );
            Ok(())
        } else {
            let msg = "Grant not found";
            Err(crate::error::ControlCenterError::Http(http::not_found(msg)))
        }
    }

    /// Revoke all grants for a secret
    pub async fn revoke_all_for_secret(&self, secret_path: &str, reason: &str) -> Result<()> {
        let mut grants = self.grants.write().await;

        for grant in grants.values_mut() {
            if grant.secret_path == secret_path {
                grant.status = GrantStatus::Revoked;
            }
        }

        warn!(
            secret = %secret_path,
            reason = %reason,
            "All grants for secret revoked"
        );

        Ok(())
    }

    /// Get sharing statistics
    pub async fn get_stats(&self) -> Result<SharingStats> {
        let grants = self.grants.read().await;
        let logs = self.access_logs.read().await;

        let stats = SharingStats {
            total_grants: grants.len(),
            active_grants: grants
                .values()
                .filter(|g| g.status == GrantStatus::Active)
                .count(),
            pending_grants: grants
                .values()
                .filter(|g| g.status == GrantStatus::Pending)
                .count(),
            revoked_grants: grants
                .values()
                .filter(|g| g.status == GrantStatus::Revoked)
                .count(),
            total_accesses: logs.len(),
            successful_accesses: logs.iter().filter(|l| l.success).count(),
            failed_accesses: logs.iter().filter(|l| !l.success).count(),
        };

        Ok(stats)
    }

    /// Add trusted workspace relationship
    pub async fn add_trusted_workspace(&self, source: &str, target: &str) -> Result<()> {
        let mut trusted = self.trusted_workspaces.write().await;
        trusted
            .entry(source.to_string())
            .or_insert_with(Vec::new)
            .push(target.to_string());

        info!(
            from = %source,
            to = %target,
            "Workspace trust relationship added"
        );

        Ok(())
    }

    /// Check if workspace is trusted
    pub async fn is_trusted(&self, source: &str, target: &str) -> Result<bool> {
        let trusted = self.trusted_workspaces.read().await;
        Ok(trusted
            .get(source)
            .map(|targets| targets.contains(&target.to_string()))
            .unwrap_or(false))
    }

    /// Helper: Check if permission is sufficient
    fn permission_sufficient(&self, granted: AccessPermission, required: AccessPermission) -> bool {
        matches!(
            (granted, required),
            (AccessPermission::ReadWrite, _)
                | (AccessPermission::Rotate, _)
                | (AccessPermission::Read, AccessPermission::Read)
        )
    }
}

/// Sharing statistics
#[derive(Debug, Serialize, Deserialize)]
pub struct SharingStats {
    pub total_grants: usize,
    pub active_grants: usize,
    pub pending_grants: usize,
    pub revoked_grants: usize,
    pub total_accesses: usize,
    pub successful_accesses: usize,
    pub failed_accesses: usize,
}

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

    #[tokio::test]
    async fn test_create_grant() {
        let sharing = SecretSharing::new();
        let grant = sharing
            .create_grant(CreateGrantRequest {
                secret_path: "librecloud/postgres/password",
                source_workspace: "librecloud",
                target_workspace: "staging",
                grantee: "alice",
                permission: AccessPermission::Read,
                reason: Some("DBA access needed".to_string()),
                require_approval: false,
                granted_by: "admin",
            })
            .await
            .expect("Should create grant");

        assert_eq!(grant.source_workspace, "librecloud");
        assert_eq!(grant.target_workspace, "staging");
        assert_eq!(grant.status, GrantStatus::Active);
    }

    #[tokio::test]
    async fn test_can_access() {
        let sharing = SecretSharing::new();
        sharing
            .create_grant(CreateGrantRequest {
                secret_path: "test/secret",
                source_workspace: "source",
                target_workspace: "target",
                grantee: "user1",
                permission: AccessPermission::Read,
                reason: None,
                require_approval: false,
                granted_by: "admin",
            })
            .await
            .expect("Should create grant");

        let can_access = sharing
            .can_access("test/secret", "user1", AccessPermission::Read)
            .await
            .expect("Should check access");

        assert!(can_access);
    }

    #[tokio::test]
    async fn test_revoke_grant() {
        let sharing = SecretSharing::new();
        let grant = sharing
            .create_grant(CreateGrantRequest {
                secret_path: "test/secret",
                source_workspace: "source",
                target_workspace: "target",
                grantee: "user1",
                permission: AccessPermission::Read,
                reason: None,
                require_approval: false,
                granted_by: "admin",
            })
            .await
            .expect("Should create grant");

        sharing
            .revoke_grant(&grant.id, "Security incident")
            .await
            .expect("Should revoke grant");

        let can_access = sharing
            .can_access("test/secret", "user1", AccessPermission::Read)
            .await
            .expect("Should check access");

        assert!(!can_access);
    }
}
