<?php
/**
 * Permission Management Library for Web Hosting Panel
 * Handles permission checking, access logging, and user feedback
 */

class permission_manager {
    
    private $db_path = '/docker/whp/sql/permissions.db';
    private $db;
    
    // Define page permissions - which users can access which pages
    private $page_permissions = array(
        'dashboard' => array('all'), // All authenticated users
        'database-management' => array('all'), // All authenticated users
        'postgresql-management' => array('all'), // All authenticated users
        'user-management' => array('root'), // Only root
        'delegated-users' => array('all'), // All users can manage their own delegated users
        'active-sessions' => array('all'), // All users can view/terminate their own sessions; root sees all
        'security-management' => array('root'), // Only root
        'announcements-management' => array('root'), // Only root
        'update-management' => array('root'), // Only root
        'docker-management' => array('root'), // Only root
        'user-resources' => array('root'), // Only root
        'domains' => array('all'), // All users (with domain restrictions)
        'sites' => array('all'), // All users (with site ownership restrictions)
        'site-builder' => array('all'), // All authenticated users
        'wordpress' => array('all'), // All authenticated users (with site ownership restrictions)
        'server-settings' => array('root'), // Only root
        'ai-monitor' => array('root'), // Only root
        'ai-monitor-customer' => array('all'), // All users with active monitoring subscriptions
        'issues' => array('root'), // Only root — unified Issues tracker
        'service-management' => array('root'), // Only root
        'email-management' => array('all'), // All authenticated users (with domain ownership restrictions)
        'backups' => array('all'), // All authenticated users (with backup ownership restrictions)
        'cpanel-import' => array('all'), // All authenticated users (can import their own backups)
        'terminal' => array('all'), // All authenticated users (with container ownership restrictions)
        'account-valkey' => array('all'), // All authenticated users — Valkey cache is a per-customer feature
        'valkey-admin'   => array('root'), // Root-only fleet overview of all customer Valkey instances
    );

    // Define API permissions
    private $api_permissions = array(
        'user-management' => array('root'),
        'database-management' => array('all'),
        'postgresql-management' => array('all'),
        'security-management' => array('root'),
        'announcements-management' => array('root'),
        'update-management' => array('root'),
        'docker-management' => array('root'),
        'user-resources' => array('root'),
        'domains' => array('all'),
        'sites' => array('all'), // All users (with site ownership restrictions)
        'server-settings' => array('root'),
        'email-management' => array('all'), // All authenticated users (with domain ownership restrictions)
        'backup-actions' => array('all'), // All authenticated users (with backup ownership restrictions)
        'sessions' => array('all'), // All users can manage their own active sessions; API enforces user-scoping
        'disk-cleanup' => array('root'), // Detect + execute disk cleanup; root only (the API also re-checks)
        'cpanel-import' => array('all'), // All authenticated users (can import their own backups)
        'terminal' => array('all'), // All authenticated users (with container ownership restrictions)
        'valkey' => array('all'), // All authenticated users — Valkey API
    );

    public function __construct() {
        $this->init_database();
    }
    
    private function init_database() {
        if (!file_exists($this->db_path)) {
            $this->db = new SQLite3($this->db_path);
            $this->db->busyTimeout(6000);
            
            // Create access attempts table
            $this->db->exec('CREATE TABLE access_attempts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user TEXT NOT NULL,
                page TEXT NOT NULL,
                ip_address TEXT NOT NULL,
                user_agent TEXT,
                access_time INTEGER NOT NULL,
                allowed INTEGER DEFAULT 0,
                reason TEXT
            )');
            
            // Create indexes for better performance
            $this->db->exec('CREATE INDEX idx_access_attempts_user ON access_attempts(user)');
            $this->db->exec('CREATE INDEX idx_access_attempts_time ON access_attempts(access_time)');
            $this->db->exec('CREATE INDEX idx_access_attempts_page ON access_attempts(page)');
        } else {
            $this->db = new SQLite3($this->db_path);
            $this->db->busyTimeout(6000);
        }
    }
    
    /**
     * Check if user has permission to access a page
     * 
     * @param string $user The username
     * @param string $page The page name
     * @return bool True if user has permission, false otherwise
     */
    // Pages allowed per restricted account type
    private $account_type_pages = [
        'domain_dns' => ['dashboard', 'domains'],
        'mail_dns'   => ['dashboard', 'domains', 'email-management'],
    ];

