//! Permission Definitions
//!
//! Defines resources, actions, and permission mappings

use serde::{Deserialize, Serialize};
use super::Role;

/// Resources in the system
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Resource {
    Server,
    Taskserv,
    Cluster,
    Workspace,
    Extension,
    OCI,
    DNS,
    Service,
    Settings,
    User,
    Role,
    AuditLog,
    SshKey,
}

impl Resource {
    pub fn as_str(&self) -> &'static str {
        match self {
            Resource::Server => "server",
            Resource::Taskserv => "taskserv",
            Resource::Cluster => "cluster",
            Resource::Workspace => "workspace",
            Resource::Extension => "extension",
            Resource::OCI => "oci",
            Resource::DNS => "dns",
            Resource::Service => "service",
            Resource::Settings => "settings",
            Resource::User => "user",
            Resource::Role => "role",
            Resource::AuditLog => "audit_log",
            Resource::SshKey => "ssh_key",
        }
    }
}

/// Actions that can be performed on resources
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Action {
    Read,
    Create,
    Update,
    Delete,
    Deploy,
    Configure,
    Manage,
    Audit,
    Execute,
}

impl Action {
    pub fn as_str(&self) -> &'static str {
        match self {
            Action::Read => "read",
            Action::Create => "create",
            Action::Update => "update",
            Action::Delete => "delete",
            Action::Deploy => "deploy",
            Action::Configure => "configure",
            Action::Manage => "manage",
            Action::Audit => "audit",
            Action::Execute => "execute",
        }
    }
}

/// Permission combines resource and action
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Permission {
    pub resource: Resource,
    pub action: Action,
}

impl Permission {
    pub fn new(resource: Resource, action: Action) -> Self {
        Self { resource, action }
    }

    /// Get all possible permissions
    pub fn all_permissions() -> Vec<Permission> {
        let resources = [
            Resource::Server,
            Resource::Taskserv,
            Resource::Cluster,
            Resource::Workspace,
            Resource::Extension,
            Resource::OCI,
            Resource::DNS,
            Resource::Service,
            Resource::Settings,
            Resource::User,
            Resource::Role,
            Resource::AuditLog,
            Resource::SshKey,
        ];

        let actions = [
            Action::Read,
            Action::Create,
            Action::Update,
            Action::Delete,
            Action::Deploy,
            Action::Configure,
            Action::Manage,
            Action::Audit,
            Action::Execute,
        ];

        let mut permissions = Vec::new();
        for resource in resources {
            for action in actions {
                permissions.push(Permission::new(resource, action));
            }
        }

        permissions
    }

