<?php
/**
 * PostgreSQL Management Library for Web Hosting Panel
 * Handles PostgreSQL user creation, database management, and permissions
 */

class pgsqlmgmt {
    
    private $db_host = '127.0.0.1';
    private $db_port = 5432;
    private $root_user = 'postgres';
    private $root_password = '';
    
    public function __construct() {
        // Use the POSTGRES_PASS constant from config.php (loaded via auto-prepend)
        if (defined('POSTGRES_PASS')) {
            $this->root_password = trim(POSTGRES_PASS);
        } else {
            throw new \Exception('POSTGRES_PASS constant is not defined. Please check your configuration.');
        }
    }
    
    /**
     * Get PostgreSQL connection using root credentials
     */
    public function getPostgreSQLConnection($database = 'postgres') {
        try {
            // Force TCP connection since PostgreSQL is running in Docker
            $dsn = "pgsql:host={$this->db_host};port={$this->db_port};dbname={$database}";
            $pdo = new PDO($dsn, $this->root_user, $this->root_password, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
            return $pdo;
        } catch (PDOException $e) {
            error_log("PostgreSQL connection failed: " . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Create a new PostgreSQL user for a hosting panel user
     * 
     * @param string $username The username for the PostgreSQL user
     * @param string $password The password for the PostgreSQL user
     * @return array Status array with success/error information
     */
    public function createPostgreSQLUser($username, $password) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if user already exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = ?");
            $stmt->execute([$username]);
            if ($stmt->fetch()) {
                return ['status' => '1', 'msg' => 'PostgreSQL user already exists'];
            }
            
            // Create the user with proper escaping
            $escaped_username = '"' . str_replace('"', '""', $username) . '"';
            $escaped_password = "'" . str_replace("'", "''", $password) . "'";
            $sql = "CREATE USER $escaped_username WITH PASSWORD $escaped_password";
            $pdo->exec($sql);
            
            // Grant CREATE privilege so user can create databases with their prefix
            $sql = "ALTER USER $escaped_username CREATEDB";
            $pdo->exec($sql);
            
            return ['status' => '0', 'msg' => 'PostgreSQL user created successfully'];
            
        } catch (PDOException $e) {
            error_log("PostgreSQL user creation failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to create PostgreSQL user: ' . $e->getMessage()];
        }
    }

    /**
     * Create a PostgreSQL user for SSO without database creation privileges
     * 
     * @param string $username The username to create
     * @param string $password The user's password
     * @return array Status array with success/error information
     */
    public function createPostgreSQLUserForSSO($username, $password) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if user already exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = ?");
            $stmt->execute([$username]);
            if ($stmt->fetch()) {
                return ['status' => '1', 'msg' => 'PostgreSQL user already exists'];
            }
            
            // Create the user with proper escaping - NO CREATEDB privilege
            $escaped_username = '"' . str_replace('"', '""', $username) . '"';
            $escaped_password = "'" . str_replace("'", "''", $password) . "'";
            $sql = "CREATE USER $escaped_username WITH PASSWORD $escaped_password";
            $pdo->exec($sql);
            
            // Set unlimited connection limit (no CREATEDB privilege for SSO users)
            $sql = "ALTER USER $escaped_username CONNECTION LIMIT -1";
            $pdo->exec($sql);
            
            return ['status' => '0', 'msg' => 'PostgreSQL SSO user created successfully'];
            
        } catch (PDOException $e) {
            error_log("PostgreSQL SSO user creation failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to create PostgreSQL SSO user: ' . $e->getMessage()];
        }
    }
    
    /**
     * Delete a PostgreSQL user
     * 
     * @param string $username The username to delete
     * @return array Status array with success/error information
     */
    public function deletePostgreSQLUser($username) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if user exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = ?");
            $stmt->execute([$username]);
            if (!$stmt->fetch()) {
                return ['status' => '1', 'msg' => 'PostgreSQL user does not exist'];
            }
            
            // Drop owned objects and reassign ownership
            $escaped_username = '"' . str_replace('"', '""', $username) . '"';
            $sql = "DROP OWNED BY $escaped_username CASCADE";
            $pdo->exec($sql);
            
            // Drop the user
            $sql = "DROP USER $escaped_username";
            $pdo->exec($sql);
            
            return ['status' => '0', 'msg' => 'PostgreSQL user deleted successfully'];
            
        } catch (PDOException $e) {
            error_log("PostgreSQL user deletion failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to delete PostgreSQL user: ' . $e->getMessage()];
        }
    }
    
    /**
     * Change PostgreSQL user password
     * 
     * @param string $username The username
     * @param string $new_password The new password
     * @return array Status array with success/error information
     */
    public function changePostgreSQLPassword($username, $new_password) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if user exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = ?");
            $stmt->execute([$username]);
            if (!$stmt->fetch()) {
                return ['status' => '1', 'msg' => 'PostgreSQL user does not exist'];
            }
            
            // Change password
            $escaped_username = '"' . str_replace('"', '""', $username) . '"';
            $escaped_password = "'" . str_replace("'", "''", $new_password) . "'";
            $sql = "ALTER USER $escaped_username WITH PASSWORD $escaped_password";
            $pdo->exec($sql);
            
            return ['status' => '0', 'msg' => 'PostgreSQL password changed successfully'];
            
        } catch (PDOException $e) {
            error_log("PostgreSQL password change failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to change PostgreSQL password: ' . $e->getMessage()];
        }
    }
    
