<?php
/**
 * HAProxy Manager - Active Implementation
 * 
 * This class handles HAProxy configuration management for the WHP control panel
 * including backend server management, load balancing, and SSL termination.
 * 
 * @version 1.0.0
 * @author WHP Development Team
 */

class haproxy_manager {
    
    private $db;
    private $haproxy_api_url;
    private $haproxy_api_key;
    private $settings;
    
    public function __construct() {
        // Initialize database connection
        require_once('/docker/whp/web/libs/mysqlmgmt.php');
        $MySQLMgmt = new mysqlmgmt();
        $this->db = $MySQLMgmt->getMySQLConnection();
        
        // Load settings
        $this->loadSettings();
        
        // Set HAProxy API configuration
        $this->haproxy_api_url = 'http://localhost:8000';
        $this->haproxy_api_key = $this->settings['haproxy_api_key'] ?? '';
    }
    
    /**
     * Destructor to properly close database connection
     */
    public function __destruct() {
        if ($this->db) {
            $this->db = null; // Close PDO connection
        }
    }
    
    /**
     * Load WHP settings from settings.json
     */
    private function loadSettings() {
        $settings_file = '/docker/whp/settings.json';
        if (file_exists($settings_file)) {
            $settings_content = file_get_contents($settings_file);
            $this->settings = json_decode($settings_content, true) ?: [];
        } else {
            $this->settings = [];
        }
    }
    
    /**
     * Make API request to HAProxy Manager
     * 
     * @param string $method HTTP method (GET, POST, DELETE)
     * @param string $endpoint API endpoint
     * @param array $data Request data
     * @return array API response
     */
    private function makeApiRequest($method, $endpoint, $data = [], $timeout = 30) {
        $url = $this->haproxy_api_url . $endpoint;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        
        // Add authorization header if API key is set
        $headers = ['Content-Type: application/json'];
        if (!empty($this->haproxy_api_key)) {
            $headers[] = 'Authorization: Bearer ' . $this->haproxy_api_key;
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        
        // Add request data for POST/PUT/DELETE requests
        if (in_array($method, ['POST', 'PUT', 'DELETE']) && !empty($data)) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }
        
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);
        
        if ($error) {
            return [
                'success' => false,
                'error' => 'cURL Error: ' . $error
            ];
        }
        
        $decoded_response = json_decode($response, true);
        