    public function check_page_permission($user, $page) {
        // Check if this is a delegated user
        if (isset($_SESSION['user_type']) && $_SESSION['user_type'] === 'delegated') {
            return $this->check_delegated_page_permission($user, $page);
        }

        // Account type restrictions for non-root system users
        $account_type = $_SESSION['account_type'] ?? 'full';
        if ($account_type !== 'full' && isset($this->account_type_pages[$account_type])) {
            return in_array($page, $this->account_type_pages[$account_type]);
        }

        // Check if page exists in permissions
        if (!isset($this->page_permissions[$page])) {
            // Unknown page - deny access
            return false;
        }

        $allowed_users = $this->page_permissions[$page];

        // Check if 'all' users are allowed
        if (in_array('all', $allowed_users)) {
            return true;
        }

        // Check if specific user is allowed
        if (in_array($user, $allowed_users)) {
            return true;
        }

        return false;
    }
    
    /**
     * Check if delegated user has permission to access a page
     * 
     * @param string $user The delegated username
     * @param string $page The page name
     * @return bool True if user has permission, false otherwise
     */
    public function check_delegated_page_permission($user, $page) {
        if (!isset($_SESSION['delegated_user_id'])) {
            return false;
        }
        
        // Map page names to delegated user permission flags
        $page_permission_map = [
            'domains' => 'can_manage_domains',
            'sites' => 'can_manage_sites',
            'database-management' => 'can_manage_databases',
            'postgresql-management' => 'can_manage_databases',
            'email-management' => 'can_manage_email',
            'backups' => 'can_manage_backups',
            'terminal' => 'can_view_terminal',
            'wordpress' => 'can_manage_wordpress',
            'dashboard' => true, // All delegated users can access dashboard
        ];
        
        // Check if page is explicitly not allowed for delegated users
        $restricted_pages = ['user-management', 'security-management', 'announcements-management', 
                           'update-management', 'docker-management', 'user-resources', 'server-settings'];
        
        if (in_array($page, $restricted_pages)) {
            return false;
        }
        
        // Special case: delegated users can access the delegated-users page (to view their own info)
        // but with limited functionality
        if ($page === 'delegated-users') {
            return true;
        }
        
        // If page is not mapped, allow access (for general pages)
        if (!isset($page_permission_map[$page])) {
            return true;
        }
        
        // If mapped to true, always allow
        if ($page_permission_map[$page] === true) {
            return true;
        }
        
        // Check delegated user permissions
        require_once('/docker/whp/web/libs/delegated_user_manager.php');
        $delegatedUserManager = new DelegatedUserManager();
        
        $permission_name = str_replace('can_', '', $page_permission_map[$page]);
        return $delegatedUserManager->hasPermission($_SESSION['delegated_user_id'], $permission_name);
    }
    
