use std::sync::Arc;

use tracing::info;
use uuid::Uuid;
use validator::Validate;

use crate::error::{http, ControlCenterError, Result};
use crate::models::{Permission, PermissionCheckRequest, PermissionResponse};
use crate::services::DatabaseService;

/// Permission service for managing permission operations
#[derive(Clone)]
pub struct PermissionService {
    db: Arc<DatabaseService>,
}

impl PermissionService {
    /// Create a new permission service
    pub fn new(db: Arc<DatabaseService>) -> Self {
        Self { db }
    }

    /// Get permission by ID
    pub async fn get_by_id(&self, permission_id: Uuid) -> Result<Permission> {
        let query = "SELECT * FROM permissions WHERE permission_id = $permission_id";
        let mut result = self
            .db
            .db
            .query(query)
            .bind(("permission_id", permission_id))
            .await?;

        let permissions: Vec<Permission> = result.take(0)?;
        permissions
            .into_iter()
            .next()
            .ok_or_else(|| ControlCenterError::PermissionNotFound(permission_id.to_string()))
    }

    /// Get permission response by ID
    pub async fn get_permission_response_by_id(
        &self,
        permission_id: Uuid,
    ) -> Result<PermissionResponse> {
        let permission = self.get_by_id(permission_id).await?;
        Ok(PermissionResponse::from(permission))
    }

    /// Find permission by name
    pub async fn find_by_name(&self, name: &str) -> Result<Option<Permission>> {
        let query = "SELECT * FROM permissions WHERE name = $name";
        let mut result = self
            .db
            .db
            .query(query)
            .bind(("name", name.to_string()))
            .await?;

        let permissions: Vec<Permission> = result.take(0)?;
        Ok(permissions.into_iter().next())
    }

    /// List all permissions
    pub async fn list_permissions(
        &self,
        resource: Option<String>,
        action: Option<String>,
        limit: Option<usize>,
        offset: Option<usize>,
    ) -> Result<Vec<PermissionResponse>> {
        let mut query = "SELECT * FROM permissions".to_string();
        let mut conditions = Vec::new();

        if let Some(resource) = resource {
            conditions.push(format!("resource = '{}'", resource));
        }

        if let Some(action) = action {
            conditions.push(format!("action = '{}'", action));
        }

        if !conditions.is_empty() {
            query.push_str(&format!(" WHERE {}", conditions.join(" AND ")));
        }

        query.push_str(" ORDER BY resource, action");

        if let Some(limit) = limit {
            query.push_str(&format!(" LIMIT {}", limit));
        }

        if let Some(offset) = offset {
            query.push_str(&format!(" START {}", offset));
        }

        let mut result = self.db.db.query(&query).await?;
        let permissions: Vec<Permission> = result.take(0)?;

        Ok(permissions
            .into_iter()
            .map(PermissionResponse::from)
            .collect())
    }

    /// Check if a permission exists
    pub async fn permission_exists(&self, name: &str) -> Result<bool> {
        let permission = self.find_by_name(name).await?;
        Ok(permission.is_some())
    }

    /// Check permission by resource and action
    pub async fn check_permission(&self, request: PermissionCheckRequest) -> Result<bool> {
        // Validate request
        request
            .validate()
            .map_err(|e| ControlCenterError::Http(http::HttpError::Validation(e.to_string())))?;

        let permission_name = format!("{}:{}", request.resource, request.action);
        self.permission_exists(&permission_name).await
    }

    /// Get permissions by resource
    pub async fn get_permissions_by_resource(
        &self,
        resource: &str,
    ) -> Result<Vec<PermissionResponse>> {
        self.list_permissions(Some(resource.to_string()), None, None, None)
            .await
    }

    /// Get permissions by action
    pub async fn get_permissions_by_action(&self, action: &str) -> Result<Vec<PermissionResponse>> {
        self.list_permissions(None, Some(action.to_string()), None, None)
            .await
    }

    /// Get all unique resources
    pub async fn get_resources(&self) -> Result<Vec<String>> {
        let query = "SELECT DISTINCT resource FROM permissions ORDER BY resource";
        let mut result = self.db.db.query(query).await?;

        let resources: Vec<String> = result.take(0)?;
        Ok(resources)
    }

    /// Get all unique actions
    pub async fn get_actions(&self) -> Result<Vec<String>> {
        let query = "SELECT DISTINCT action FROM permissions ORDER BY action";
        let mut result = self.db.db.query(query).await?;

        let actions: Vec<String> = result.take(0)?;
        Ok(actions)
    }

