use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

/// User authentication and profile information
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct User {
    pub id: Uuid,
    pub email: String,
    pub username: String,
    pub display_name: Option<String>,
    pub avatar_url: Option<String>,
    pub roles: Vec<Role>,
    pub is_active: bool,
    pub email_verified: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub last_login: Option<DateTime<Utc>>,
    pub profile: UserProfile,
    pub two_factor_enabled: bool,
}

/// Extended user profile information
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UserProfile {
    pub first_name: Option<String>,
    pub last_name: Option<String>,
    pub bio: Option<String>,
    pub timezone: Option<String>,
    pub locale: Option<String>,
    pub preferences: HashMap<String, String>,
    pub categories: Vec<String>,
    pub tags: Vec<String>,
}

/// User roles for RBAC
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Role {
    Admin,
    Moderator,
    User,
    Guest,
    Custom(String),
}

impl Role {
    pub fn permissions(&self) -> Vec<Permission> {
        match self {
            Role::Admin => vec![
                Permission::ReadUsers,
                Permission::WriteUsers,
                Permission::DeleteUsers,
                Permission::ReadContent,
                Permission::WriteContent,
                Permission::DeleteContent,
                Permission::ManageRoles,
                Permission::ManageSystem,
            ],
            Role::Moderator => vec![
                Permission::ReadUsers,
                Permission::ReadContent,
                Permission::WriteContent,
                Permission::DeleteContent,
            ],
            Role::User => vec![Permission::ReadContent, Permission::WriteContent],
            Role::Guest => vec![Permission::ReadContent],
            Role::Custom(_) => vec![], // Custom roles need to be defined in the database
        }
    }
}

/// Permissions for fine-grained access control
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Permission {
    ReadUsers,
    WriteUsers,
    DeleteUsers,
    ReadContent,
    WriteContent,
    DeleteContent,
    ManageRoles,
    ManageSystem,
    // Database access permissions
    ReadDatabase(String),
    WriteDatabase(String),
    DeleteDatabase(String),
    // File access permissions
    ReadFile(String),
    WriteFile(String),
    DeleteFile(String),
    // Category-based permissions
    AccessCategory(String),
    // Tag-based permissions
    AccessTag(String),
    Custom(String),
}

/// JWT token claims
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims {
    pub sub: String,      // Subject (user ID)
    pub email: String,    // User email
    pub roles: Vec<Role>, // User roles
    pub exp: usize,       // Expiration time
    pub iat: usize,       // Issued at
    pub iss: String,      // Issuer
}

/// Login credentials
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct LoginCredentials {
    pub email: String,
    pub password: String,
    pub remember_me: bool,
}

/// User registration data
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RegisterUserData {
    pub email: String,
    pub username: String,
    pub password: String,
    pub display_name: Option<String>,
    pub first_name: Option<String>,
    pub last_name: Option<String>,
}

/// OAuth2 provider information
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum OAuthProvider {
    Google,
    GitHub,
    Discord,
    Microsoft,
    Custom(String),
}

impl OAuthProvider {
    pub fn as_str(&self) -> &str {
        match self {
            OAuthProvider::Google => "google",
            OAuthProvider::GitHub => "github",
            OAuthProvider::Discord => "discord",
            OAuthProvider::Microsoft => "microsoft",
            OAuthProvider::Custom(name) => name,
        }
    }
}

/// OAuth2 user information from provider
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct OAuthUserInfo {
    pub provider: OAuthProvider,
    pub provider_id: String,
    pub email: String,
    pub username: Option<String>,
    pub display_name: Option<String>,
    pub avatar_url: Option<String>,
    pub raw_data: HashMap<String, serde_json::Value>,
}

/// Password reset request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PasswordResetRequest {
    pub email: String,
}

/// Password reset confirmation
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PasswordResetConfirm {
    pub token: String,
    pub new_password: String,
}

/// Email verification request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct EmailVerificationRequest {
    pub email: String,
}

/// Session information
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SessionInfo {
    pub id: String,
    pub user_id: Uuid,
    pub created_at: DateTime<Utc>,
    pub expires_at: DateTime<Utc>,
    pub last_accessed: DateTime<Utc>,
    pub ip_address: Option<String>,
    pub user_agent: Option<String>,
    pub is_active: bool,
}

/// Authentication response
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AuthResponse {
    pub user: User,
    pub access_token: String,
    pub refresh_token: Option<String>,
    pub expires_in: i64,
    pub token_type: String,
    pub requires_2fa: bool, // Indicates if 2FA is required for this login
}

/// Token refresh request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RefreshTokenRequest {
    pub refresh_token: String,
}

/// User update data
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UpdateUserData {
    pub display_name: Option<String>,
    pub first_name: Option<String>,
    pub last_name: Option<String>,
    pub bio: Option<String>,
    pub timezone: Option<String>,
    pub locale: Option<String>,
    pub preferences: Option<HashMap<String, String>>,
}

