<?php

/**
 * Delegated User Management Library
 * Handles creation, authentication, and management of delegated users
 */

class DelegatedUserManager {
    
    private $mysql_mgmt;
    private $permissions_db;
    
    public function __construct() {
        // Initialize MySQL management library for database access
        require_once('/docker/whp/web/libs/mysqlmgmt.php');
        $this->mysql_mgmt = new mysqlmgmt();
        
        // Initialize permissions database for logging
        $this->permissions_db = new SQLite3('/docker/whp/sql/permissions.db');
        $this->permissions_db->busyTimeout(6000);
    }
    
    /**
     * Get MySQL database connection with whp database selected
     */
    private function getConnection() {
        $db = $this->mysql_mgmt->getMySQLConnection();
        if ($db) {
            $db->exec("USE whp");
        }
        return $db;
    }
    
    /**
     * Create a new delegated user
     */
    public function createDelegatedUser($data, $created_by) {
        try {
            // Validate input
            $validation = $this->validateDelegatedUserData($data);
            if (!$validation['valid']) {
                return ['success' => false, 'message' => $validation['message']];
            }
            
            // Check if username already exists (both system and delegated users)
            if ($this->usernameExists($data['username'])) {
                return ['success' => false, 'message' => 'Username already exists'];
            }
            
            // Verify the parent user exists and the creator has permission
            if (!$this->canCreateDelegatedUser($data['parent_username'], $created_by)) {
                return ['success' => false, 'message' => 'Not authorized to create delegated users for this account'];
            }
            
            // Hash the password securely
            $password_hash = password_hash($data['password'], PASSWORD_ARGON2ID, [
                'memory_cost' => 65536,
                'time_cost' => 4,
                'threads' => 3
            ]);
            
            // Get database connection
            $db = $this->getConnection();
            if (!$db) {
                return ['success' => false, 'message' => 'Database connection failed'];
            }
            
            // Prepare the insert statement
            $stmt = $db->prepare("
                INSERT INTO delegated_users (
                    username, password_hash, parent_username, display_name, email,
                    can_manage_domains, can_manage_sites, can_manage_databases, 
                    can_manage_email, can_manage_files, can_manage_backups,
                    can_view_terminal, can_manage_wordpress, created_by
                ) VALUES (
                    :username, :password_hash, :parent_username, :display_name, :email,
                    :can_manage_domains, :can_manage_sites, :can_manage_databases,
                    :can_manage_email, :can_manage_files, :can_manage_backups,
                    :can_view_terminal, :can_manage_wordpress, :created_by
                )
            ");
            
            // Execute the statement
            $stmt->execute([
                ':username' => $data['username'],
                ':password_hash' => $password_hash,
                ':parent_username' => $data['parent_username'],
                ':display_name' => $data['display_name'] ?? null,
                ':email' => $data['email'] ?? null,
                ':can_manage_domains' => ($data['can_manage_domains'] ?? false) ? 1 : 0,
                ':can_manage_sites' => ($data['can_manage_sites'] ?? false) ? 1 : 0,
                ':can_manage_databases' => ($data['can_manage_databases'] ?? false) ? 1 : 0,
                ':can_manage_email' => ($data['can_manage_email'] ?? false) ? 1 : 0,
                ':can_manage_files' => ($data['can_manage_files'] ?? false) ? 1 : 0,
                ':can_manage_backups' => ($data['can_manage_backups'] ?? false) ? 1 : 0,
                ':can_view_terminal' => ($data['can_view_terminal'] ?? false) ? 1 : 0,
                ':can_manage_wordpress' => ($data['can_manage_wordpress'] ?? false) ? 1 : 0,
                ':created_by' => $created_by
            ]);
            
            $delegated_user_id = $db->lastInsertId();
            
            // Log the creation
            $this->logActivity($delegated_user_id, 'user_created', 'delegated_user', $delegated_user_id, 
                json_encode(['created_by' => $created_by]), $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'] ?? '', 'success');
            
            return ['success' => true, 'message' => 'Delegated user created successfully', 'user_id' => $delegated_user_id];
            
        } catch (Exception $e) {
            error_log("Error creating delegated user: " . $e->getMessage());
            return ['success' => false, 'message' => 'Database error occurred'];
        }
    }
    
    /**
     * Authenticate a delegated user
     */
    public function authenticateDelegatedUser($username, $password, $ip_address, $user_agent = '') {
        try {
            // Get database connection
            $db = $this->getConnection();
            if (!$db) {
                return ['success' => false, 'message' => 'Database connection failed'];
            }
            
            // Get user from database
            $stmt = $db->prepare("
                SELECT id, username, password_hash, parent_username, is_active, 
                       failed_login_attempts, locked_until 
                FROM delegated_users 
                WHERE username = :username
            ");
            $stmt->execute([':username' => $username]);
            $user = $stmt->fetch();
            
            if (!$user) {
                return ['success' => false, 'message' => 'Invalid credentials'];
            }
            
            // Check if account is active
            if (!$user['is_active']) {
                return ['success' => false, 'message' => 'Account is disabled'];
            }
            
            // Check if account is locked
            if ($user['locked_until'] && strtotime($user['locked_until']) > time()) {
                return ['success' => false, 'message' => 'Account is temporarily locked'];
            }
            
            // Verify password
            if (!password_verify($password, $user['password_hash'])) {
                // Increment failed login attempts
                $this->incrementFailedLogins($user['id']);
                $this->logActivity($user['id'], 'login_failed', 'authentication', null, 
                    json_encode(['reason' => 'invalid_password']), $ip_address, $user_agent, 'failure');
                return ['success' => false, 'message' => 'Invalid credentials'];
            }
            
            // Reset failed login attempts on successful login
            $this->resetFailedLogins($user['id']);
            
            // Create session
            $session_id = $this->createSession($user['id'], $ip_address, $user_agent);
            
            // Update last login
            $stmt = $db->prepare("UPDATE delegated_users SET last_login = NOW() WHERE id = :id");
            $stmt->execute([':id' => $user['id']]);
            
            // Log successful login
            $this->logActivity($user['id'], 'login_success', 'authentication', null, 
                json_encode(['session_id' => $session_id]), $ip_address, $user_agent, 'success');
            
            return [
                'success' => true, 
                'message' => 'Login successful',
                'session_id' => $session_id,
                'user' => [
                    'id' => $user['id'],
                    'username' => $user['username'],
                    'parent_username' => $user['parent_username']
                ]
            ];
            
        } catch (Exception $e) {
            error_log("Error authenticating delegated user: " . $e->getMessage());
            return ['success' => false, 'message' => 'Authentication error occurred'];
        }
    }
    
    /**
     * Get delegated user by session ID
     */
    public function getUserBySession($session_id) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                return false;
            }
            
            $stmt = $db->prepare("
                SELECT du.*, dus.ip_address, dus.last_activity
                FROM delegated_users du
                JOIN delegated_user_sessions dus ON du.id = dus.delegated_user_id
                WHERE dus.session_id = :session_id 
                AND dus.expires_at > NOW()
                AND du.is_active = TRUE
            ");
            $stmt->execute([':session_id' => $session_id]);
            return $stmt->fetch();
        } catch (Exception $e) {
            error_log("Error getting user by session: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Check if delegated user has permission for specific action
     */
    public function hasPermission($delegated_user_id, $permission, $resource_id = null) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                return false;
            }
            
            $stmt = $db->prepare("SELECT * FROM delegated_users WHERE id = :id AND is_active = TRUE");
            $stmt->execute([':id' => $delegated_user_id]);
            $user = $stmt->fetch();
            
            if (!$user) {
                return false;
            }
            
            // Check basic permission flags
            $permission_map = [
                'manage_domains' => $user['can_manage_domains'],
                'manage_sites' => $user['can_manage_sites'],
                'manage_databases' => $user['can_manage_databases'],
                'manage_email' => $user['can_manage_email'],
                'manage_files' => $user['can_manage_files'],
                'manage_backups' => $user['can_manage_backups'],
                'view_terminal' => $user['can_view_terminal'],
                'manage_wordpress' => $user['can_manage_wordpress']
            ];
            
            if (!isset($permission_map[$permission]) || !$permission_map[$permission]) {
                return false;
            }
            
            // Additional checks for domain-specific permissions
            if ($resource_id && $permission === 'manage_domains' && $user['allowed_domains']) {
                $allowed_domains = json_decode($user['allowed_domains'], true);
                if (is_array($allowed_domains) && !in_array($resource_id, $allowed_domains)) {
                    return false;
                }
            }
            
            return true;
            
        } catch (Exception $e) {
            error_log("Error checking permission: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Get delegated users for a parent user
     */
    public function getDelegatedUsers($parent_username, $include_inactive = false) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                return [];
            }
            
            $where_clause = "parent_username = :parent_username";
            if (!$include_inactive) {
                $where_clause .= " AND is_active = TRUE";
            }
            
            $stmt = $db->prepare("
                SELECT id, username, display_name, email, parent_username,
                       can_manage_domains, can_manage_sites, can_manage_databases,
                       can_manage_email, can_manage_files, can_manage_backups,
                       can_view_terminal, can_manage_wordpress,
                       is_active, last_login, created_at
                FROM delegated_users 
                WHERE {$where_clause}
                ORDER BY created_at DESC
            ");
            $stmt->execute([':parent_username' => $parent_username]);
            return $stmt->fetchAll();
        } catch (Exception $e) {
            error_log("Error getting delegated users: " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Get all delegated users across all parent users (root only)
     */
    public function getAllDelegatedUsers($include_inactive = false) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                return [];
            }
            
            $where_clause = "1=1"; // Always true base condition
            if (!$include_inactive) {
                $where_clause .= " AND is_active = TRUE";
            }
            
            $stmt = $db->prepare("
                SELECT id, username, display_name, email, parent_username,
                       can_manage_domains, can_manage_sites, can_manage_databases,
                       can_manage_email, can_manage_files, can_manage_backups,
                       can_view_terminal, can_manage_wordpress,
                       is_active, last_login, created_at
                FROM delegated_users 
                WHERE {$where_clause}
                ORDER BY parent_username, created_at DESC
            ");
            $stmt->execute();
            return $stmt->fetchAll();
        } catch (Exception $e) {
            error_log("Error getting all delegated users: " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Update delegated user permissions
     */
    public function updateDelegatedUser($user_id, $data, $updated_by) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                return ['success' => false, 'message' => 'Database connection failed'];
            }
            
            // Get current user to verify ownership
            $stmt = $db->prepare("SELECT parent_username FROM delegated_users WHERE id = :id");
            $stmt->execute([':id' => $user_id]);
            $user = $stmt->fetch();
            
            if (!$user) {
                return ['success' => false, 'message' => 'Delegated user not found'];
            }
            
            // Verify the updater has permission
            if (!$this->canManageDelegatedUser($user['parent_username'], $updated_by)) {
                return ['success' => false, 'message' => 'Not authorized to update this delegated user'];
            }
            
            // Build update query dynamically
            $allowed_fields = [
                'display_name', 'email', 'can_manage_domains', 'can_manage_sites',
                'can_manage_databases', 'can_manage_email', 'can_manage_files',
                'can_manage_backups', 'can_view_terminal', 'can_manage_wordpress',
                'is_active'
            ];
            
            $update_fields = [];
            $params = [':id' => $user_id];
            
            // Boolean fields that need conversion to integers
            $boolean_fields = [
                'can_manage_domains', 'can_manage_sites', 'can_manage_databases',
                'can_manage_email', 'can_manage_files', 'can_manage_backups',
                'can_view_terminal', 'can_manage_wordpress', 'is_active'
            ];
            
            foreach ($allowed_fields as $field) {
                if (array_key_exists($field, $data)) {
                    $update_fields[] = "$field = :$field";
                    if (in_array($field, $boolean_fields)) {
                        // Convert boolean to integer for MySQL TINYINT columns
                        $params[":$field"] = $data[$field] ? 1 : 0;
                    } else {
                        $params[":$field"] = $data[$field];
                    }
                }
            }
            
            if (empty($update_fields)) {
                return ['success' => false, 'message' => 'No valid fields to update'];
            }
            
            $sql = "UPDATE delegated_users SET " . implode(', ', $update_fields) . " WHERE id = :id";
            $stmt = $db->prepare($sql);
            $stmt->execute($params);
            
            // Log the update
            $this->logActivity($user_id, 'user_updated', 'delegated_user', $user_id,
                json_encode(['updated_by' => $updated_by, 'fields' => array_keys($data)]), 
                $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'] ?? '', 'success');
            
            return ['success' => true, 'message' => 'Delegated user updated successfully'];
            
        } catch (Exception $e) {
            error_log("Error updating delegated user: " . $e->getMessage());
            return ['success' => false, 'message' => 'Database error occurred'];
        }
    }
    
    /**
     * Delete a delegated user
     */
    public function deleteDelegatedUser($user_id, $deleted_by) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                return ['success' => false, 'message' => 'Database connection failed'];
            }
            
            // Get current user to verify ownership
            $stmt = $db->prepare("SELECT username, parent_username FROM delegated_users WHERE id = :id");
            $stmt->execute([':id' => $user_id]);
            $user = $stmt->fetch();
            
            if (!$user) {
                return ['success' => false, 'message' => 'Delegated user not found'];
            }
            
            // Verify the deleter has permission
            if (!$this->canManageDelegatedUser($user['parent_username'], $deleted_by)) {
                return ['success' => false, 'message' => 'Not authorized to delete this delegated user'];
            }
            
            // Delete the user (cascading will handle sessions and activity logs)
            $stmt = $db->prepare("DELETE FROM delegated_users WHERE id = :id");
            $stmt->execute([':id' => $user_id]);
            
            return ['success' => true, 'message' => 'Delegated user deleted successfully'];
            
        } catch (Exception $e) {
            error_log("Error deleting delegated user: " . $e->getMessage());
            return ['success' => false, 'message' => 'Database error occurred'];
        }
    }
    
    /**
     * Validate delegated user data
     */
    private function validateDelegatedUserData($data) {
        if (empty($data['username'])) {
            return ['valid' => false, 'message' => 'Username is required'];
        }
        
        if (empty($data['password'])) {
            return ['valid' => false, 'message' => 'Password is required'];
        }
        
        if (empty($data['parent_username'])) {
            return ['valid' => false, 'message' => 'Parent username is required'];
        }
        
        // Validate username format
        if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9]$/', $data['username'])) {
            return ['valid' => false, 'message' => 'Username must contain only letters, numbers, dots, underscores, and hyphens'];
        }
        