    /**
     * Check if user has permission to access an API
     * 
     * @param string $user The username
     * @param string $api The API name
     * @return bool True if user has permission, false otherwise
     */
    public function check_api_permission($user, $api) {
        // Check if this is a delegated user
        if (isset($_SESSION['user_type']) && $_SESSION['user_type'] === 'delegated') {
            return $this->check_delegated_api_permission($user, $api);
        }
        
        // Check if API exists in permissions
        if (!isset($this->api_permissions[$api])) {
            // Unknown API - deny access
            return false;
        }
        
        $allowed_users = $this->api_permissions[$api];
        
        // Check if 'all' users are allowed
        if (in_array('all', $allowed_users)) {
            return true;
        }
        
        // Check if specific user is allowed
        if (in_array($user, $allowed_users)) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Check if delegated user has permission to access an API
     * 
     * @param string $user The delegated username
     * @param string $api The API name
     * @return bool True if user has permission, false otherwise
     */
    public function check_delegated_api_permission($user, $api) {
        if (!isset($_SESSION['delegated_user_id'])) {
            return false;
        }
        
        // Map API names to delegated user permission flags
        $api_permission_map = [
            'domains' => 'can_manage_domains',
            'sites' => 'can_manage_sites',
            'database-management' => 'can_manage_databases',
            'postgresql-management' => 'can_manage_databases',
            'email-management' => 'can_manage_email',
            'backup-actions' => 'can_manage_backups',
            'terminal' => 'can_view_terminal',
            'wordpress-ajax' => 'can_manage_wordpress',
        ];
        
        // Check if API is explicitly not allowed for delegated users
        $restricted_apis = ['user-management', 'security-management', 'announcements-management', 
                          'update-management', 'docker-management', 'user-resources'];
        
        if (in_array($api, $restricted_apis)) {
            return false;
        }
        
        // Allow delegated user management API for the delegated user itself
        if ($api === 'delegated-user-management') {
            return true;
        }
        
        // If API is not mapped, deny access for security
        if (!isset($api_permission_map[$api])) {
            return false;
        }
        
        // Check delegated user permissions
        require_once('/docker/whp/web/libs/delegated_user_manager.php');
        $delegatedUserManager = new DelegatedUserManager();
        
        $permission_name = str_replace('can_', '', $api_permission_map[$api]);
        return $delegatedUserManager->hasPermission($_SESSION['delegated_user_id'], $permission_name);
    }
    
    /**
     * Log an access attempt
     * 
     * @param string $user The username
     * @param string $page The page or API name
     * @param string $ip_address The IP address
     * @param string $user_agent The user agent
     * @param bool $allowed Whether access was allowed
     * @param string $reason Reason for denial (if applicable)
     * @return bool True on success
     */
    public function log_access_attempt($user, $page, $ip_address, $user_agent = '', $allowed = true, $reason = '') {
        $stmt = $this->db->prepare('INSERT INTO access_attempts (user, page, ip_address, user_agent, access_time, allowed, reason) VALUES (:user, :page, :ip, :agent, :time, :allowed, :reason)');
        $stmt->bindValue(':user', $user, SQLITE3_TEXT);
        $stmt->bindValue(':page', $page, SQLITE3_TEXT);
        $stmt->bindValue(':ip', $ip_address, SQLITE3_TEXT);
        $stmt->bindValue(':agent', $user_agent, SQLITE3_TEXT);
        $stmt->bindValue(':time', time(), SQLITE3_INTEGER);
        $stmt->bindValue(':allowed', $allowed ? 1 : 0, SQLITE3_INTEGER);
        $stmt->bindValue(':reason', $reason, SQLITE3_TEXT);
        $stmt->execute();
        
        return true;
    }
    
    /**
     * Get access statistics for a user
     * 
     * @param string $user The username
     * @param int $days Number of days to look back
     * @return array Access statistics
     */
    public function get_user_access_stats($user, $days = 30) {
        $time_limit = time() - ($days * 86400);
        
        // Get total attempts
        $stmt = $this->db->prepare('SELECT COUNT(*) as total FROM access_attempts WHERE user = :user AND access_time > :time');
        $stmt->bindValue(':user', $user, SQLITE3_TEXT);
        $stmt->bindValue(':time', $time_limit, SQLITE3_INTEGER);
        $result = $stmt->execute();
        $total = $result->fetchArray(SQLITE3_ASSOC)['total'];
        
        // Get denied attempts
        $stmt = $this->db->prepare('SELECT COUNT(*) as denied FROM access_attempts WHERE user = :user AND access_time > :time AND allowed = 0');
        $stmt->bindValue(':user', $user, SQLITE3_TEXT);
        $stmt->bindValue(':time', $time_limit, SQLITE3_INTEGER);
        $result = $stmt->execute();
        $denied = $result->fetchArray(SQLITE3_ASSOC)['denied'];
        
        // Get recent denied attempts
        $stmt = $this->db->prepare('SELECT * FROM access_attempts WHERE user = :user AND allowed = 0 ORDER BY access_time DESC LIMIT 10');
        $stmt->bindValue(':user', $user, SQLITE3_TEXT);
        $result = $stmt->execute();
        
        $recent_denials = array();
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            $recent_denials[] = $row;
        }
        
        return array(
            'total_attempts' => $total,
            'denied_attempts' => $denied,
            'success_rate' => $total > 0 ? round((($total - $denied) / $total) * 100, 2) : 100,
            'recent_denials' => $recent_denials
        );
    }
    
    /**
     * Get system-wide access statistics
     * 
     * @param int $days Number of days to look back
     * @return array System access statistics
     */
    public function get_system_access_stats($days = 30) {
        $time_limit = time() - ($days * 86400);
        
        // Get total attempts
        $stmt = $this->db->prepare('SELECT COUNT(*) as total FROM access_attempts WHERE access_time > :time');
        $stmt->bindValue(':time', $time_limit, SQLITE3_INTEGER);
        $result = $stmt->execute();
        $total = $result->fetchArray(SQLITE3_ASSOC)['total'];
        
        // Get denied attempts
        $stmt = $this->db->prepare('SELECT COUNT(*) as denied FROM access_attempts WHERE access_time > :time AND allowed = 0');
        $stmt->bindValue(':time', $time_limit, SQLITE3_INTEGER);
        $result = $stmt->execute();
        $denied = $result->fetchArray(SQLITE3_ASSOC)['denied'];
        
        // Get top denied pages
        $stmt = $this->db->prepare('SELECT page, COUNT(*) as count FROM access_attempts WHERE access_time > :time AND allowed = 0 GROUP BY page ORDER BY count DESC LIMIT 10');
        $stmt->bindValue(':time', $time_limit, SQLITE3_INTEGER);
        $result = $stmt->execute();
        
        $top_denied_pages = array();
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            $top_denied_pages[] = $row;
        }
        
