use bcrypt::{hash, verify, DEFAULT_COST};
use chrono::{Duration, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    pub user_id: String,
    pub username: String,
    pub password_hash: String,
    pub created_at: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
    pub token: String,
    pub user_id: String,
    pub username: String,
    pub created_at: String,
    pub expires_at: String,
    pub groups: Vec<String>,
    pub permissions: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoginRequest {
    pub username: String,
    pub password: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoginResponse {
    pub token: String,
    pub expires_at: String,
    pub user_id: String,
    pub groups: Vec<String>,
    pub permissions: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdminLoginRequest {
    pub username: String,
    pub password: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdminSession {
    pub token: String,
    pub username: String,
    pub created_at: String,
    pub expires_at: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdminLoginResponse {
    pub token: String,
    pub expires_at: String,
    pub username: String,
}

#[derive(Clone)]
pub struct AuthState {
    pub sessions: Arc<Mutex<HashMap<String, Session>>>,
    pub users: Arc<Mutex<HashMap<String, User>>>,
    pub auth_db: Option<crate::sync::AuthDb>,
    pub admin_sessions: Arc<Mutex<HashMap<String, AdminSession>>>,
}

impl AuthState {
    pub fn new() -> Self {
        let mut users = HashMap::new();
        
        // Create default demo user
        let password_hash = hash("demo123", DEFAULT_COST).unwrap();
        users.insert(
            "demo".to_string(),
            User {
                user_id: "demo".to_string(),
                username: "demo".to_string(),
                password_hash,
                created_at: Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string(),
            },
        );
        
        AuthState {
            sessions: Arc::new(Mutex::new(HashMap::new())),
            users: Arc::new(Mutex::new(users)),
            auth_db: None,
            admin_sessions: Arc::new(Mutex::new(HashMap::new())),
        }
    }
    
    pub fn with_sync(auth_db_path: &str) -> Self {
        let auth_db = crate::sync::AuthDb::new(auth_db_path).ok();
        
        AuthState {
            sessions: Arc::new(Mutex::new(HashMap::new())),
            users: Arc::new(Mutex::new(HashMap::new())),
            auth_db,
            admin_sessions: Arc::new(Mutex::new(HashMap::new())),
        }
    }
    
    pub fn login(&self, username: &str, password: &str) -> Option<LoginResponse> {
        let users = self.users.lock().unwrap();
        let user = users.get(username)?;
        
        if verify(password, &user.password_hash).unwrap_or(false) {
            let token = Uuid::new_v4().to_string();
            let now = Utc::now();
            let expires_at = now + Duration::hours(24);
            
            let session = Session {
                token: token.clone(),
                user_id: user.user_id.clone(),
                username: user.username.clone(),
                created_at: now.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                expires_at: expires_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                groups: vec![],
                permissions: "{}".to_string(),
            };
            
            let mut sessions = self.sessions.lock().unwrap();
            sessions.insert(token.clone(), session);
            
            Some(LoginResponse {
                token,
                expires_at: expires_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                user_id: user.user_id.clone(),
                groups: vec![],
                permissions: "{}".to_string(),
            })
        } else {
None
        }
    }
    
    pub fn admin_login(&self, username: &str, password: &str) -> Option<AdminLoginResponse> {
        if let Some(auth_db) = &self.auth_db {
            match auth_db.get_admin(username) {
                Ok(Some(admin)) if admin.status == 1 => {
                    if verify(password, &admin.password_hash).unwrap_or(false) {
                        let token = Uuid::new_v4().to_string();
                        let now = Utc::now();
                        let expires_at = now + Duration::hours(24);
                        
                        let session = AdminSession {
                            token: token.clone(),
                            username: username.to_string(),
                            created_at: now.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                            expires_at: expires_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                        };
                        
                        let mut admin_sessions = self.admin_sessions.lock().unwrap();
                        admin_sessions.insert(token.clone(), session);
                        
                        log::info!("Admin {} logged in successfully", username);
                        
                        Some(AdminLoginResponse {
                            token,
                            expires_at: expires_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                            username: username.to_string(),
                        })
                    } else {
                        log::warn!("Invalid password for admin {}", username);
                        None
                    }
                }
                Ok(Some(_)) => {
                    log::warn!("Admin {} is not active", username);
                    None
                }
                Ok(None) => {
                    log::warn!("Admin {} not found", username);
                    None
                }
                Err(e) => {
                    log::error!("Failed to get admin {}: {}", username, e);
                    None
                }
            }
        } else {
            log::warn!("Auth DB not available for admin login");
            None
        }
    }
    
    pub fn verify_admin_token(&self, token: &str) -> Option<AdminSession> {
        let admin_sessions = self.admin_sessions.lock().unwrap();
        
        if let Some(session) = admin_sessions.get(token) {
            let expires_at = chrono::DateTime::parse_from_rfc3339(&session.expires_at)
                .ok()
                .map(|dt| dt.with_timezone(&Utc));
            
            if let Some(exp) = expires_at {
                if Utc::now() < exp {
                    return Some(session.clone());
                } else {
                    log::warn!("Admin token {} has expired", token);
                }
            }
        }
        
        None
    }
    
    pub fn login_with_sync(&self, username: &str, password: &str) -> Option<LoginResponse> {
        if let Some(auth_db) = &self.auth_db {
            // Get user from auth.sqlite
            let user = match auth_db.get_user(username) {
                Ok(Some(user)) => user,
                Ok(None) => {
                    log::warn!("User {} not found in auth database", username);
                    return None;
                }
                Err(e) => {
                    log::error!("Failed to get user {}: {}", username, e);
                    return None;
                }
            };
            
            if user.status != 1 {
                log::warn!("User {} is disabled", username);
                return None;
            }
            
            if verify(password, &user.password_hash).unwrap_or(false) {
                let groups = auth_db.get_user_groups(username).unwrap_or_default();
                let permissions = user.permissions.clone();
                
                let token = Uuid::new_v4().to_string();
                let now = Utc::now();
                let expires_at = now + Duration::hours(24);
                
                let session = Session {
                    token: token.clone(),
                    user_id: username.to_string(),
                    username: username.to_string(),
                    created_at: now.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                    expires_at: expires_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                    groups: groups.clone(),
                    permissions: permissions.clone(),
                };
                
                let mut sessions = self.sessions.lock().unwrap();
                sessions.insert(token.clone(), session);
                
                log::info!("User {} logged in successfully", username);
                
                Some(LoginResponse {
                    token,
                    expires_at: expires_at.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
                    user_id: username.to_string(),
                    groups,
                    permissions,
                })
            } else {
                log::warn!("Invalid password for user {}", username);
                None
            }
        } else {
            self.login(username, password)
        }
    }
    
    pub fn verify_token(&self, token: &str) -> Option<Session> {
        let sessions = self.sessions.lock().unwrap();
        let session = sessions.get(token)?;
        
        // Check expiration
        let expires_at = chrono::DateTime::parse_from_rfc3339(&session.expires_at)
            .ok()?
            .with_timezone(&Utc);
        
        if Utc::now() > expires_at {
            return None;
        }
        
        Some(session.clone())
    }
    
    pub fn logout(&self, token: &str) -> bool {
        let mut sessions = self.sessions.lock().unwrap();
        sessions.remove(token).is_some()
    }
    
    pub fn create_user(&self, username: &str, password: &str) -> Result<String, String> {
        let mut users = self.users.lock().unwrap();
        
        if users.contains_key(username) {
            return Err("User already exists".to_string());
        }
        
        let password_hash = hash(password, DEFAULT_COST)
            .map_err(|e| e.to_string())?;
        
        let user_id = Uuid::new_v4().to_string();
        let user = User {
            user_id: user_id.clone(),
            username: username.to_string(),
            password_hash,
            created_at: Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string(),
        };
        
        users.insert(username.to_string(), user);
        Ok(user_id)
    }
}

// Authorization header parser
pub fn parse_auth_header(header: &str) -> Option<String> {
    if header.starts_with("Bearer ") {
        Some(header.trim_start_matches("Bearer ").to_string())
    } else {
        None
    }
}