        if (strlen($data['username']) < 3 || strlen($data['username']) > 32) {
            return ['valid' => false, 'message' => 'Username must be between 3 and 32 characters'];
        }
        
        // Validate password strength
        if (strlen($data['password']) < 8) {
            return ['valid' => false, 'message' => 'Password must be at least 8 characters long'];
        }
        
        // Validate email if provided
        if (!empty($data['email']) && !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            return ['valid' => false, 'message' => 'Invalid email address'];
        }
        
        return ['valid' => true];
    }
    
    /**
     * Check if username exists (system or delegated)
     */
    private function usernameExists($username) {
        // Check system users (basic check using /etc/passwd if available)
        $system_user_exists = false;
        if (function_exists('posix_getpwnam')) {
            $system_user_exists = posix_getpwnam($username) !== false;
        } elseif (file_exists('/etc/passwd')) {
            // Fallback method: check /etc/passwd file
            $passwd_content = file_get_contents('/etc/passwd');
            $system_user_exists = strpos($passwd_content, $username . ':') !== false;
        }
        
        if ($system_user_exists) {
            return true;
        }
        
        // Check delegated users
        $db = $this->getConnection();
        if (!$db) {
            return true; // Assume exists on DB error for safety
        }
        
        $stmt = $db->prepare("SELECT COUNT(*) FROM delegated_users WHERE username = :username");
        $stmt->execute([':username' => $username]);
        return $stmt->fetchColumn() > 0;
    }
    
    /**
     * Check if user can create delegated users for parent
     */
    private function canCreateDelegatedUser($parent_username, $created_by) {
        // Root can create for anyone, users can create for themselves
        return $created_by === 'root' || $created_by === $parent_username;
    }
    
    /**
     * Check if user can manage delegated user
     */
    private function canManageDelegatedUser($parent_username, $manager) {
        // Root can manage anyone, users can manage their own delegated users
        return $manager === 'root' || $manager === $parent_username;
    }
    
    /**
     * Create a new session for delegated user
     */
    private function createSession($delegated_user_id, $ip_address, $user_agent) {
        $session_id = bin2hex(random_bytes(32));
        $expires_at = date('Y-m-d H:i:s', time() + (24 * 60 * 60)); // 24 hours
        
        $db = $this->getConnection();
        if (!$db) {
            return false;
        }
        
        $stmt = $db->prepare("
            INSERT INTO delegated_user_sessions (session_id, delegated_user_id, ip_address, user_agent, expires_at)
            VALUES (:session_id, :delegated_user_id, :ip_address, :user_agent, :expires_at)
        ");
        
        $stmt->execute([
            ':session_id' => $session_id,
            ':delegated_user_id' => $delegated_user_id,
            ':ip_address' => $ip_address,
            ':user_agent' => $user_agent,
            ':expires_at' => $expires_at
        ]);
        
        return $session_id;
    }
    
    /**
     * Increment failed login attempts and lock if necessary
     */
    private function incrementFailedLogins($user_id) {
        $db = $this->getConnection();
        if (!$db) {
            error_log("Database connection failed in incrementFailedLogins");
            return;
        }
        
        $stmt = $db->prepare("
            UPDATE delegated_users 
            SET failed_login_attempts = failed_login_attempts + 1,
                locked_until = CASE 
                    WHEN failed_login_attempts >= 4 THEN DATE_ADD(NOW(), INTERVAL 30 MINUTE)
                    ELSE locked_until 
                END
            WHERE id = :id
        ");
        $stmt->execute([':id' => $user_id]);
    }
    
    /**
     * Reset failed login attempts
     */
    private function resetFailedLogins($user_id) {
        $db = $this->getConnection();
        if (!$db) {
            error_log("Database connection failed in resetFailedLogins");
            return;
        }
        
        $stmt = $db->prepare("
            UPDATE delegated_users 
            SET failed_login_attempts = 0, locked_until = NULL 
            WHERE id = :id
        ");
        $stmt->execute([':id' => $user_id]);
    }
    
    /**
     * Log activity for audit trail
     */
    private function logActivity($delegated_user_id, $action, $resource_type, $resource_id, $details, $ip_address, $user_agent, $result) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                error_log("Database connection failed in logActivity");
                return;
            }
            
            $stmt = $db->prepare("
                INSERT INTO delegated_user_activity 
                (delegated_user_id, action, resource_type, resource_id, details, ip_address, user_agent, result)
                VALUES (:delegated_user_id, :action, :resource_type, :resource_id, :details, :ip_address, :user_agent, :result)
            ");
            
            $stmt->execute([
                ':delegated_user_id' => $delegated_user_id,
                ':action' => $action,
                ':resource_type' => $resource_type,
                ':resource_id' => $resource_id,
                ':details' => $details,
                ':ip_address' => $ip_address,
                ':user_agent' => $user_agent,
                ':result' => $result
            ]);
        } catch (Exception $e) {
            error_log("Error logging delegated user activity: " . $e->getMessage());
        }
    }
    
    /**
     * Cleanup expired sessions
     */
    public function cleanupExpiredSessions() {
        try {
            $db = $this->getConnection();
            if (!$db) {
                error_log("Database connection failed in cleanupExpiredSessions");
                return 0;
            }
            
            $stmt = $db->prepare("DELETE FROM delegated_user_sessions WHERE expires_at < NOW()");
            $stmt->execute();
            return $stmt->rowCount();
        } catch (Exception $e) {
            error_log("Error cleaning up expired sessions: " . $e->getMessage());
            return 0;
        }
    }
    
    /**
     * Get activity log for a delegated user
     */
    public function getActivityLog($delegated_user_id, $limit = 100) {
        try {
            $db = $this->getConnection();
            if (!$db) {
                error_log("Database connection failed in getActivityLog");
                return [];
            }
            
            $stmt = $db->prepare("
                SELECT action, resource_type, resource_id, details, ip_address, result, created_at
                FROM delegated_user_activity
                WHERE delegated_user_id = :delegated_user_id
                ORDER BY created_at DESC
                LIMIT :limit
            ");
            $stmt->bindValue(':delegated_user_id', $delegated_user_id, PDO::PARAM_INT);
            $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
            $stmt->execute();
            return $stmt->fetchAll();
        } catch (Exception $e) {
            error_log("Error getting activity log: " . $e->getMessage());
            return [];
        }
    }
}