        if ($http_code >= 200 && $http_code < 300) {
            return [
                'success' => true,
                'data' => $decoded_response,
                'http_code' => $http_code
            ];
        } else {
            return [
                'success' => false,
                'error' => $decoded_response['error'] ?? $decoded_response['message'] ?? 'HTTP Error: ' . $http_code,
                'http_code' => $http_code
            ];
        }
    }
    
    /**
     * Add domain to HAProxy configuration
     * 
     * @param string $domain Domain name
     * @param array $servers Backend servers configuration
     * @return array Result array with success/error status
     */
    public function addDomain($domain, $servers = [], $is_wildcard = false) {
        // Generate backend name from domain
        $backend_name = $this->generateBackendName($domain);

        // Prepare API request data
        $request_data = [
            'domain' => $domain,
            'backend_name' => $backend_name,
            'template_override' => null,
            'servers' => $servers,
            'is_wildcard' => $is_wildcard
        ];

        // Make API request
        $result = $this->makeApiRequest('POST', '/api/domain', $request_data);

        if ($result['success']) {
            // Store domain configuration in database
            $this->storeDomainConfiguration($domain, $backend_name, $servers, $is_wildcard);
        }

        return $result;
    }
    

    
    /**
     * Remove domain from HAProxy configuration
     * 
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function removeDomain($domain) {
        // Make API request to remove domain
        $result = $this->makeApiRequest('DELETE', '/api/domain', ['domain' => $domain]);
        
        if ($result['success']) {
            // Remove domain configuration from database
            $this->removeDomainConfiguration($domain);
        }
        
        return $result;
    }
    
    /**
     * Enable SSL for a domain
     * 
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function enableSSL($domain) {
        error_log("HAProxy Manager: Enabling SSL for domain: $domain");
        
        // Make API request to enable SSL
        $result = $this->makeApiRequest('POST', '/api/ssl', ['domain' => $domain]);
        
        error_log("HAProxy Manager: SSL enable result for $domain: " . ($result['success'] ? 'SUCCESS' : 'FAILED - ' . ($result['error'] ?? 'Unknown error')));
        
        if ($result['success']) {
            // Update SSL status in database
            $this->updateSSLStatus($domain, true);
        }
        
        return $result;
    }
    
    /**
     * Request SSL certificate generation/regeneration for a domain
     * This regenerates the combined PEM file from Let's Encrypt certificates
     *
     * @param string $domain Domain name
     * @param bool $force_renewal Force certificate renewal (default: false)
     * @param bool $include_www Include www subdomain (default: false)
     * @return array Result array with success/error status
     */
    public function requestCertificate($domain, $force_renewal = false, $include_www = false) {
        return $this->makeApiRequest('POST', '/api/certificates/request', [
            'domains' => [$domain],
            'force_renewal' => $force_renewal,
            'include_www' => $include_www
        ]);
    }
    
    /**
     * Renew SSL certificate for a domain
     * 
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function renewCertificate($domain) {
        return $this->makeApiRequest('POST', '/api/certificates/renew', ['domain' => $domain]);
    }
    
    /**
     * Get SSL certificate status for a domain
     * 
     * @param string $domain Domain name (optional, if not provided gets all certificates)
     * @return array Result array with success/error status
     */
    public function getCertificateStatus($domain = null) {
        $data = [];
        if ($domain) {
            $data['domain'] = $domain;
        }
        return $this->makeApiRequest('GET', '/api/certificates/status', $data);
    }
    
    /**
     * Download SSL certificate for a domain
     * 
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function downloadCertificate($domain) {
        error_log("HAProxy Manager: Attempting to download certificate for domain: $domain");
        $result = $this->makeApiRequest('GET', '/api/certificates/' . urlencode($domain) . '/download');
        error_log("HAProxy Manager: Download certificate result for $domain: " . json_encode($result));
        return $result;
    }
    
    /**
     * Get SSL certificate key for a domain
     * 
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function getCertificateKey($domain) {
        return $this->makeApiRequest('GET', '/api/certificates/' . urlencode($domain) . '/key');
    }
    
    /**
     * Get SSL certificate cert for a domain
     * 
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function getCertificateCert($domain) {
        return $this->makeApiRequest('GET', '/api/certificates/' . urlencode($domain) . '/cert');
    }
    
    /**
     * Get HAProxy health status
     * 
     * @return array Health status
     */
    public function getHealthStatus() {
        // Health endpoint is not under /api path
        $health_url = 'http://localhost:8000/health';
        
        try {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $health_url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 10);
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Authorization: Bearer ' . $this->haproxy_api_key,
                'Content-Type: application/json'
            ]);
            
            $response = curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            
            if (curl_error($ch)) {
                curl_close($ch);
                return ['success' => false, 'error' => 'Connection error: ' . curl_error($ch)];
            }
            
            curl_close($ch);
            
            if ($http_code === 200) {
                $data = json_decode($response, true);
                return ['success' => true, 'data' => $data];
            } else {
                return ['success' => false, 'error' => 'HTTP ' . $http_code . ': ' . $response];
            }
        } catch (Exception $e) {
            return ['success' => false, 'error' => 'Health check failed: ' . $e->getMessage()];
        }
    }
    
    /**
     * Reload HAProxy configuration
     * 
     * @return array Result array with success/error status
     */
    public function reloadConfiguration() {
        return $this->makeApiRequest('GET', '/api/reload');
    }
    
    /**
     * Regenerate HAProxy configuration
     * 
     * @return array Result array with success/error status
     */
    public function regenerateConfiguration() {
        return $this->makeApiRequest('GET', '/api/regenerate');
    }
    
    /**
     * Generate backend name from domain
     * 
     * Converts domain names to HAProxy-compatible backend names by replacing
     * dots and special characters with underscores. The HAProxy service templates
     * automatically append '-backend' suffix, so this method returns only the
     * sanitized domain name (e.g., 'example.com' becomes 'example_com').
     * 
     * @param string $domain Domain name (e.g., 'example.com')
     * @return string Sanitized backend name (e.g., 'example_com')
     */
    private function generateBackendName($domain) {
        // Replace wildcard prefix before sanitizing so *.example.com becomes wildcard_example_com
        $domain = str_replace('*', 'wildcard', $domain);
        // Remove dots and special characters, convert to lowercase
        $backend_name = preg_replace('/[^a-zA-Z0-9_]/', '_', strtolower($domain));
        // Don't append '_backend' here as the HAProxy service appends '-backend' itself
        // Final HAProxy backend name will be: {sanitized_domain}-backend
        return $backend_name;
    }
    
    /**
     * Store domain configuration in database
     *
     * Preserves existing SSL settings (ssl_enabled, ssl_certificate_path, ssl_last_renewal)
     * when updating backend configuration. Only updates backend_name and server list.
     *
     * @param string $domain Domain name
     * @param string $backend_name Backend name
     * @param array $servers Backend servers
     */
    private function storeDomainConfiguration($domain, $backend_name, $servers, $is_wildcard = false) {
        try {
            // Insert or update domain configuration
            // Note: ON DUPLICATE KEY UPDATE intentionally only updates backend_name, is_wildcard, and updated_at
            // to preserve SSL settings (ssl_enabled, ssl_certificate_path, ssl_last_renewal)
            $stmt = $this->db->prepare("
                INSERT INTO whp.haproxy_domains (domain_name, backend_name, is_wildcard)
                VALUES (?, ?, ?)
                ON DUPLICATE KEY UPDATE
                backend_name = VALUES(backend_name),
                is_wildcard = VALUES(is_wildcard),
                updated_at = CURRENT_TIMESTAMP
            ");
            $stmt->execute([$domain, $backend_name, $is_wildcard ? 1 : 0]);
            
            // Remove existing servers for this backend
            $stmt = $this->db->prepare("DELETE FROM whp.haproxy_backend_servers WHERE backend_name = ?");
            $stmt->execute([$backend_name]);
            
            // Insert new servers
            foreach ($servers as $server) {
                $stmt = $this->db->prepare("
                    INSERT INTO whp.haproxy_backend_servers 
                    (backend_name, server_name, server_address, server_port, server_options) 
                    VALUES (?, ?, ?, ?, ?)
                ");
                $stmt->execute([
                    $backend_name,
                    $server['name'],
                    $server['address'],
                    $server['port'],
                    $server['options'] ?? 'check'
                ]);
            }
        } catch (PDOException $e) {
            error_log("HAProxy Manager: Failed to store domain configuration: " . $e->getMessage());
        }
    }
    
    /**
     * Remove domain configuration from database
     * 
     * @param string $domain Domain name
     */
    private function removeDomainConfiguration($domain) {
        try {
            // Get backend name
            $stmt = $this->db->prepare("SELECT backend_name FROM whp.haproxy_domains WHERE domain_name = ?");
            $stmt->execute([$domain]);
            $result = $stmt->fetch();
            
            if ($result) {
                $backend_name = $result['backend_name'];
                
                // Remove servers
                $stmt = $this->db->prepare("DELETE FROM whp.haproxy_backend_servers WHERE backend_name = ?");
                $stmt->execute([$backend_name]);
                
                // Remove domain
                $stmt = $this->db->prepare("DELETE FROM whp.haproxy_domains WHERE domain_name = ?");
                $stmt->execute([$domain]);
            }
        } catch (PDOException $e) {
            error_log("HAProxy Manager: Failed to remove domain configuration: " . $e->getMessage());
        }
    }
    
    /**
     * Update SSL status in database
     * 
     * @param string $domain Domain name
     * @param bool $enabled SSL enabled status
     */
    private function updateSSLStatus($domain, $enabled) {
        try {
            $stmt = $this->db->prepare("
                UPDATE whp.haproxy_domains 
                SET ssl_enabled = ?, 
                    ssl_last_renewal = CURRENT_TIMESTAMP,
                    updated_at = CURRENT_TIMESTAMP
                WHERE domain_name = ?
            ");
            $stmt->execute([$enabled ? 1 : 0, $domain]);
        } catch (PDOException $e) {
            error_log("HAProxy Manager: Failed to update SSL status: " . $e->getMessage());
        }
    }
    
    /**
     * Get domain configuration from database
     * 
     * @param string $domain Domain name
     * @return array Domain configuration
     */
    public function getDomainConfiguration($domain) {
        try {
            $stmt = $this->db->prepare("
                SELECT * FROM whp.haproxy_domains 
                WHERE domain_name = ?
            ");
            $stmt->execute([$domain]);
            $domain_config = $stmt->fetch();
            
            if ($domain_config) {
                // Get backend servers
                $stmt = $this->db->prepare("
                    SELECT * FROM whp.haproxy_backend_servers 
                    WHERE backend_name = ?
                ");
                $stmt->execute([$domain_config['backend_name']]);
                $servers = $stmt->fetchAll();
                
                $domain_config['servers'] = $servers;
            }
            
            return $domain_config ?: [];
        } catch (PDOException $e) {
            error_log("HAProxy Manager: Failed to get domain configuration: " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Get all configured domains
     * 
     * @return array List of configured domains
     */
    public function getAllDomains() {
        try {
            $stmt = $this->db->query("SELECT * FROM whp.haproxy_domains ORDER BY domain_name");
            return $stmt->fetchAll();
        } catch (PDOException $e) {
            error_log("HAProxy Manager: Failed to get all domains: " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Configure HAProxy for a WHP site
     * 
     * @param int $site_id Site ID
     * @param array $site_data Site configuration data
     * @return array Result array with success/error status
     */
    public function configureSite($site_id, $site_data) {
        try {
            // Get site domains with FQDNs and wildcard flag
            $stmt = $this->db->prepare("
                SELECT
                    d.domain_name,
                    sd.fqdn,
                    sd.is_wildcard
                FROM whp.site_domains sd
                JOIN whp.domains d ON sd.domain_id = d.id
                WHERE sd.site_id = ?
            ");
            $stmt->execute([$site_id]);
            $site_domains = $stmt->fetchAll();

            // Check container_mode for this site
            $stmt = $this->db->prepare("SELECT container_mode FROM whp.sites WHERE id = ?");
            $stmt->execute([$site_id]);
            $container_mode = $stmt->fetchColumn() ?: 'standalone';

            // Get site containers and primary domain, including listen_port_tls
            $stmt = $this->db->prepare("
                SELECT sc.container_id, sc.container_number, ct.listen_port, ct.listen_port_tls, COALESCE(sd.fqdn, d.domain_name) as primary_domain
                FROM whp.site_containers sc
                JOIN whp.sites s ON sc.site_id = s.id
                JOIN whp.domains d ON s.primary_domain_id = d.id
                LEFT JOIN whp.site_domains sd ON s.id = sd.site_id AND sd.is_primary = 1
                JOIN whp.container_types ct ON s.container_type_id = ct.id
                WHERE sc.site_id = ? AND sc.status = 'running'
            ");
            $stmt->execute([$site_id]);
            $containers = $stmt->fetchAll();

            // Configure each FQDN
            $results = [];

            // Check if we have containers
            if (empty($containers)) {
                error_log("HAProxy Manager: No active containers found for site_id $site_id");
                return [
                    'success' => false,
                    'error' => 'No active containers found for site. Containers must be running before configuring HAProxy.'
                ];
            }

            foreach ($site_domains as $site_domain) {
                $fqdn = $site_domain['fqdn'] ?: $site_domain['domain_name']; // Use FQDN if available, fallback to domain_name
                $is_wildcard = !empty($site_domain['is_wildcard']);
                $servers = [];

                if ($container_mode === 'shared_httpd') {
                    // Shared httpd mode: route to shared-httpd instance(s) on port 443 with TLS
                    $shared_instances = $this->getSharedHttpdInstances();
                    if (empty($shared_instances)) {
                        error_log("HAProxy Manager: No running shared-httpd instances for shared_httpd site $site_id");
                        // Fall through to use FPM containers directly as a safeguard
                    } else {
                        foreach ($shared_instances as $instance_name) {
                            $servers[] = [
                                'name' => $instance_name,
                                'address' => $instance_name, // container name, DNS-resolvable on client-net
                                'port' => 443,
                                'options' => 'check ssl verify none'
                            ];
                        }
                    }
                }

                // Standalone mode (or shared_httpd fallback if no instances found)
                if (empty($servers)) {
                    foreach ($containers as $container) {
                        // Generate container name from primary domain and container number
                        // For wildcard domains, replace *. with wildcard. in the container name
                        $primary_for_container = $container['primary_domain'];
                        if (strpos($primary_for_container, '*.') === 0) {
                            $primary_for_container = 'wildcard.' . substr($primary_for_container, 2);
                        }
                        $container_name = $primary_for_container . '-' . str_pad($container['container_number'], 2, '0', STR_PAD_LEFT);
                        $options = 'check';
                        if (!empty($container['listen_port_tls']) && $container['listen_port_tls'] == 1) {
                            $options .= ' ssl verify none';
                        }
                        $servers[] = [
                            'name' => $container_name,
                            'address' => $container_name, // Use container name for client-net resolution
                            'port' => $container['listen_port'],
                            'options' => $options
                        ];
                    }
                }

                // Update FQDN configuration in HAProxy with new server configuration
                // Note: This preserves SSL settings via ON DUPLICATE KEY UPDATE in storeDomainConfiguration()
                $result = $this->addDomain($fqdn, $servers, $is_wildcard);
                $results[$fqdn] = $result;
            }

            // Regenerate HAProxy configuration to ensure changes take effect
            $regenerate_result = $this->regenerateConfiguration();
            if (!$regenerate_result['success']) {
                error_log("HAProxy Manager: Failed to regenerate configuration: " . $regenerate_result['error']);
            }

            return [
                'success' => true,
                'results' => $results,
                'regenerate_result' => $regenerate_result
            ];
        } catch (PDOException $e) {
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        }
    }

    /**
     * Get list of running shared-httpd container names
     *
     * @return array Container names
     */
    private function getSharedHttpdInstances() {
        $output = [];
        exec("docker ps --filter 'name=shared-httpd-' --filter 'status=running' --format '{{.Names}}' 2>/dev/null", $output);
        return array_filter($output);
    }
    
    /**
     * Remove HAProxy configuration for a WHP site
     * 
     * @param int $site_id Site ID
     * @return array Result array with success/error status
     */
    public function removeSite($site_id) {
        try {
            // Get site domains with FQDNs
            $stmt = $this->db->prepare("
                SELECT 
                    d.domain_name,
                    sd.fqdn
                FROM whp.site_domains sd
                JOIN whp.domains d ON sd.domain_id = d.id
                WHERE sd.site_id = ?
            ");
            $stmt->execute([$site_id]);
            $site_domains = $stmt->fetchAll();
            
            // Remove each FQDN from HAProxy
            $results = [];
            foreach ($site_domains as $site_domain) {
                $fqdn = $site_domain['fqdn'] ?: $site_domain['domain_name']; // Use FQDN if available, fallback to domain_name
                $result = $this->removeDomain($fqdn);
                $results[$fqdn] = $result;
            }
            
            return [
                'success' => true,
                'results' => $results
            ];
        } catch (PDOException $e) {
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Block an IP address in HAProxy
     * 
     * @param string $ip_address IP address to block
     * @param string $reason Reason for blocking
     * @param string $blocked_by Who initiated the block
     * @return bool True on success, false on failure
     */
    public function block_ip($ip_address, $reason = 'Manual block', $blocked_by = 'WHP System') {
        if (!$this->validate_ip_or_cidr($ip_address)) {
            error_log("HAProxy Manager: Invalid IP address or CIDR provided for blocking: $ip_address");
            return false;
        }

        // For CIDR ranges, skip the is_blocked check as it's more complex
        // Let HAProxy API handle whether it's already blocked
        if (strpos($ip_address, '/') === false) {
            // Check if IP is already blocked to avoid unnecessary API calls (single IP only)
            if ($this->is_ip_blocked($ip_address)) {
                // IP is already blocked, no need to send duplicate block command
                return true;
            }
        }

        $data = [
            'ip_address' => $ip_address,
            'reason' => $reason,
            'blocked_by' => $blocked_by
        ];

        $result = $this->makeApiRequest('POST', '/api/blocked-ips', $data);

        if ($result['success']) {
            error_log("HAProxy Manager: Successfully blocked IP/CIDR $ip_address");
            return true;
        } elseif (isset($result['data']['status']) && $result['data']['status'] === 'error' &&
                  strpos($result['data']['message'], 'already blocked') !== false) {
            // IP is already blocked, consider this a success but don't log as it's expected
            return true;
        }

        error_log("HAProxy Manager: Failed to block IP/CIDR $ip_address - " . ($result['error'] ?? 'Unknown error'));
        return false;
    }
    
    /**
     * Validates if a string is a valid IP address or CIDR notation
     *
     * @param string $input IP address or CIDR notation (e.g., "192.168.1.0/24")
     * @return bool True if valid IP or CIDR, false otherwise
     */
    private function validate_ip_or_cidr($input) {
        // Check if it's a plain IP address
        if (filter_var($input, FILTER_VALIDATE_IP)) {
            return true;
        }

        // Check if it's CIDR notation
        if (strpos($input, '/') !== false) {
            list($ip, $mask) = explode('/', $input, 2);

            // Validate IP part
            if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
                return false;
            }

            // Validate mask (0-32 for IPv4)
            if (!is_numeric($mask) || $mask < 0 || $mask > 32) {
                return false;
            }

            return true;
        }

        return false;
    }

    /**
     * Unblock an IP address or CIDR range in HAProxy
     *
     * @param string $ip_address IP address or CIDR notation to unblock
     * @return bool True on success, false on failure
     */
    public function unblock_ip($ip_address) {
        if (!$this->validate_ip_or_cidr($ip_address)) {
            error_log("HAProxy Manager: Invalid IP address or CIDR provided for unblocking: $ip_address");
            return false;
        }

        // For CIDR ranges, skip the is_blocked check as it's more complex
        // Let HAProxy API handle whether it's actually blocked
        if (strpos($ip_address, '/') === false) {
            // Check if IP is actually blocked before trying to unblock (single IP only)
            if (!$this->is_ip_blocked($ip_address)) {
                // IP is not blocked, no need to send unblock command
                return true;
            }
        }

        $data = [
            'ip_address' => $ip_address
        ];

        $result = $this->makeApiRequest('DELETE', '/api/blocked-ips', $data);

        if ($result['success']) {
            error_log("HAProxy Manager: Successfully unblocked IP/CIDR $ip_address");
            return true;
        }

        error_log("HAProxy Manager: Failed to unblock IP/CIDR $ip_address - " . ($result['error'] ?? 'Unknown error'));
        return false;
    }
    
    /**
     * Get list of blocked IPs from HAProxy
     * 
     * @return array Array of blocked IP information or empty array on failure
     */
    public function get_blocked_ips() {
        $result = $this->makeApiRequest('GET', '/api/blocked-ips');
        
        if ($result['success']) {
            return $result['data'] ?: [];
        }
        
        error_log("HAProxy Manager: Failed to get blocked IPs list - " . ($result['error'] ?? 'Unknown error'));
        return [];
    }
    
    /**
     * Check if an IP is blocked in HAProxy
     * 
     * @param string $ip_address IP address to check
     * @return bool True if blocked, false otherwise
     */
    public function is_ip_blocked($ip_address) {
        if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
            return false;
        }
        
        $blocked_ips = $this->get_blocked_ips();
        foreach ($blocked_ips as $blocked) {
            if (isset($blocked['ip_address']) && $blocked['ip_address'] === $ip_address) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Check HAProxy service health
     * 
     * @return array Health status information or false on failure
     */
    public function check_health() {
        $result = $this->makeApiRequest('GET', '/health');
        
        if ($result['success']) {
            return $result['data'];
        }
        
        return false;
    }
    
    /**
     * Safe configuration reload with validation and rollback
     * 
     * @return array Result array with success/error status
     */
    public function safe_config_reload() {
        $result = $this->makeApiRequest('POST', '/api/config/reload');
        
        if ($result['success']) {
            error_log("HAProxy Manager: Safe configuration reload completed successfully");
            return $result;
        }
        
        error_log("HAProxy Manager: Safe configuration reload failed - " . ($result['error'] ?? 'Unknown error'));
        return $result;
    }
    
    /**
     * Synchronize blocked IPs from database to HAProxy runtime map
     * 
     * @return array Result array with success/error status including sync statistics
     */
    public function sync_blocked_ips() {
        $result = $this->makeApiRequest('POST', '/api/blocked-ips/sync');

        if ($result['success']) {
            $total_ips = $result['data']['total_ips'] ?? 0;
            $synced_ips = $result['data']['synced_ips'] ?? 0;
            error_log("HAProxy Manager: Blocked IPs sync completed - $synced_ips/$total_ips IPs synced");
            return $result;
        }

        error_log("HAProxy Manager: Blocked IPs sync failed - " . ($result['error'] ?? 'Unknown error'));
        return $result;
    }

    /**
     * Request DNS-01 challenge for wildcard SSL certificate
     *
     * Initiates ACME order for a wildcard domain using DNS-01 validation.
     * Returns the challenge token that must be placed as a TXT record.
     *
     * @param string $wildcard_domain Wildcard domain (e.g., *.domain.tld)
     * @return array Result with challenge_token on success
     */
    public function requestDnsChallenge($wildcard_domain) {
        // Longer timeout: HAProxy polls up to 30s for certbot to produce token
        return $this->makeApiRequest('POST', '/api/ssl/dns-challenge/request', [
            'domain' => $wildcard_domain
        ], 60);
    }

    /**
     * Verify DNS-01 challenge and finalize wildcard SSL certificate
     *
     * Tells HAProxy to verify the TXT record and complete certificate issuance.
     *
     * @param string $base_domain Base domain (e.g., domain.tld)
     * @return array Result with success/error status
     */
    public function verifyDnsChallenge($base_domain) {
        // Longer timeout: HAProxy waits up to 120s for certbot to finish
        return $this->makeApiRequest('POST', '/api/ssl/dns-challenge/verify', [
            'domain' => $base_domain
        ], 180);
    }
}
?> 