use anyhow::Result;
use serde::{Deserialize, Serialize};
use shared::auth::{AccessRule, Permission, RBACConfig, ResourceType, Role};
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use tokio::fs as async_fs;

/// TOML configuration structure for RBAC
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RBACTomlConfig {
    pub rbac: RBACSettings,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RBACSettings {
    pub cache_ttl_seconds: Option<u64>,
    pub default_permissions: Option<HashMap<String, Vec<String>>>,
    pub category_hierarchies: Option<HashMap<String, Vec<String>>>,
    pub tag_hierarchies: Option<HashMap<String, Vec<String>>>,
    pub rules: Option<Vec<AccessRuleToml>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessRuleToml {
    pub id: String,
    pub name: Option<String>,
    pub description: Option<String>,
    pub resource_type: String,
    pub resource_name: String,
    pub action: Option<String>,
    pub priority: Option<i32>,
    pub allowed_roles: Option<Vec<String>>,
    pub allowed_permissions: Option<Vec<String>>,
    pub required_categories: Option<Vec<String>>,
    pub required_tags: Option<Vec<String>>,
    pub deny_categories: Option<Vec<String>>,
    pub deny_tags: Option<Vec<String>>,
    pub is_active: Option<bool>,
}

/// RBAC configuration loader
pub struct RBACConfigLoader {
    config_path: String,
}

impl RBACConfigLoader {
    /// Create a new configuration loader
    pub fn new(config_path: impl Into<String>) -> Self {
        Self {
            config_path: config_path.into(),
        }
    }

    /// Load RBAC configuration from TOML file
    pub async fn load_from_file(&self) -> Result<RBACConfig> {
        let content = async_fs::read_to_string(&self.config_path).await?;
        self.load_from_toml_string(&content)
    }

    /// Load RBAC configuration synchronously
    pub fn load_from_file_sync(&self) -> Result<RBACConfig> {
        let content = fs::read_to_string(&self.config_path)?;
        self.load_from_toml_string(&content)
    }

    /// Load RBAC configuration from TOML string
    pub fn load_from_toml_string(&self, content: &str) -> Result<RBACConfig> {
        let toml_config: RBACTomlConfig = toml::from_str(content)?;
        self.convert_toml_to_rbac_config(toml_config)
    }

    /// Convert TOML configuration to RBAC configuration
    fn convert_toml_to_rbac_config(&self, toml_config: RBACTomlConfig) -> Result<RBACConfig> {
        let rbac_settings = toml_config.rbac;

        let mut rbac_config = RBACConfig::default();

        // Set cache TTL
        if let Some(ttl) = rbac_settings.cache_ttl_seconds {
            rbac_config.cache_ttl_seconds = ttl;
        }

        // Convert default permissions
        if let Some(default_perms) = rbac_settings.default_permissions {
            rbac_config.default_permissions = default_perms
                .into_iter()
                .map(|(resource_type, permissions)| {
                    let converted_permissions = permissions
                        .into_iter()
                        .map(|p| self.parse_permission(&p))
                        .collect();
                    (resource_type, converted_permissions)
                })
                .collect();
        }

        // Set category hierarchies
        if let Some(hierarchies) = rbac_settings.category_hierarchies {
            rbac_config.category_hierarchies = hierarchies;
        }

        // Set tag hierarchies
        if let Some(hierarchies) = rbac_settings.tag_hierarchies {
            rbac_config.tag_hierarchies = hierarchies;
        }

        // Convert access rules
        if let Some(rules) = rbac_settings.rules {
            rbac_config.rules = rules
                .into_iter()
                .map(|rule| self.convert_access_rule_toml(rule))
                .collect::<Result<Vec<_>>>()?;
        }

        Ok(rbac_config)
    }

    /// Convert TOML access rule to AccessRule
    fn convert_access_rule_toml(&self, rule: AccessRuleToml) -> Result<AccessRule> {
        let resource_type = self.parse_resource_type(&rule.resource_type)?;

        let allowed_roles = rule
            .allowed_roles
            .unwrap_or_default()
            .into_iter()
            .map(|r| self.parse_role(&r))
            .collect();

        let allowed_permissions = rule
            .allowed_permissions
            .unwrap_or_default()
            .into_iter()
            .map(|p| self.parse_permission(&p))
            .collect();

        Ok(AccessRule {
            id: rule.id,
            resource_type,
            resource_name: rule.resource_name,
            allowed_roles,
            allowed_permissions,
            required_categories: rule.required_categories.unwrap_or_default(),
            required_tags: rule.required_tags.unwrap_or_default(),
            deny_categories: rule.deny_categories.unwrap_or_default(),
            deny_tags: rule.deny_tags.unwrap_or_default(),
            is_active: rule.is_active.unwrap_or(true),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
        })
    }

    /// Parse resource type from string
    fn parse_resource_type(&self, resource_type: &str) -> Result<ResourceType> {
        match resource_type.to_lowercase().as_str() {
            "database" => Ok(ResourceType::Database),
            "file" => Ok(ResourceType::File),
            "directory" => Ok(ResourceType::Directory),
            "content" => Ok(ResourceType::Content),
            "api" => Ok(ResourceType::Api),
            custom => Ok(ResourceType::Custom(custom.to_string())),
        }
    }

    /// Parse role from string
    fn parse_role(&self, role: &str) -> Role {
        match role.to_lowercase().as_str() {
            "admin" => Role::Admin,
            "moderator" => Role::Moderator,
            "user" => Role::User,
            "guest" => Role::Guest,
            custom => Role::Custom(custom.to_string()),
        }
    }

    /// Parse permission from string
    fn parse_permission(&self, permission: &str) -> Permission {
        match permission.to_lowercase().as_str() {
            "read_users" => Permission::ReadUsers,
            "write_users" => Permission::WriteUsers,
            "delete_users" => Permission::DeleteUsers,
            "read_content" => Permission::ReadContent,
            "write_content" => Permission::WriteContent,
            "delete_content" => Permission::DeleteContent,
            "manage_roles" => Permission::ManageRoles,
            "manage_system" => Permission::ManageSystem,
            _ => {
                // Handle scoped permissions
                if permission.contains(':') {
                    let parts: Vec<&str> = permission.splitn(2, ':').collect();
                    if parts.len() == 2 {
                        let prefix = parts[0].to_lowercase();
                        let scope = parts[1].to_string();

                        return match prefix.as_str() {
                            "read_database" => Permission::ReadDatabase(scope),
                            "write_database" => Permission::WriteDatabase(scope),
                            "delete_database" => Permission::DeleteDatabase(scope),
                            "read_file" => Permission::ReadFile(scope),
                            "write_file" => Permission::WriteFile(scope),
                            "delete_file" => Permission::DeleteFile(scope),
                            "access_category" => Permission::AccessCategory(scope),
                            "access_tag" => Permission::AccessTag(scope),
                            _ => Permission::Custom(permission.to_string()),
                        };
                    }
                }
                Permission::Custom(permission.to_string())
            }
        }
    }

    /// Save RBAC configuration to TOML file
    pub async fn save_to_file(&self, config: &RBACConfig) -> Result<()> {
        let toml_config = self.convert_rbac_to_toml_config(config)?;
        let content = toml::to_string_pretty(&toml_config)?;
        async_fs::write(&self.config_path, content).await?;
        Ok(())
    }

    /// Save RBAC configuration synchronously
    pub fn save_to_file_sync(&self, config: &RBACConfig) -> Result<()> {
        let toml_config = self.convert_rbac_to_toml_config(config)?;
        let content = toml::to_string_pretty(&toml_config)?;
        fs::write(&self.config_path, content)?;
        Ok(())
    }

    /// Convert RBAC configuration to TOML configuration
    fn convert_rbac_to_toml_config(&self, config: &RBACConfig) -> Result<RBACTomlConfig> {
        let default_permissions = config
            .default_permissions
            .iter()
            .map(|(resource_type, permissions)| {
                let permission_strings = permissions
                    .iter()
                    .map(|p| self.permission_to_string(p))
                    .collect();
                (resource_type.clone(), permission_strings)
            })
            .collect();

        let rules = config
            .rules
            .iter()
            .map(|rule| self.convert_access_rule_to_toml(rule))
            .collect();

        let rbac_settings = RBACSettings {
            cache_ttl_seconds: Some(config.cache_ttl_seconds),
            default_permissions: Some(default_permissions),
            category_hierarchies: Some(config.category_hierarchies.clone()),
            tag_hierarchies: Some(config.tag_hierarchies.clone()),
            rules: Some(rules),
        };

        Ok(RBACTomlConfig {
            rbac: rbac_settings,
        })
    }

    /// Convert AccessRule to TOML format
    fn convert_access_rule_to_toml(&self, rule: &AccessRule) -> AccessRuleToml {
        let allowed_roles = rule
            .allowed_roles
            .iter()
            .map(|r| self.role_to_string(r))
            .collect();

        let allowed_permissions = rule
            .allowed_permissions
            .iter()
            .map(|p| self.permission_to_string(p))
            .collect();

        AccessRuleToml {
            id: rule.id.clone(),
            name: None,
            description: None,
            resource_type: self.resource_type_to_string(&rule.resource_type),
            resource_name: rule.resource_name.clone(),
            action: None,
            priority: None,
            allowed_roles: Some(allowed_roles),
            allowed_permissions: Some(allowed_permissions),
            required_categories: Some(rule.required_categories.clone()),
            required_tags: Some(rule.required_tags.clone()),
            deny_categories: Some(rule.deny_categories.clone()),
            deny_tags: Some(rule.deny_tags.clone()),
            is_active: Some(rule.is_active),
        }
    }

    /// Convert ResourceType to string
    fn resource_type_to_string(&self, resource_type: &ResourceType) -> String {
        match resource_type {
            ResourceType::Database => "database".to_string(),
            ResourceType::File => "file".to_string(),
            ResourceType::Directory => "directory".to_string(),
            ResourceType::Content => "content".to_string(),
            ResourceType::Api => "api".to_string(),
            ResourceType::Custom(name) => name.clone(),
        }
    }

    /// Convert Role to string
    fn role_to_string(&self, role: &Role) -> String {
        match role {
            Role::Admin => "admin".to_string(),
            Role::Moderator => "moderator".to_string(),
            Role::User => "user".to_string(),
            Role::Guest => "guest".to_string(),
            Role::Custom(name) => name.clone(),
        }
    }

    /// Convert Permission to string
    fn permission_to_string(&self, permission: &Permission) -> String {
        match permission {
            Permission::ReadUsers => "read_users".to_string(),
            Permission::WriteUsers => "write_users".to_string(),
            Permission::DeleteUsers => "delete_users".to_string(),
            Permission::ReadContent => "read_content".to_string(),
            Permission::WriteContent => "write_content".to_string(),
            Permission::DeleteContent => "delete_content".to_string(),
            Permission::ManageRoles => "manage_roles".to_string(),
            Permission::ManageSystem => "manage_system".to_string(),
            Permission::ReadDatabase(scope) => format!("read_database:{}", scope),
            Permission::WriteDatabase(scope) => format!("write_database:{}", scope),
            Permission::DeleteDatabase(scope) => format!("delete_database:{}", scope),
            Permission::ReadFile(scope) => format!("read_file:{}", scope),
            Permission::WriteFile(scope) => format!("write_file:{}", scope),
            Permission::DeleteFile(scope) => format!("delete_file:{}", scope),
            Permission::AccessCategory(scope) => format!("access_category:{}", scope),
            Permission::AccessTag(scope) => format!("access_tag:{}", scope),
            Permission::Custom(name) => name.clone(),
        }
    }

    /// Check if configuration file exists
    pub fn config_exists(&self) -> bool {
        Path::new(&self.config_path).exists()
    }

    /// Create default configuration file
    pub async fn create_default_config(&self) -> Result<()> {
        let default_config = self.create_default_rbac_config();
        self.save_to_file(&default_config).await
    }

    /// Create default RBAC configuration
    fn create_default_rbac_config(&self) -> RBACConfig {
        let mut default_permissions = HashMap::new();
        default_permissions.insert("Database".to_string(), vec![Permission::ReadContent]);
        default_permissions.insert(
            "File".to_string(),
            vec![Permission::ReadFile("public/*".to_string())],
        );
        default_permissions.insert("Content".to_string(), vec![Permission::ReadContent]);

        let mut category_hierarchies = HashMap::new();
        category_hierarchies.insert(
            "admin".to_string(),
            vec!["editor".to_string(), "viewer".to_string()],
        );
        category_hierarchies.insert("editor".to_string(), vec!["viewer".to_string()]);

        let mut tag_hierarchies = HashMap::new();
        tag_hierarchies.insert("public".to_string(), vec!["internal".to_string()]);
        tag_hierarchies.insert("internal".to_string(), vec!["confidential".to_string()]);

        let rules = vec![
            AccessRule {
                id: "admin_full_access".to_string(),
                resource_type: ResourceType::Database,
                resource_name: "*".to_string(),
                allowed_roles: vec![Role::Admin],
                allowed_permissions: vec![],
                required_categories: vec![],
                required_tags: vec![],
                deny_categories: vec![],
                deny_tags: vec![],
                is_active: true,
                created_at: chrono::Utc::now(),
                updated_at: chrono::Utc::now(),
            },
            AccessRule {
                id: "editor_content_access".to_string(),
                resource_type: ResourceType::Content,
                resource_name: "*".to_string(),
                allowed_roles: vec![Role::Custom("editor".to_string())],
                allowed_permissions: vec![Permission::WriteContent],
                required_categories: vec!["editor".to_string()],
                required_tags: vec![],
                deny_categories: vec![],
                deny_tags: vec!["restricted".to_string()],
                is_active: true,
                created_at: chrono::Utc::now(),
                updated_at: chrono::Utc::now(),
            },
            AccessRule {
                id: "user_public_files".to_string(),
                resource_type: ResourceType::File,
                resource_name: "public/*".to_string(),
                allowed_roles: vec![Role::User],
                allowed_permissions: vec![],
                required_categories: vec![],
                required_tags: vec!["public".to_string()],
                deny_categories: vec![],
                deny_tags: vec![],
                is_active: true,
                created_at: chrono::Utc::now(),
                updated_at: chrono::Utc::now(),
            },
        ];

        RBACConfig {
            rules,
            default_permissions,
            category_hierarchies,
            tag_hierarchies,
            cache_ttl_seconds: 300,
        }
    }
}

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

    #[test]
    fn test_parse_permission() {
        let loader = RBACConfigLoader::new("test.toml");

        assert_eq!(loader.parse_permission("read_users"), Permission::ReadUsers);
        assert_eq!(
            loader.parse_permission("read_database:test_db"),
            Permission::ReadDatabase("test_db".to_string())
        );
        assert_eq!(
            loader.parse_permission("custom_permission"),
            Permission::Custom("custom_permission".to_string())
        );
    }

    #[test]
    fn test_parse_role() {
        let loader = RBACConfigLoader::new("test.toml");

        assert_eq!(loader.parse_role("admin"), Role::Admin);
        assert_eq!(loader.parse_role("user"), Role::User);
        assert_eq!(
            loader.parse_role("custom"),
            Role::Custom("custom".to_string())
        );
    }

    #[test]
    fn test_parse_resource_type() {
        let loader = RBACConfigLoader::new("test.toml");

        assert_eq!(
            loader.parse_resource_type("database").unwrap(),
            ResourceType::Database
        );
        assert_eq!(
            loader.parse_resource_type("file").unwrap(),
            ResourceType::File
        );
        assert_eq!(
            loader.parse_resource_type("custom").unwrap(),
            ResourceType::Custom("custom".to_string())
        );
    }

    #[tokio::test]
    async fn test_load_and_save_config() {
        let temp_file = NamedTempFile::new().unwrap();
        let config_path = temp_file.path().to_str().unwrap();
        let loader = RBACConfigLoader::new(config_path);

        // Create and save default config
        let default_config = loader.create_default_rbac_config();
        loader.save_to_file(&default_config).await.unwrap();

        // Load config back
        let loaded_config = loader.load_from_file().await.unwrap();

        assert_eq!(loaded_config.cache_ttl_seconds, 300);
        assert!(!loaded_config.rules.is_empty());
        assert!(!loaded_config.default_permissions.is_empty());
    }

    #[test]
    fn test_toml_parsing() {
        let toml_content = r#"
[rbac]
cache_ttl_seconds = 600

[rbac.default_permissions]
Database = ["read_content"]
File = ["read_file:public/*"]

[rbac.category_hierarchies]
admin = ["editor", "viewer"]

[rbac.tag_hierarchies]
public = ["internal"]

[[rbac.rules]]
id = "test_rule"
resource_type = "database"
resource_name = "test_db"
allowed_roles = ["admin"]
allowed_permissions = ["read_database:test_db"]
required_categories = ["admin"]
is_active = true
"#;

        let loader = RBACConfigLoader::new("test.toml");
        let config = loader.load_from_toml_string(toml_content).unwrap();

        assert_eq!(config.cache_ttl_seconds, 600);
        assert_eq!(config.rules.len(), 1);
        assert_eq!(config.rules[0].id, "test_rule");
        assert_eq!(config.rules[0].resource_type, ResourceType::Database);
        assert_eq!(config.rules[0].resource_name, "test_db");
    }
}