/// Password change request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ChangePasswordRequest {
    pub current_password: String,
    pub new_password: String,
}

/// 2FA setup request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Setup2FARequest {
    pub password: String, // Current password for verification
}

/// 2FA setup response
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Setup2FAResponse {
    pub secret: String,            // Base32 encoded secret
    pub qr_code_url: String,       // QR code data URL
    pub backup_codes: Vec<String>, // Recovery codes
}

/// 2FA verification request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Verify2FARequest {
    pub code: String, // 6-digit TOTP code or backup code
}

/// 2FA login request (after initial login)
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Login2FARequest {
    pub email: String,
    pub code: String, // 6-digit TOTP code or backup code
    pub remember_me: bool,
}

/// 2FA status response
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TwoFactorStatus {
    pub is_enabled: bool,
    pub backup_codes_remaining: u32,
    pub last_used: Option<DateTime<Utc>>,
}

/// 2FA disable request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Disable2FARequest {
    pub password: String,
    pub code: String, // TOTP code or backup code
}

/// Generate new backup codes request
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GenerateBackupCodesRequest {
    pub password: String,
    pub code: String, // TOTP code for verification
}

/// Backup codes response
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct BackupCodesResponse {
    pub codes: Vec<String>,
    pub generated_at: DateTime<Utc>,
}

/// API error types
#[derive(Debug, Serialize, Deserialize, Clone, thiserror::Error)]
pub enum AuthError {
    #[error("Invalid credentials")]
    InvalidCredentials,
    #[error("User not found")]
    UserNotFound,
    #[error("Email already exists")]
    EmailAlreadyExists,
    #[error("Username already exists")]
    UsernameAlreadyExists,
    #[error("Invalid token")]
    InvalidToken,
    #[error("Token expired")]
    TokenExpired,
    #[error("Insufficient permissions")]
    InsufficientPermissions,
    #[error("Account not verified")]
    AccountNotVerified,
    #[error("Account suspended")]
    AccountSuspended,
    #[error("Rate limit exceeded")]
    RateLimitExceeded,
    #[error("OAuth error: {0}")]
    OAuthError(String),
    #[error("Database error")]
    DatabaseError,
    #[error("Internal server error")]
    InternalError,
    #[error("Validation error: {0}")]
    ValidationError(String),
    #[error("Two-factor authentication required")]
    TwoFactorRequired,
    #[error("Invalid 2FA code")]
    Invalid2FACode,
    #[error("2FA already enabled")]
    TwoFactorAlreadyEnabled,
    #[error("2FA not enabled")]
    TwoFactorNotEnabled,
    #[error("Invalid backup code")]
    InvalidBackupCode,
    #[error("2FA setup required")]
    TwoFactorSetupRequired,
    #[error("Too many 2FA attempts")]
    TooMany2FAAttempts,
}

impl Default for UserProfile {
    fn default() -> Self {
        Self {
            first_name: None,
            last_name: None,
            bio: None,
            timezone: None,
            locale: None,
            preferences: HashMap::new(),
            categories: Vec::new(),
            tags: Vec::new(),
        }
    }
}

impl User {
    /// Check if user has a specific role
    pub fn has_role(&self, role: &Role) -> bool {
        self.roles.contains(role)
    }

    /// Check if user has a specific permission
    pub fn has_permission(&self, permission: &Permission) -> bool {
        self.roles
            .iter()
            .any(|role| role.permissions().contains(permission))
    }

    /// Get all permissions for this user
    pub fn get_permissions(&self) -> Vec<Permission> {
        self.roles
            .iter()
            .flat_map(|role| role.permissions())
            .collect()
    }

    /// Check if user is admin
    pub fn is_admin(&self) -> bool {
        self.has_role(&Role::Admin)
    }

    /// Check if user is moderator or admin
    pub fn is_moderator_or_admin(&self) -> bool {
        self.has_role(&Role::Admin) || self.has_role(&Role::Moderator)
    }

    /// Get display name or fallback to username
    pub fn display_name_or_username(&self) -> &str {
        self.display_name.as_ref().unwrap_or(&self.username)
    }
}

/// Helper trait for authorization checks
pub trait HasPermissions {
    fn has_permission(&self, permission: &Permission) -> bool;
    fn has_role(&self, role: &Role) -> bool;
    fn is_admin(&self) -> bool;
}

impl HasPermissions for User {
    fn has_permission(&self, permission: &Permission) -> bool {
        self.has_permission(permission)
    }

    fn has_role(&self, role: &Role) -> bool {
        self.has_role(role)
    }

    fn is_admin(&self) -> bool {
        self.is_admin()
    }
}

impl HasPermissions for Option<User> {
    fn has_permission(&self, permission: &Permission) -> bool {
        self.as_ref()
            .map_or(false, |user| user.has_permission(permission))
    }

    fn has_role(&self, role: &Role) -> bool {
        self.as_ref().map_or(false, |user| user.has_role(role))
    }

    fn is_admin(&self) -> bool {
        self.as_ref().map_or(false, |user| user.is_admin())
    }
}