        // Get top denied users
        $stmt = $this->db->prepare('SELECT user, COUNT(*) as count FROM access_attempts WHERE access_time > :time AND allowed = 0 GROUP BY user ORDER BY count DESC LIMIT 10');
        $stmt->bindValue(':time', $time_limit, SQLITE3_INTEGER);
        $result = $stmt->execute();
        
        $top_denied_users = array();
        while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
            $top_denied_users[] = $row;
        }
        
        return array(
            'total_attempts' => $total,
            'denied_attempts' => $denied,
            'success_rate' => $total > 0 ? round((($total - $denied) / $total) * 100, 2) : 100,
            'top_denied_pages' => $top_denied_pages,
            'top_denied_users' => $top_denied_users
        );
    }
    
    /**
     * Clean up old access logs
     * 
     * @param int $days Keep logs for this many days
     * @return bool True on success
     */
    public function cleanup_old_logs($days = 90) {
        $time_limit = time() - ($days * 86400);
        
        $stmt = $this->db->prepare('DELETE FROM access_attempts WHERE access_time <= :time');
        $stmt->bindValue(':time', $time_limit, SQLITE3_INTEGER);
        $stmt->execute();
        
        return true;
    }
    
    /**
     * Check if user owns a specific domain
     * 
     * @param string $user The username
     * @param string $domain The domain name
     * @return bool True if user owns the domain, false otherwise
     */
    public function check_domain_ownership($user, $domain) {
        // Root can access all domains
        if ($user === 'root') {
            return true;
        }
        
        // For non-root users, check if domain belongs to them
        // Domain naming convention: username_domain.com or domain.com (if user is root)
        $domain_parts = explode('.', $domain);
        if (count($domain_parts) >= 2) {
            $subdomain = $domain_parts[0];
            // Check if domain starts with username_
            if (strpos($subdomain, $user . '_') === 0) {
                return true;
            }
            // Check if domain is exactly the username
            if ($subdomain === $user) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Get domains owned by a user
     * 
     * @param string $user The username
     * @return array Array of domain names owned by the user
     */
    public function get_user_domains($user) {
        // Root can see all domains
        if ($user === 'root') {
            return $this->get_all_domains();
        }
        
        // For non-root users, return domains that match their username pattern
        $all_domains = $this->get_all_domains();
        $user_domains = array();
        
        foreach ($all_domains as $domain) {
            if ($this->check_domain_ownership($user, $domain)) {
                $user_domains[] = $domain;
            }
        }
        
        return $user_domains;
    }
    
    /**
     * Get all domains in the system
     * 
     * @return array Array of all domain names
     */
    private function get_all_domains() {
        // This would typically query a database or configuration file
        // For now, return a placeholder list
        // In a real implementation, this would read from Apache vhost configs or a database
        
        $domains = array();
        
        // Check Apache vhost configurations
        $vhost_dir = '/etc/httpd/conf.d/';
        if (is_dir($vhost_dir)) {
            $files = glob($vhost_dir . '*.conf');
            foreach ($files as $file) {
                $content = file_get_contents($file);
                if (preg_match('/ServerName\s+([^\s]+)/', $content, $matches)) {
                    $domains[] = trim($matches[1]);
                }
            }
        }
        
        // Add some example domains for testing
        if (empty($domains)) {
            $domains = array(
                'example.com',
                'test.com',
                'demo.com'
            );
        }
        
        return $domains;
    }
    
    /**
     * Get a user-friendly error message for permission denial
     * 
     * @param string $page The page that was denied
     * @param string $user The username
     * @return string User-friendly error message
     */
    public function get_denial_message($page, $user) {
        $page_names = array(
            'user-management' => 'User Management',
            'security-management' => 'Security Management',
            'announcements-management' => 'Announcements Management',
            'update-management' => 'Update Management',
            'docker-management' => 'Docker Management',
            'user-resources' => 'User Resources',
            'domains' => 'Domains',
            'server-settings' => 'Server Settings',
            'cpanel-import' => 'cPanel Import'
        );
        
        $page_name = isset($page_names[$page]) ? $page_names[$page] : $page;
        
        if ($page === 'user-management' || $page === 'security-management' || $page === 'announcements-management' || $page === 'update-management' || $page === 'docker-management' || $page === 'user-resources' || $page === 'server-settings') {
            return "You do not have permission to access the {$page_name} page. This page requires administrator privileges.";
        } else {
            return "You do not have permission to access the {$page_name} page.";
        }
    }
    
    public function __destruct() {
        if ($this->db) {
            $this->db->close();
        }
    }
} 