    /// Get permission count
    pub async fn get_permission_count(&self, resource: Option<String>) -> Result<i64> {
        let mut query = "SELECT count() FROM permissions".to_string();

        if let Some(resource) = resource {
            query.push_str(&format!(" WHERE resource = '{}'", resource));
        }

        let mut result = self.db.db.query(&query).await?;
        let counts: Vec<i64> = result.take(0)?;

        Ok(counts.into_iter().next().unwrap_or(0))
    }

    /// Get permissions by names
    pub async fn get_permissions_by_names(
        &self,
        permission_names: &[String],
    ) -> Result<Vec<Permission>> {
        if permission_names.is_empty() {
            return Ok(Vec::new());
        }

        let query = "SELECT * FROM permissions WHERE name IN $permission_names";
        let mut result = self
            .db
            .db
            .query(query)
            .bind(("permission_names", permission_names.to_vec()))
            .await?;

        let permissions: Vec<Permission> = result.take(0)?;
        Ok(permissions)
    }

    /// Validate permissions exist
    pub async fn validate_permissions_exist(
        &self,
        permission_names: &[String],
    ) -> Result<Vec<String>> {
        let existing_permissions = self.get_permissions_by_names(permission_names).await?;
        let existing_names: std::collections::HashSet<_> = existing_permissions
            .iter()
            .map(|p| p.name.clone())
            .collect();

        let missing_permissions: Vec<String> = permission_names
            .iter()
            .filter(|name| !existing_names.contains(*name))
            .cloned()
            .collect();

        Ok(missing_permissions)
    }

    /// Create a custom permission (for advanced use cases)
    pub async fn create_permission(
        &self,
        name: String,
        resource: String,
        action: String,
        description: Option<String>,
    ) -> Result<PermissionResponse> {
        // Check if permission already exists
        if self.find_by_name(&name).await?.is_some() {
            let msg = format!("Permission '{}' already exists", name);
            return Err(ControlCenterError::Http(http::conflict(&msg)));
        }

        // Create permission
        let permission = Permission::new(name, resource, action, description);

        // Save to database
        let created_permission: Permission = self
            .db
            .db
            .create("permissions")
            .content(permission)
            .await?
            .ok_or_else(|| {
                ControlCenterError::from(crate::error::database::DatabaseError::Database(
                    "Failed to create permission".to_string(),
                ))
            })?;

        info!("Created permission: {}", created_permission.name);

        Ok(PermissionResponse::from(created_permission))
    }

    /// Delete a custom permission (system permissions cannot be deleted)
    pub async fn delete_permission(&self, permission_id: Uuid) -> Result<()> {
        let permission = self.get_by_id(permission_id).await?;

        // Check if permission is in use by any roles
        let roles_using_permission = self.get_roles_using_permission(&permission.name).await?;
        if !roles_using_permission.is_empty() {
            let msg = format!(
                "Cannot delete permission '{}' - it is used by {} roles: {}",
                permission.name,
                roles_using_permission.len(),
                roles_using_permission.join(", ")
            );
            return Err(ControlCenterError::Http(http::conflict(&msg)));
        }

        // Delete permission
        let query = "DELETE permissions WHERE permission_id = $permission_id";
        self.db
            .db
            .query(query)
            .bind(("permission_id", permission_id))
            .await?;

        info!("Deleted permission: {}", permission.name);

        Ok(())
    }

    /// Get roles that use a specific permission
    async fn get_roles_using_permission(&self, permission_name: &str) -> Result<Vec<String>> {
        let query = "SELECT name FROM roles WHERE $permission_name IN permissions";
        let mut result = self
            .db
            .db
            .query(query)
            .bind(("permission_name", permission_name.to_string()))
            .await?;

        let role_names: Vec<String> = result.take(0)?;
        Ok(role_names)
    }
}

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

    #[test]
    fn test_permission_check_request_validation() {
        // Valid request
        let valid_request = PermissionCheckRequest {
            resource: "users".to_string(),
            action: "read".to_string(),
        };
        assert!(valid_request.validate().is_ok());

        // Empty resource
        let empty_resource = PermissionCheckRequest {
            resource: "".to_string(),
            action: "read".to_string(),
        };
        assert!(empty_resource.validate().is_err());

        // Empty action
        let empty_action = PermissionCheckRequest {
            resource: "users".to_string(),
            action: "".to_string(),
        };
        assert!(empty_action.validate().is_err());

        // Both empty
        let both_empty = PermissionCheckRequest {
            resource: "".to_string(),
            action: "".to_string(),
        };
        assert!(both_empty.validate().is_err());
    }

    #[test]
    fn test_permission_name_format() {
        let resource = "users";
        let action = "read";
        let expected_name = "users:read";
        let actual_name = format!("{}:{}", resource, action);
        assert_eq!(actual_name, expected_name);
    }
}