/// Resource access rule for RBAC
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AccessRule {
    pub id: String,
    pub resource_type: ResourceType,
    pub resource_name: String,
    pub allowed_roles: Vec<Role>,
    pub allowed_permissions: Vec<Permission>,
    pub required_categories: Vec<String>,
    pub required_tags: Vec<String>,
    pub deny_categories: Vec<String>,
    pub deny_tags: Vec<String>,
    pub is_active: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

/// Resource types that can be protected
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ResourceType {
    Database,
    File,
    Directory,
    Content,
    Api,
    Custom(String),
}

/// Access control context for evaluating permissions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessContext {
    pub user: Option<User>,
    pub resource_type: ResourceType,
    pub resource_name: String,
    pub action: String,
    pub additional_context: HashMap<String, String>,
}

/// Permission evaluation result
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum AccessResult {
    Allow,
    Deny,
    RequireAdditionalAuth,
}

/// RBAC configuration that can be loaded from TOML
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RBACConfig {
    pub rules: Vec<AccessRule>,
    pub default_permissions: HashMap<String, Vec<Permission>>,
    pub category_hierarchies: HashMap<String, Vec<String>>,
    pub tag_hierarchies: HashMap<String, Vec<String>>,
    pub cache_ttl_seconds: u64,
}

impl User {
    /// Check if user has access to a specific category
    pub fn has_category(&self, category: &str) -> bool {
        self.profile.categories.contains(&category.to_string())
    }

    /// Check if user has access to a specific tag
    pub fn has_tag(&self, tag: &str) -> bool {
        self.profile.tags.contains(&tag.to_string())
    }

    /// Check if user has any of the required categories
    pub fn has_any_category(&self, categories: &[String]) -> bool {
        categories.iter().any(|cat| self.has_category(cat))
    }

    /// Check if user has any of the required tags
    pub fn has_any_tag(&self, tags: &[String]) -> bool {
        tags.iter().any(|tag| self.has_tag(tag))
    }

    /// Check if user has all required categories
    pub fn has_all_categories(&self, categories: &[String]) -> bool {
        categories.iter().all(|cat| self.has_category(cat))
    }

    /// Check if user has all required tags
    pub fn has_all_tags(&self, tags: &[String]) -> bool {
        tags.iter().all(|tag| self.has_tag(tag))
    }

    /// Check if user is denied by any category
    pub fn is_denied_by_categories(&self, deny_categories: &[String]) -> bool {
        deny_categories.iter().any(|cat| self.has_category(cat))
    }

    /// Check if user is denied by any tag
    pub fn is_denied_by_tags(&self, deny_tags: &[String]) -> bool {
        deny_tags.iter().any(|tag| self.has_tag(tag))
    }
}

impl AccessRule {
    /// Evaluate if a user has access based on this rule
    pub fn evaluate(&self, context: &AccessContext) -> AccessResult {
        if !self.is_active {
            return AccessResult::Deny;
        }

        // Check resource type and name match
        if self.resource_type != context.resource_type {
            return AccessResult::Deny;
        }

        // Check if resource name matches (supports wildcards)
        if !self.matches_resource_name(&context.resource_name) {
            return AccessResult::Deny;
        }

        let Some(user) = &context.user else {
            return AccessResult::Deny;
        };

        // Check deny conditions first
        if user.is_denied_by_categories(&self.deny_categories)
            || user.is_denied_by_tags(&self.deny_tags)
        {
            return AccessResult::Deny;
        }

        // Check role requirements
        if !self.allowed_roles.is_empty()
            && !self.allowed_roles.iter().any(|role| user.has_role(role))
        {
            return AccessResult::Deny;
        }

        // Check permission requirements
        if !self.allowed_permissions.is_empty()
            && !self
                .allowed_permissions
                .iter()
                .any(|perm| user.has_permission(perm))
        {
            return AccessResult::Deny;
        }

        // Check category requirements
        if !self.required_categories.is_empty() && !user.has_any_category(&self.required_categories)
        {
            return AccessResult::Deny;
        }

        // Check tag requirements
        if !self.required_tags.is_empty() && !user.has_any_tag(&self.required_tags) {
            return AccessResult::Deny;
        }

        AccessResult::Allow
    }

    fn matches_resource_name(&self, resource_name: &str) -> bool {
        // Simple wildcard matching - can be enhanced with regex
        if self.resource_name == "*" {
            return true;
        }

        if self.resource_name.ends_with("*") {
            let prefix = &self.resource_name[..self.resource_name.len() - 1];
            return resource_name.starts_with(prefix);
        }

        self.resource_name == resource_name
    }
}

impl Default for RBACConfig {
    fn default() -> Self {
        Self {
            rules: Vec::new(),
            default_permissions: HashMap::new(),
            category_hierarchies: HashMap::new(),
            tag_hierarchies: HashMap::new(),
            cache_ttl_seconds: 300, // 5 minutes
        }
    }
}