    /// Get permissions for a specific role
    pub fn for_role(role: Role) -> Vec<Permission> {
        match role {
            Role::Admin => Self::all_permissions(),

            Role::Operator => vec![
                // Full access to infrastructure
                Permission::new(Resource::Server, Action::Read),
                Permission::new(Resource::Server, Action::Create),
                Permission::new(Resource::Server, Action::Update),
                Permission::new(Resource::Server, Action::Delete),
                Permission::new(Resource::Server, Action::Deploy),
                Permission::new(Resource::Taskserv, Action::Read),
                Permission::new(Resource::Taskserv, Action::Create),
                Permission::new(Resource::Taskserv, Action::Update),
                Permission::new(Resource::Taskserv, Action::Delete),
                Permission::new(Resource::Taskserv, Action::Deploy),
                Permission::new(Resource::Cluster, Action::Read),
                Permission::new(Resource::Cluster, Action::Create),
                Permission::new(Resource::Cluster, Action::Update),
                Permission::new(Resource::Cluster, Action::Delete),
                Permission::new(Resource::Cluster, Action::Deploy),
                // Read workspaces and extensions
                Permission::new(Resource::Workspace, Action::Read),
                Permission::new(Resource::Extension, Action::Read),
                // Manage services
                Permission::new(Resource::Service, Action::Read),
                Permission::new(Resource::Service, Action::Manage),
                // Read SSH keys
                Permission::new(Resource::SshKey, Action::Read),
                Permission::new(Resource::SshKey, Action::Create),
            ],

            Role::Developer => vec![
                // Read infrastructure
                Permission::new(Resource::Server, Action::Read),
                Permission::new(Resource::Taskserv, Action::Read),
                Permission::new(Resource::Cluster, Action::Read),
                // Manage workspaces
                Permission::new(Resource::Workspace, Action::Read),
                Permission::new(Resource::Workspace, Action::Create),
                Permission::new(Resource::Workspace, Action::Update),
                // Deploy to development
                Permission::new(Resource::Taskserv, Action::Deploy),
                Permission::new(Resource::Cluster, Action::Deploy),
                // Read extensions
                Permission::new(Resource::Extension, Action::Read),
                // Read services
                Permission::new(Resource::Service, Action::Read),
                // Read SSH keys
                Permission::new(Resource::SshKey, Action::Read),
            ],

            Role::Viewer => vec![
                // Read-only access to everything
                Permission::new(Resource::Server, Action::Read),
                Permission::new(Resource::Taskserv, Action::Read),
                Permission::new(Resource::Cluster, Action::Read),
                Permission::new(Resource::Workspace, Action::Read),
                Permission::new(Resource::Extension, Action::Read),
                Permission::new(Resource::OCI, Action::Read),
                Permission::new(Resource::DNS, Action::Read),
                Permission::new(Resource::Service, Action::Read),
                Permission::new(Resource::Settings, Action::Read),
            ],

            Role::ServiceAccount => vec![
                // Automation-focused permissions
                Permission::new(Resource::Server, Action::Create),
                Permission::new(Resource::Server, Action::Deploy),
                Permission::new(Resource::Taskserv, Action::Create),
                Permission::new(Resource::Taskserv, Action::Deploy),
                Permission::new(Resource::Cluster, Action::Create),
                Permission::new(Resource::Cluster, Action::Deploy),
                Permission::new(Resource::Workspace, Action::Read),
                Permission::new(Resource::Extension, Action::Read),
                Permission::new(Resource::Service, Action::Execute),
                Permission::new(Resource::SshKey, Action::Read),
            ],

            Role::Auditor => vec![
                // Read-only plus audit access
                Permission::new(Resource::Server, Action::Read),
                Permission::new(Resource::Taskserv, Action::Read),
                Permission::new(Resource::Cluster, Action::Read),
                Permission::new(Resource::Workspace, Action::Read),
                Permission::new(Resource::Extension, Action::Read),
                Permission::new(Resource::Service, Action::Read),
                Permission::new(Resource::Settings, Action::Read),
                Permission::new(Resource::AuditLog, Action::Read),
                Permission::new(Resource::AuditLog, Action::Audit),
                Permission::new(Resource::User, Action::Read),
                Permission::new(Resource::Role, Action::Read),
                Permission::new(Resource::SshKey, Action::Audit),
            ],
        }
    }

    /// Check if a role has a specific permission
    pub fn role_has_permission(role: Role, resource: Resource, action: Action) -> bool {
        let permissions = Self::for_role(role);
        permissions.contains(&Permission::new(resource, action))
    }
}

impl std::fmt::Display for Permission {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}:{}", self.resource.as_str(), self.action.as_str())
    }
}

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

    #[test]
    fn test_admin_has_all_permissions() {
        let admin_perms = Permission::for_role(Role::Admin);
        let all_perms = Permission::all_permissions();

        assert_eq!(admin_perms.len(), all_perms.len());
    }

    #[test]
    fn test_viewer_read_only() {
        let viewer_perms = Permission::for_role(Role::Viewer);

        // All viewer permissions should be Read action
        for perm in viewer_perms {
            assert_eq!(perm.action, Action::Read);
        }
    }

    #[test]
    fn test_operator_can_deploy() {
        assert!(Permission::role_has_permission(
            Role::Operator,
            Resource::Server,
            Action::Deploy
        ));
    }

    #[test]
    fn test_developer_cannot_delete() {
        assert!(!Permission::role_has_permission(
            Role::Developer,
            Resource::Server,
            Action::Delete
        ));
    }
}