    /**
     * Create a new database
     * 
     * @param string $database_name The name of the database to create
     * @param string $owner_username The username who will own this database
     * @return array Status array with success/error information
     */
    public function createDatabase($database_name, $owner_username) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if database already exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_database WHERE datname = ?");
            $stmt->execute([$database_name]);
            if ($stmt->fetch()) {
                return ['status' => '1', 'msg' => 'Database already exists'];
            }
            
            // Create the database with owner
            $escaped_database = '"' . str_replace('"', '""', $database_name) . '"';
            $escaped_owner = '"' . str_replace('"', '""', $owner_username) . '"';
            $sql = "CREATE DATABASE $escaped_database OWNER $escaped_owner";
            $pdo->exec($sql);

            // Grant explicit schema permissions for PostgreSQL 15+ compatibility
            if (!$this->grantOwnerPermissions($database_name, $owner_username)) {
                error_log("Warning: Database '$database_name' created but schema permissions could not be fully set for '$owner_username'");
            }

            return ['status' => '0', 'msg' => 'Database created successfully'];
            
        } catch (PDOException $e) {
            error_log("Database creation failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to create database: ' . $e->getMessage()];
        }
    }
    
    /**
     * Grant full permissions on a database to its owner user.
     * Connects to the target database and grants schema-level privileges.
     * Required for PostgreSQL 15+ where public schema CREATE is revoked by default.
     *
     * @param string $database_name The database to grant permissions on
     * @param string $owner_username The owner's PostgreSQL username
     * @return bool True on success, false on failure
     */
    private function grantOwnerPermissions($database_name, $owner_username) {
        try {
            $escaped_username = '"' . str_replace('"', '""', $owner_username) . '"';
            $escaped_database = '"' . str_replace('"', '""', $database_name) . '"';

            // Grant database-level privileges
            $pdo = $this->getPostgreSQLConnection();
            if ($pdo) {
                $pdo->exec("GRANT ALL PRIVILEGES ON DATABASE $escaped_database TO $escaped_username");
            }

            // Connect to the target database for schema-level grants
            $db_pdo = $this->getPostgreSQLConnection($database_name);
            if (!$db_pdo) {
                error_log("grantOwnerPermissions: Could not connect to database '$database_name'");
                return false;
            }

            // Grant schema privileges
            $db_pdo->exec("GRANT ALL ON SCHEMA public TO $escaped_username");
            $db_pdo->exec("GRANT ALL ON ALL TABLES IN SCHEMA public TO $escaped_username");
            $db_pdo->exec("GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO $escaped_username");
            $db_pdo->exec("GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO $escaped_username");

            // Set default privileges for future objects
            $db_pdo->exec("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $escaped_username");
            $db_pdo->exec("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $escaped_username");
            $db_pdo->exec("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO $escaped_username");

            return true;

        } catch (PDOException $e) {
            error_log("grantOwnerPermissions failed for '$database_name' / '$owner_username': " . $e->getMessage());
            return false;
        }
    }

    /**
     * Delete a database
     *
     * @param string $database_name The name of the database to delete
     * @return array Status array with success/error information
     */
    public function deleteDatabase($database_name) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if database exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_database WHERE datname = ?");
            $stmt->execute([$database_name]);
            if (!$stmt->fetch()) {
                return ['status' => '1', 'msg' => 'Database does not exist'];
            }
            
            // Terminate active connections to the database
            $stmt = $pdo->prepare("
                SELECT pg_terminate_backend(pg_stat_activity.pid)
                FROM pg_stat_activity
                WHERE pg_stat_activity.datname = ?
                AND pid <> pg_backend_pid()
            ");
            $stmt->execute([$database_name]);
            
            // Drop the database
            $escaped_database = '"' . str_replace('"', '""', $database_name) . '"';
            $sql = "DROP DATABASE $escaped_database";
            $pdo->exec($sql);
            
            return ['status' => '0', 'msg' => 'Database deleted successfully'];
            
        } catch (PDOException $e) {
            error_log("Database deletion failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to delete database: ' . $e->getMessage()];
        }
    }
    
    /**
     * Create a new database user (sub-user for a hosting panel user)
     * 
     * @param string $username The username for the database user
     * @param string $password The password for the database user
     * @param string $database_name The database this user will have access to
     * @param array $privileges Array of privileges to grant (default: ['ALL'])
     * @return array Status array with success/error information
     */
    public function createDatabaseUser($username, $password, $database_name, $privileges = ['ALL']) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if database exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_database WHERE datname = ?");
            $stmt->execute([$database_name]);
            if (!$stmt->fetch()) {
                return ['status' => '1', 'msg' => 'Database does not exist'];
            }

            // Always drop user if exists to avoid conflicts
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = ?");
            $stmt->execute([$username]);
            if ($stmt->fetch()) {
                $escaped_username = '"' . str_replace('"', '""', $username) . '"';
                $sql = "DROP USER $escaped_username";
                $pdo->exec($sql);
            }

            // Create the user
            $escaped_username = '"' . str_replace('"', '""', $username) . '"';
            $escaped_password = "'" . str_replace("'", "''", $password) . "'";
            $sql = "CREATE USER $escaped_username WITH PASSWORD $escaped_password";
            $pdo->exec($sql);

            // Grant privileges on database
            if (in_array('ALL', $privileges)) {
                // Grant all privileges on database
                $escaped_database = '"' . str_replace('"', '""', $database_name) . '"';
                $escaped_username = '"' . str_replace('"', '""', $username) . '"';
                $sql = "GRANT ALL PRIVILEGES ON DATABASE $escaped_database TO $escaped_username";
                $pdo->exec($sql);
                
                // Connect to the specific database to grant schema privileges
                $db_pdo = $this->getPostgreSQLConnection($database_name);
                if ($db_pdo) {
                    // Grant all privileges on all schemas
                    $escaped_username = '"' . str_replace('"', '""', $username) . '"';
                    $db_pdo->exec("GRANT ALL ON SCHEMA public TO $escaped_username");
                    $db_pdo->exec("GRANT ALL ON ALL TABLES IN SCHEMA public TO $escaped_username");
                    $db_pdo->exec("GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO $escaped_username");
                    $db_pdo->exec("GRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO $escaped_username");
                    
                    // Set default privileges for future objects
                    $db_pdo->exec("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $escaped_username");
                    $db_pdo->exec("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $escaped_username");
                    $db_pdo->exec("ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO $escaped_username");
                }
            } else {
                // Grant specific privileges
                $privilege_map = [
                    'SELECT' => 'SELECT',
                    'INSERT' => 'INSERT',
                    'UPDATE' => 'UPDATE',
                    'DELETE' => 'DELETE',
                    'CREATE' => 'CREATE',
                    'TEMP' => 'TEMPORARY',
                    'CONNECT' => 'CONNECT'
                ];
                
                $valid_privileges = [];
                foreach ($privileges as $priv) {
                    if (isset($privilege_map[strtoupper($priv)])) {
                        $valid_privileges[] = $privilege_map[strtoupper($priv)];
                    }
                }
                
                if (!empty($valid_privileges)) {
                    $escaped_database = '"' . str_replace('"', '""', $database_name) . '"';
                    $escaped_username = '"' . str_replace('"', '""', $username) . '"';
                    $sql = 'GRANT ' . implode(', ', $valid_privileges) . " ON DATABASE $escaped_database TO $escaped_username";
                    $pdo->exec($sql);
                }
            }

            return ['status' => '0', 'msg' => 'Database user created successfully'];

        } catch (PDOException $e) {
            error_log("Database user creation failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to create database user: ' . $e->getMessage()];
        }
    }
    
    /**
     * Delete a database user
     * 
     * @param string $username The username to delete
     * @return array Status array with success/error information
     */
    public function deleteDatabaseUser($username) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if user exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = ?");
            $stmt->execute([$username]);
            if (!$stmt->fetch()) {
                return ['status' => '1', 'msg' => 'Database user does not exist'];
            }
            
            // Drop owned objects
            $escaped_username = '"' . str_replace('"', '""', $username) . '"';
            $sql = "DROP OWNED BY $escaped_username CASCADE";
            $pdo->exec($sql);
            
            // Drop the user
            $sql = "DROP USER $escaped_username";
            $pdo->exec($sql);
            
            return ['status' => '0', 'msg' => 'Database user deleted successfully'];
            
        } catch (PDOException $e) {
            error_log("Database user deletion failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to delete database user: ' . $e->getMessage()];
        }
    }
    
    /**
     * Update database user privileges
     * 
     * @param string $username The username
     * @param string $database_name The database name
     * @param array $privileges Array of privileges to grant
     * @return array Status array with success/error information
     */
    public function updateDatabaseUserPrivileges($username, $database_name, $privileges) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Check if user exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = ?");
            $stmt->execute([$username]);
            if (!$stmt->fetch()) {
                return ['status' => '1', 'msg' => 'Database user does not exist'];
            }
            
            // Check if database exists
            $stmt = $pdo->prepare("SELECT 1 FROM pg_database WHERE datname = ?");
            $stmt->execute([$database_name]);
            if (!$stmt->fetch()) {
                return ['status' => '1', 'msg' => 'Database does not exist'];
            }
            
            // Revoke all privileges first
            $escaped_database = '"' . str_replace('"', '""', $database_name) . '"';
            $escaped_username = '"' . str_replace('"', '""', $username) . '"';
            $sql = "REVOKE ALL PRIVILEGES ON DATABASE $escaped_database FROM $escaped_username";
            $pdo->exec($sql);
            
            // Grant new privileges
            if (in_array('ALL', $privileges)) {
                $sql = "GRANT ALL PRIVILEGES ON DATABASE $escaped_database TO $escaped_username";
                $pdo->exec($sql);
            } else {
                $privilege_map = [
                    'SELECT' => 'SELECT',
                    'INSERT' => 'INSERT',
                    'UPDATE' => 'UPDATE',
                    'DELETE' => 'DELETE',
                    'CREATE' => 'CREATE',
                    'TEMP' => 'TEMPORARY',
                    'CONNECT' => 'CONNECT'
                ];
                
                $valid_privileges = [];
                foreach ($privileges as $priv) {
                    if (isset($privilege_map[strtoupper($priv)])) {
                        $valid_privileges[] = $privilege_map[strtoupper($priv)];
                    }
                }
                
                if (!empty($valid_privileges)) {
                    $escaped_database = '"' . str_replace('"', '""', $database_name) . '"';
                    $escaped_username = '"' . str_replace('"', '""', $username) . '"';
                    $sql = 'GRANT ' . implode(', ', $valid_privileges) . " ON DATABASE $escaped_database TO $escaped_username";
                    $pdo->exec($sql);
                }
            }
            
            return ['status' => '0', 'msg' => 'Database user privileges updated successfully'];
            
        } catch (PDOException $e) {
            error_log("Database user privileges update failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to update database user privileges: ' . $e->getMessage()];
        }
    }
    
    /**
     * List all databases for a user
     * 
     * @param string $username The username
     * @return array Array of databases the user has access to
     */
    public function listUserDatabases($username) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            if ($username === 'root' || $username === 'postgres') {
                // Root can see all databases except templates
                $stmt = $pdo->prepare("
                    SELECT datname 
                    FROM pg_database 
                    WHERE datistemplate = false
                    ORDER BY datname
                ");
                $stmt->execute();
            } else {
                // Regular users see databases they own or have access to
                $stmt = $pdo->prepare("
                    SELECT d.datname 
                    FROM pg_database d
                    LEFT JOIN pg_user u ON d.datdba = u.usesysid
                    WHERE d.datistemplate = false 
                    AND (u.usename = ? OR d.datname LIKE ?)
                    ORDER BY d.datname
                ");
                $user_prefix = $username . '_%';
                $stmt->execute([$username, $user_prefix]);
            }
            
            $databases = $stmt->fetchAll();
            
            return ['status' => '0', 'databases' => array_column($databases, 'datname')];
            
        } catch (PDOException $e) {
            error_log("Database listing failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to list databases: ' . $e->getMessage()];
        }
    }
    
    /**
     * List all database users for a specific database
     * 
     * @param string $database_name The database name
     * @return array Array of users with access to the database
     */
    public function listDatabaseUsers($database_name) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            // Get users with explicit privileges on the database
            $stmt = $pdo->prepare("
                SELECT DISTINCT 
                    pg_user.usename as username,
                    CASE 
                        WHEN has_database_privilege(pg_user.usename, ?, 'CREATE') THEN 'Yes' 
                        ELSE 'No' 
                    END as can_create,
                    CASE 
                        WHEN has_database_privilege(pg_user.usename, ?, 'CONNECT') THEN 'Yes' 
                        ELSE 'No' 
                    END as can_connect,
                    CASE 
                        WHEN has_database_privilege(pg_user.usename, ?, 'TEMP') THEN 'Yes' 
                        ELSE 'No' 
                    END as can_temp
                FROM pg_user
                WHERE pg_user.usename != 'postgres'
                AND has_database_privilege(pg_user.usename, ?, 'CONNECT')
                ORDER BY pg_user.usename
            ");
            $stmt->execute([$database_name, $database_name, $database_name, $database_name]);
            $users = $stmt->fetchAll();
            
            return ['status' => '0', 'users' => $users];
            
        } catch (PDOException $e) {
            error_log("Database users listing failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to list database users: ' . $e->getMessage()];
        }
    }
    
    /**
     * Check if a PostgreSQL user exists
     * 
     * @param string $pgUser The PostgreSQL username to check
     * @return array Result with exists boolean or error message
     */
    public function checkUserExists($pgUser) {
        try {
            $pdo = new PDO("pgsql:host={$this->db_host};port={$this->db_port};dbname=postgres", 
                          $this->root_user, $this->root_password);
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            
            $stmt = $pdo->prepare("SELECT 1 FROM pg_user WHERE usename = :username");
            $stmt->bindParam(':username', $pgUser);
            $stmt->execute();
            
            $exists = $stmt->fetch() !== false;
            
            return ['status' => '0', 'exists' => $exists];
            
        } catch (Exception $e) {
            error_log("PostgreSQL checkUserExists error: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Error checking user existence: ' . $e->getMessage()];
        }
    }

    /**
     * List database users that a specific user can manage
     * 
     * @param string $username The username requesting the list
     * @return array Array of database users the user can manage
     */
    public function listManageableDatabaseUsers($username) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            if ($username === 'root' || $username === 'postgres') {
                // Root can see all database users except postgres itself
                $stmt = $pdo->prepare("
                    SELECT usename as username 
                    FROM pg_user 
                    WHERE usename != 'postgres' 
                    ORDER BY usename
                ");
                $stmt->execute();
            } else {
                // Regular users can only see their own prefixed database users
                $user_prefix = $username . '_%';
                $stmt = $pdo->prepare("
                    SELECT usename as username 
                    FROM pg_user 
                    WHERE usename LIKE ? 
                    AND usename != ? 
                    ORDER BY usename
                ");
                $stmt->execute([$user_prefix, $username]);
            }
            
            $users = $stmt->fetchAll();
            
            // Get database access for each user
            foreach ($users as &$user) {
                $stmt = $pdo->prepare("
                    SELECT datname 
                    FROM pg_database 
                    WHERE has_database_privilege(?, datname, 'CONNECT')
                    AND datistemplate = false
                    ORDER BY datname
                ");
                $stmt->execute([$user['username']]);
                $databases = $stmt->fetchAll();
                $user['databases'] = implode(', ', array_column($databases, 'datname'));
            }
            
            return ['status' => '0', 'users' => $users];
            
        } catch (PDOException $e) {
            error_log("Manageable database users listing failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to list manageable database users: ' . $e->getMessage()];
        }
    }
    
    /**
     * Check if a user can manage a specific database user
     * 
     * @param string $requesting_user The user making the request
     * @param string $target_user The database user to check
     * @return bool True if the user can manage the target user
     */
    public function canManageDatabaseUser($requesting_user, $target_user) {
        // Root can manage any database user (except postgres itself)
        if ($requesting_user === 'root' || $requesting_user === 'postgres') {
            return $target_user !== 'postgres';
        }
        
        // Regular users can only manage their own prefixed database users
        if (strpos($target_user, $requesting_user . '_') === 0 && $target_user !== $requesting_user) {
            return true;
        }
        
        return false;
    }
    
    /**
     * List databases that a specific user can manage
     * 
     * @param string $username The username requesting the list
     * @return array Array of databases the user can manage
     */
    public function listManageableDatabases($username) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            if ($username === 'root' || $username === 'postgres') {
                // Root can see all databases except templates
                $stmt = $pdo->prepare("
                    SELECT datname 
                    FROM pg_database 
                    WHERE datistemplate = false
                    ORDER BY datname
                ");
                $stmt->execute();
            } else {
                // Regular users can see databases they own or with their prefix
                $user_prefix = $username . '_%';
                $stmt = $pdo->prepare("
                    SELECT d.datname 
                    FROM pg_database d
                    LEFT JOIN pg_user u ON d.datdba = u.usesysid
                    WHERE d.datistemplate = false 
                    AND (u.usename = ? OR d.datname LIKE ?)
                    ORDER BY d.datname
                ");
                $stmt->execute([$username, $user_prefix]);
            }
            
            $databases = $stmt->fetchAll();
            
            return ['status' => '0', 'databases' => array_column($databases, 'datname')];
            
        } catch (PDOException $e) {
            error_log("Manageable databases listing failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to list manageable databases: ' . $e->getMessage()];
        }
    }
    
    /**
     * Check if a user can manage a specific database
     * 
     * @param string $requesting_user The user making the request
     * @param string $target_database The database to check
     * @return bool True if the user can manage the target database
     */
    public function canManageDatabase($requesting_user, $target_database) {
        // Root can manage any database
        if ($requesting_user === 'root' || $requesting_user === 'postgres') {
            return true;
        }
        
        // Regular users can only manage their own databases
        if (strpos($target_database, $requesting_user . '_') === 0) {
            return true;
        }
        
        // Check if user owns the database
        $pdo = $this->getPostgreSQLConnection();
        if ($pdo) {
            try {
                $stmt = $pdo->prepare("
                    SELECT 1 
                    FROM pg_database d
                    JOIN pg_user u ON d.datdba = u.usesysid
                    WHERE d.datname = ? AND u.usename = ?
                ");
                $stmt->execute([$target_database, $requesting_user]);
                if ($stmt->fetch()) {
                    return true;
                }
            } catch (PDOException $e) {
                error_log("Database ownership check failed: " . $e->getMessage());
            }
        }
        
        return false;
    }
    
    /**
     * Get database size information
     * 
     * @param string $database_name The database name
     * @return array Database size information
     */
    public function getDatabaseSize($database_name) {
        $pdo = $this->getPostgreSQLConnection();
        if (!$pdo) {
            return ['status' => '1', 'msg' => 'Failed to connect to PostgreSQL server'];
        }
        
        try {
            $stmt = $pdo->prepare("
                SELECT 
                    pg_database_size(?) AS size_bytes,
                    pg_size_pretty(pg_database_size(?)) AS size_pretty,
                    (SELECT COUNT(*) FROM pg_stat_user_tables WHERE schemaname = 'public') as table_count
            ");
            $stmt->execute([$database_name, $database_name]);
            $result = $stmt->fetch();
            
            if ($result) {
                $result['size_mb'] = round($result['size_bytes'] / 1024 / 1024, 2);
            }
            
            return ['status' => '0', 'size_info' => $result];
            
        } catch (PDOException $e) {
            error_log("Database size query failed: " . $e->getMessage());
            return ['status' => '1', 'msg' => 'Failed to get database size: ' . $e->getMessage()];
        }
    }
    
    /**
     * Test PostgreSQL connection with given credentials
     * 
     * @param string $username The username to test
     * @param string $password The password to test
     * @param string $database The database to connect to (optional)
     * @return array Status array with success/error information
     */
    public function testConnection($username, $password, $database = null) {
        try {
            $dsn = "pgsql:host={$this->db_host};port={$this->db_port}";
            if ($database) {
                $dsn .= ";dbname={$database}";
            }
            
            $pdo = new PDO($dsn, $username, $password, [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
            
            return ['status' => '0', 'msg' => 'Connection successful'];
            
        } catch (PDOException $e) {
            return ['status' => '1', 'msg' => 'Connection failed: ' . $e->getMessage()];
        }
    }
}
?>