<?php
/**
 * SSL Manager - Active Implementation
 * 
 * This class handles SSL certificate management for the WHP control panel
 * including Let's Encrypt integration and HAProxy SSL termination.
 * 
 * @version 1.0.0
 * @author WHP Development Team
 */

class ssl_manager {
    
    private $db;
    private $haproxy_manager;
    private $settings;
    
    public function __construct() {
        // Initialize database connection
        require_once('/docker/whp/web/libs/mysqlmgmt.php');
        $MySQLMgmt = new mysqlmgmt();
        $this->db = $MySQLMgmt->getMySQLConnection();
        
        // Initialize HAProxy manager
        require_once('/docker/whp/web/libs/haproxy_manager.php');
        $this->haproxy_manager = new haproxy_manager();
        
        // Load settings
        $this->loadSettings();
    }
    
    /**
     * 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 = [];
        }
    }
    
    /**
     * Enable SSL certificate for a site
     * 
     * @param int $site_id Site ID
     * @param string $certificate_type Type of certificate (letsencrypt|custom|self_signed)
     * @param array $options Additional options for certificate generation
     * @return array Result array with success/error status
     */
    public function enable_ssl($site_id, $certificate_type = 'letsencrypt', $options = []) {
        try {
            // Get all domains for this site (including primary domain and FQDNs)
            $stmt = $this->db->prepare("
                SELECT COALESCE(sd.fqdn, d.domain_name) as domain_name
                FROM whp.site_domains sd
                JOIN whp.domains d ON sd.domain_id = d.id
                WHERE sd.site_id = ?
            ");
            $stmt->execute([$site_id]);
            $domains = $stmt->fetchAll(PDO::FETCH_COLUMN);
            
            // Add primary domain if not already in list
            $stmt = $this->db->prepare("
                SELECT d.domain_name 
                FROM whp.sites s
                JOIN whp.domains d ON s.primary_domain_id = d.id
                WHERE s.id = ?
            ");
            $stmt->execute([$site_id]);
            $primary_domain = $stmt->fetchColumn();
            
            if ($primary_domain && !in_array($primary_domain, $domains)) {
                $domains[] = $primary_domain;
            }
            
            // Remove any duplicates from the domains array
            $domains = array_unique($domains);
            
            if (empty($domains)) {
                return [
                    'success' => false,
                    'error' => 'No domains found for site'
                ];
            }
            
            $results = [];
            
            // Log the domains being processed
            error_log("SSL Manager: Processing SSL for site $site_id with domains: " . implode(', ', $domains));
            
            // Process each domain
            foreach ($domains as $domain) {
                error_log("SSL Manager: Enabling SSL for domain: $domain");
                
                switch ($certificate_type) {
                    case 'letsencrypt':
                        $result = $this->enableLetsEncryptSSL($domain, $options);
                        break;
                    case 'custom':
                        $result = $this->enableCustomSSL($domain, $options);
                        break;
                    case 'self_signed':
                        $result = $this->enableSelfSignedSSL($domain, $options);
                        break;
                    default:
                        $result = [
                            'success' => false,
                            'error' => 'Invalid certificate type'
                        ];
                }
                
                error_log("SSL Manager: Result for domain $domain: " . ($result['success'] ? 'SUCCESS' : 'FAILED - ' . ($result['error'] ?? 'Unknown error')));
                $results[$domain] = $result;
            }
            
            // Update site SSL status
            $this->updateSiteSSLStatus($site_id, $certificate_type);
            
            return [
                'success' => true,
                'results' => $results
            ];
        } catch (PDOException $e) {
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Renew SSL certificate for a site
     * 
     * @param int $site_id Site ID
     * @return array Result array with success/error status
     */
    public function renew_ssl($site_id) {
        try {
            // Get all domains for this site (including primary domain and FQDNs)
            $stmt = $this->db->prepare("
                SELECT COALESCE(sd.fqdn, d.domain_name) as domain_name
                FROM whp.site_domains sd
                JOIN whp.domains d ON sd.domain_id = d.id
                WHERE sd.site_id = ?
            ");
            $stmt->execute([$site_id]);
            $domains = $stmt->fetchAll(PDO::FETCH_COLUMN);
            
            // Add primary domain if not already in list
            $stmt = $this->db->prepare("
                SELECT d.domain_name 
                FROM whp.sites s
                JOIN whp.domains d ON s.primary_domain_id = d.id
                WHERE s.id = ?
            ");
            $stmt->execute([$site_id]);
            $primary_domain = $stmt->fetchColumn();
            
            if ($primary_domain && !in_array($primary_domain, $domains)) {
                $domains[] = $primary_domain;
            }
            
            // Remove any duplicates from the domains array
            $domains = array_unique($domains);
            
            $results = [];
            
            // Renew SSL for each domain
            foreach ($domains as $domain) {
                $result = $this->renewDomainSSL($domain);
                $results[$domain] = $result;
            }
            
            return [
                'success' => true,
                'results' => $results
            ];
        } catch (PDOException $e) {
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Get SSL certificate status for a site
     * 
     * @param int $site_id Site ID
     * @return array SSL certificate information
     */
    public function get_ssl_status($site_id) {
        try {
            // Get site SSL configuration
            $stmt = $this->db->prepare("
                SELECT ssl_enabled, ssl_certificate_path, ssl_key_path, ssl_last_renewal
                FROM whp.sites 
                WHERE id = ?
            ");
            $stmt->execute([$site_id]);
            $site_ssl = $stmt->fetch();
            
            if (!$site_ssl) {
                return [
                    'ssl_enabled' => false,
                    'certificate_type' => null,
                    'expires_date' => null,
                    'auto_renew' => false,
                    'status' => 'not_configured'
                ];
            }
            
            // Get domain SSL information (including primary domain and FQDNs)
            $stmt = $this->db->prepare("
                SELECT COALESCE(sd.fqdn, d.domain_name) as domain_name, hd.ssl_enabled, hd.ssl_last_renewal
                FROM whp.site_domains sd
                JOIN whp.domains d ON sd.domain_id = d.id
                LEFT JOIN whp.haproxy_domains hd ON COALESCE(sd.fqdn, d.domain_name) = hd.domain_name
                WHERE sd.site_id = ?
            ");
            $stmt->execute([$site_id]);
            $domain_ssl = $stmt->fetchAll();
            
            // Add primary domain SSL information
            $stmt = $this->db->prepare("
                SELECT d.domain_name, hd.ssl_enabled, hd.ssl_last_renewal
                FROM whp.sites s
                JOIN whp.domains d ON s.primary_domain_id = d.id
                LEFT JOIN whp.haproxy_domains hd ON d.domain_name = hd.domain_name
                WHERE s.id = ?
            ");
            $stmt->execute([$site_id]);
            $primary_domain_ssl = $stmt->fetch();
            
            // Add primary domain if not already in list
            if ($primary_domain_ssl) {
                $primary_domain_exists = false;
                foreach ($domain_ssl as $domain) {
                    if ($domain['domain_name'] === $primary_domain_ssl['domain_name']) {
                        $primary_domain_exists = true;
                        break;
                    }
                }
                if (!$primary_domain_exists) {
                    $domain_ssl[] = $primary_domain_ssl;
                }
            }
            
            return [
                'ssl_enabled' => (bool)$site_ssl['ssl_enabled'],
                'certificate_type' => $this->getCertificateType($site_ssl['ssl_enabled']),
                'expires_date' => $this->getExpirationDate($site_ssl['ssl_certificate_path']),
                'auto_renew' => true, // Always true for Let's Encrypt
                'status' => $site_ssl['ssl_enabled'] ? 'active' : 'disabled',
                'domains' => $domain_ssl,
                'last_renewal' => $site_ssl['ssl_last_renewal']
            ];
        } catch (PDOException $e) {
            return [
                'ssl_enabled' => false,
                'certificate_type' => null,
                'expires_date' => null,
                'auto_renew' => false,
                'status' => 'error',
                'error' => $e->getMessage()
            ];
        }
    }
    
    /**
     * List all SSL certificates that need renewal
     * 
     * @param int $days_before_expiry Number of days before expiry to consider for renewal
     * @return array Array of certificates that need renewal
     */
    public function get_certificates_for_renewal($days_before_expiry = 30) {
        try {
            // Get all domains with SSL enabled
            $stmt = $this->db->query("
                SELECT domain_name, ssl_last_renewal
                FROM whp.haproxy_domains 
                WHERE ssl_enabled = 1
            ");
            $ssl_domains = $stmt->fetchAll();
            
            $renewal_candidates = [];
            
            foreach ($ssl_domains as $domain) {
                $last_renewal = strtotime($domain['ssl_last_renewal']);
                $days_since_renewal = (time() - $last_renewal) / (24 * 60 * 60);
                
                // Let's Encrypt certificates are valid for 90 days, renew at 60 days
                if ($days_since_renewal >= 60) {
                    $renewal_candidates[] = $domain['domain_name'];
                }
            }
            
            return $renewal_candidates;
        } catch (PDOException $e) {
            error_log("SSL Manager: Failed to get certificates for renewal: " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Enable Let's Encrypt SSL certificate
     * 
     * @param string $domain Domain name
     * @param array $options Additional options
     * @return array Result array with success/error status
     */
    private function enableLetsEncryptSSL($domain, $options = []) {
        // Use HAProxy Manager to enable SSL (it handles Let's Encrypt)
        $result = $this->haproxy_manager->enableSSL($domain);
        
        if ($result['success']) {
            // Log the SSL enable action
            $this->logSSLRenewal($domain, 'success', 'Let\'s Encrypt SSL enabled');
        } else {
            // Log the SSL enable failure
            $this->logSSLRenewal($domain, 'failed', $result['error'] ?? 'Unknown error');
        }
        
        return $result;
    }
    
    /**
     * Enable custom SSL certificate
     * 
     * @param string $domain Domain name
     * @param array $options Certificate options (cert_content, key_content, chain_content)
     * @return array Result array with success/error status
     */
    private function enableCustomSSL($domain, $options = []) {
        // Custom SSL certificates would need to be handled by the HAProxy Manager
        // For now, return not implemented
        return [
            'success' => false,
            'error' => 'Custom SSL certificates not yet implemented'
        ];
    }
    
    /**
     * Enable self-signed SSL certificate
     * 
     * @param string $domain Domain name
     * @param array $options Certificate options
     * @return array Result array with success/error status
     */
    private function enableSelfSignedSSL($domain, $options = []) {
        // Self-signed certificates would need to be generated and uploaded to HAProxy
        // For now, return not implemented
        return [
            'success' => false,
            'error' => 'Self-signed SSL certificates not yet implemented'
        ];
    }
    
    /**
     * Renew SSL certificate for a domain
     * 
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    private function renewDomainSSL($domain) {
        // For wildcard domains, use certbot renew (reuses original DNS-01 hooks)
        // instead of enableSSL which runs certbot certonly --standalone (HTTP-01)
        if (strpos($domain, '*.') === 0) {
            $result = $this->haproxy_manager->renewCertificate($domain);
        } else {
            // Use HAProxy Manager to renew SSL (it handles Let's Encrypt renewal)
            $result = $this->haproxy_manager->enableSSL($domain);
        }

        if ($result['success']) {
            // Log the SSL renewal
            $this->logSSLRenewal($domain, 'success', 'SSL certificate renewed');
        } else {
            // Log the SSL renewal failure
            $this->logSSLRenewal($domain, 'failed', $result['error'] ?? 'Unknown error');
        }

        return $result;
    }
    
    /**
     * Update site SSL status in database
     * 
     * @param int $site_id Site ID
     * @param string $certificate_type Certificate type (letsencrypt, custom, self_signed)
     */
    private function updateSiteSSLStatus($site_id, $certificate_type) {
        try {
            // Convert certificate type to integer value
            $ssl_enabled = 0;
            switch ($certificate_type) {
                case 'letsencrypt':
                    $ssl_enabled = 1;
                    break;
                case 'custom':
                    $ssl_enabled = 2;
                    break;
                case 'self_signed':
                    $ssl_enabled = 3;
                    break;
            }
            
            $stmt = $this->db->prepare("
                UPDATE whp.sites 
                SET ssl_enabled = ?,
                    ssl_last_renewal = CURRENT_TIMESTAMP,
                    updated_at = CURRENT_TIMESTAMP
                WHERE id = ?
            ");
            $stmt->execute([$ssl_enabled, $site_id]);
        } catch (PDOException $e) {
            error_log("SSL Manager: Failed to update site SSL status: " . $e->getMessage());
        }
    }
    
    /**
     * Log SSL renewal attempt
     * 
     * @param string $domain Domain name
     * @param string $status Status (success, failed, pending)
     * @param string $message Log message
     */
    private function logSSLRenewal($domain, $status, $message = '') {
        try {
            $stmt = $this->db->prepare("
                INSERT INTO whp.ssl_renewal_log 
                (domain_name, renewal_status, error_message) 
                VALUES (?, ?, ?)
            ");
            $stmt->execute([$domain, $status, $message]);
        } catch (PDOException $e) {
            error_log("SSL Manager: Failed to log SSL renewal: " . $e->getMessage());
        }
    }
    
    /**
     * Get certificate type from SSL enabled value
     * 
     * @param int $ssl_enabled SSL enabled value
     * @return string Certificate type
     */
    private function getCertificateType($ssl_enabled) {
        switch ($ssl_enabled) {
            case 1:
                return 'letsencrypt';
            case 2:
                return 'custom';
            case 3:
                return 'self_signed';
            default:
                return null;
        }
    }
    
    /**
     * Get certificate expiration date
     * 
     * @param string $certificate_path Path to certificate file
     * @return string|null Expiration date
     */
    private function getExpirationDate($certificate_path) {
        if (!$certificate_path || !file_exists($certificate_path)) {
            return null;
        }
        
        $cert_info = openssl_x509_parse(file_get_contents($certificate_path));
        if ($cert_info && isset($cert_info['validTo_time_t'])) {
            return date('Y-m-d H:i:s', $cert_info['validTo_time_t']);
        }
        
        return null;
    }
    
    /**
     * Validate domain name format (RFC 1123)
     *
     * @param string $domain Domain name to validate
     * @return bool True if valid
     */
    private function isValidDomain($domain) {
        // RFC 1123 hostname validation
        $pattern = '/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i';
        return (bool)preg_match($pattern, $domain) && strlen($domain) <= 253 && strlen($domain) > 0;
    }

    /**
     * Validate service name against whitelist
     *
     * @param string $service_name Service name to validate
     * @return bool True if valid
     */
    private function isValidServiceName($service_name) {
        $allowed_services = ['httpd', 'proftpd'];
        return in_array($service_name, $allowed_services, true);
    }

    /**
     * Enable SSL for system services
     *
     * @param string $service_name Service name (httpd, proftpd, etc.)
     * @param string $domain Domain name for certificate
     * @return array Result array with success/error status
     */
    public function enable_system_service_ssl($service_name, $domain) {
        try {
            // Validate inputs
            if (!$this->isValidServiceName($service_name)) {
                return [
                    'success' => false,
                    'error' => 'Invalid service name'
                ];
            }

            if (!$this->isValidDomain($domain)) {
                return [
                    'success' => false,
                    'error' => 'Invalid domain name format'
                ];
            }

            error_log("SSL Manager: Starting SSL enable for system service: {$service_name} with domain: {$domain}");
            
            // Request SSL certificate from HAProxy Manager
            $result = $this->haproxy_manager->enableSSL($domain);
            
            error_log("SSL Manager: enableSSL result for {$service_name}: " . json_encode($result));
            
            if ($result['success']) {
                error_log("SSL Manager: SSL enabled successfully, attempting to copy certificate from container for {$domain}");
                
                // Create certificate directory if it doesn't exist
                $cert_dir = "/etc/certs";
                if (!is_dir($cert_dir)) {
                    if (!mkdir($cert_dir, 0755, true)) {
                        return [
                            'success' => false,
                            'error' => 'Failed to create certificate directory: ' . $cert_dir
                        ];
                    }
                }
                
                // Copy certificate from HAProxy container
                $container_cert_path = "/etc/haproxy/certs/{$domain}.pem";
                $host_cert_path = "{$cert_dir}/{$domain}.pem";
                
                // Wait a moment for the certificate to be generated
                sleep(2);
                
                // Check if certificate exists in container
                $safe_container_path = escapeshellarg($container_cert_path);
                $check_cmd = "docker exec haproxy-manager test -f " . $safe_container_path;
                exec($check_cmd . " 2>&1", $output, $return_code);

                if ($return_code !== 0) {
                    error_log("SSL Manager: Certificate not found in container at {$container_cert_path} (exit code: {$return_code})");
                    return [
                        'success' => false,
                        'error' => 'Certificate not found in HAProxy container. The certificate may still be generating or there was an issue with the SSL request.'
                    ];
                }

                // Copy certificate from container to host
                $safe_host_path = escapeshellarg($host_cert_path);
                $copy_cmd = "docker cp haproxy-manager:" . $safe_container_path . " " . $safe_host_path;
                exec($copy_cmd . " 2>&1", $copy_output, $copy_return);

                if ($copy_return !== 0) {
                    $error_msg = implode("\n", $copy_output);
                    error_log("SSL Manager: Error copying certificate: " . $error_msg);
                    return [
                        'success' => false,
                        'error' => 'Failed to copy certificate from container'
                    ];
                }
                
                // Verify the certificate file was copied successfully
                if (!file_exists($host_cert_path)) {
                    return [
                        'success' => false,
                        'error' => 'Certificate file was not copied successfully to: ' . $host_cert_path
                    ];
                }
                
                // Set proper permissions
                chmod($host_cert_path, 0600);
                
                error_log("SSL Manager: Successfully copied certificate from container to {$host_cert_path}");
                
                // When saving system service SSL, both certificate_path and private_key_path are set to the same PEM file.
                $stmt = $this->db->prepare("
                    INSERT INTO whp.system_ssl_certificates 
                    (service_name, domain_name, certificate_path, private_key_path, ssl_enabled) 
                    VALUES (?, ?, ?, ?, 1)
                    ON DUPLICATE KEY UPDATE
                    ssl_enabled = 1,
                    last_renewal = CURRENT_TIMESTAMP,
                    certificate_path = VALUES(certificate_path),
                    private_key_path = VALUES(private_key_path)
                ");
                
                $stmt->execute([$service_name, $domain, $host_cert_path, $host_cert_path]);
                
                // Configure the service to use the SSL certificate
                $this->configureServiceSSL($service_name, $domain, $host_cert_path);
                
                return [
                    'success' => true,
                    'message' => "SSL enabled for {$service_name} with domain {$domain}"
                ];
            } else {
                return $result;
            }
        } catch (PDOException $e) {
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Configure system service to use SSL certificate
     * 
     * @param string $service_name Service name
     * @param string $domain Domain name
     * @param string $pem_path Combined PEM file path (certificate + private key)
     */
    private function configureServiceSSL($service_name, $domain, $pem_path) {
        switch ($service_name) {
            case 'httpd':
                $this->configureApacheSSL($domain, $pem_path);
                break;
            case 'proftpd':
                $this->configureProFTPDSSL($domain, $pem_path);
                break;
            default:
                error_log("SSL Manager: Unknown service: {$service_name}");
        }
    }
    
    /**
     * Configure Apache to use SSL certificate
     * 
     * @param string $domain Domain name
     * @param string $pem_path Combined PEM file path
     */
    private function configureApacheSSL($domain, $pem_path) {
        $ssl_conf = "
Listen 8443 https
<VirtualHost *:8443>
    ServerName {$domain}
    DocumentRoot /docker/whp/web
    SSLEngine on
    SSLCertificateFile {$pem_path}
    <FilesMatch \".(cgi|shtml|phtml|php)$\">
        SSLOptions +StdEnvVars
    </FilesMatch>
    
    BrowserMatch \"MSIE [2-5]\" \\
             nokeepalive ssl-unclean-shutdown \\
             downgrade-1.0 force-response-1.0
             
    CustomLog logs/ssl_request_log \\
              \"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \\\"%r\\\" %b\"
</VirtualHost>
SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog
SSLSessionCache         shmcb:/run/httpd/sslcache(512000)
SSLSessionCacheTimeout  300
SSLCryptoDevice builtin
";
        
        file_put_contents("/etc/httpd/conf.d/{$domain}-ssl.conf", $ssl_conf);
        //Remove the default ssl.conf if it exists
        if (file_exists("/etc/httpd/conf.d/ssl.conf")) {
            unlink("/etc/httpd/conf.d/ssl.conf");
        }

        // Restart Apache
        shell_exec('systemctl restart httpd');
    }
    
    /**
     * Configure ProFTPD to use SSL certificate
     * 
     * @param string $domain Domain name
     * @param string $pem_path Combined PEM file path
     */
    private function configureProFTPDSSL($domain, $pem_path) {
        // Update ProFTPD configuration to use SSL certificate
        $proftpd_ssl = "
<IfModule mod_tls.c>
    TLSEngine on
    TLSRequired on
    TLSECCertificateFile {$pem_path}
    TLSVerifyClient off
    TLSLog /var/log/proftpd/tls.log
    TLSProtocol TLSv1.2 TLSv1.3
</IfModule>
";
        
        file_put_contents("/etc/proftpd/conf.d/{$domain}-ssl.conf", $proftpd_ssl);
        //Remove the default mod_tls.conf if it exists
        if (file_exists("/etc/proftpd/mod_tls.conf")) {
            unlink("/etc/proftpd/mod_tls.conf");
        }
        // Restart ProFTPD
        shell_exec('systemctl restart proftpd');
    }
    
    /**
     * Get SSL renewal log for a domain
     * 
     * @param string $domain Domain name
     * @param int $limit Number of log entries to retrieve
     * @return array SSL renewal log entries
     */
    public function getSSLRenewalLog($domain, $limit = 50) {
        try {
            $stmt = $this->db->prepare("
                SELECT * FROM whp.ssl_renewal_log 
                WHERE domain_name = ? 
                ORDER BY renewal_date DESC 
                LIMIT ?
            ");
            $stmt->execute([$domain, $limit]);
            return $stmt->fetchAll();
        } catch (PDOException $e) {
            error_log("SSL Manager: Failed to get SSL renewal log: " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Split combined certificate file into separate certificate and private key
     * 
     * @param string $cert_data Combined certificate data containing both cert and key
     * @return array|false Array with 'certificate' and 'private_key' keys, or false on failure
     */
    private function splitCertificateFile($cert_data) {
        // Split the data by PEM boundaries
        $parts = [];
        $current_part = '';
        $current_type = '';
        
        $lines = explode("\n", $cert_data);
        
        foreach ($lines as $line) {
            $line = trim($line);
            
            if (empty($line)) {
                continue;
            }
            
            // Check for PEM headers
            if (strpos($line, '-----BEGIN ') === 0) {
                // Save previous part if exists
                if (!empty($current_part) && !empty($current_type)) {
                    $parts[$current_type] = trim($current_part);
                }
                
                // Start new part
                $current_part = $line . "\n";
                
                if (strpos($line, 'CERTIFICATE') !== false) {
                    $current_type = 'certificate';
                } elseif (strpos($line, 'PRIVATE KEY') !== false) {
                    $current_type = 'private_key';
                } else {
                    $current_type = 'unknown';
                }
            } elseif (strpos($line, '-----END ') === 0) {
                // End of current part
                $current_part .= $line . "\n";
                
                if (!empty($current_type) && $current_type !== 'unknown') {
                    $parts[$current_type] = trim($current_part);
                }
                
                $current_part = '';
                $current_type = '';
            } else {
                // Add line to current part
                $current_part .= $line . "\n";
            }
        }
        
        // Save final part if exists
        if (!empty($current_part) && !empty($current_type) && $current_type !== 'unknown') {
            $parts[$current_type] = trim($current_part);
        }
        
        // Verify we have both certificate and private key
        if (isset($parts['certificate']) && isset($parts['private_key'])) {
            return [
                'certificate' => $parts['certificate'],
                'private_key' => $parts['private_key']
            ];
        }
        
        return false;
    }
    
    /**
     * Renew SSL certificate for a system service
     * 
     * @param string $service_name Service name
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function renew_system_service_ssl($service_name, $domain) {
        try {
            // Validate inputs
            if (!$this->isValidServiceName($service_name)) {
                return [
                    'success' => false,
                    'error' => 'Invalid service name'
                ];
            }

            if (!$this->isValidDomain($domain)) {
                return [
                    'success' => false,
                    'error' => 'Invalid domain name format'
                ];
            }

            // Request SSL certificate renewal from HAProxy Manager
            $result = $this->haproxy_manager->renewCertificate($domain);
            
            if ($result['success']) {
                error_log("SSL Manager: SSL renewal successful, copying renewed certificate from container for {$domain}");
                
                // Create certificate directory if it doesn't exist
                $cert_dir = "/etc/certs";
                if (!is_dir($cert_dir)) {
                    if (!mkdir($cert_dir, 0755, true)) {
                        return [
                            'success' => false,
                            'error' => 'Failed to create certificate directory: ' . $cert_dir
                        ];
                    }
                }
                
                // Copy renewed certificate from HAProxy container
                $container_cert_path = "/etc/haproxy/certs/{$domain}.pem";
                $host_cert_path = "{$cert_dir}/{$domain}.pem";
                
                // Wait a moment for the certificate to be renewed
                sleep(2);
                
                // Check if certificate exists in container
                $safe_container_path = escapeshellarg($container_cert_path);
                $check_cmd = "docker exec haproxy-manager test -f " . $safe_container_path;
                exec($check_cmd . " 2>&1", $output, $return_code);

                if ($return_code !== 0) {
                    error_log("SSL Manager: Renewed certificate not found in container at {$container_cert_path} (exit code: {$return_code})");
                    return [
                        'success' => false,
                        'error' => 'Renewed certificate not found in HAProxy container. The certificate may still be renewing or there was an issue with the renewal request.'
                    ];
                }

                // Copy certificate from container to host
                $safe_host_path = escapeshellarg($host_cert_path);
                $copy_cmd = "docker cp haproxy-manager:" . $safe_container_path . " " . $safe_host_path;
                exec($copy_cmd . " 2>&1", $copy_output, $copy_return);

                if ($copy_return !== 0) {
                    $error_msg = implode("\n", $copy_output);
                    error_log("SSL Manager: Error copying renewed certificate: " . $error_msg);
                    return [
                        'success' => false,
                        'error' => 'Failed to copy renewed certificate from container'
                    ];
                }
                
                // Verify the certificate file was copied successfully
                if (!file_exists($host_cert_path)) {
                    return [
                        'success' => false,
                        'error' => 'Renewed certificate file was not copied successfully to: ' . $host_cert_path
                    ];
                }
                
                // Set proper permissions
                chmod($host_cert_path, 0600);
                
                error_log("SSL Manager: Successfully copied renewed certificate from container to {$host_cert_path}");
                
                $cert_path = $host_cert_path;
                // Update system service SSL configuration
                $stmt = $this->db->prepare("
                    UPDATE whp.system_ssl_certificates 
                    SET last_renewal = CURRENT_TIMESTAMP,
                        certificate_path = ?,
                        private_key_path = ?
                    WHERE service_name = ? AND domain_name = ?
                ");
                $stmt->execute([$cert_path, $cert_path, $service_name, $domain]);
                // Restart the service to use the new certificate
                $this->restartService($service_name);
                
                return [
                    'success' => true,
                    'message' => "SSL certificate renewed for {$service_name} with domain {$domain}"
                ];
            } else {
                return $result;
            }
        } catch (PDOException $e) {
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Restart a system service
     *
     * @param string $service_name Service name
     * @return bool True if successful
     */
    private function restartService($service_name) {
        // Strict whitelist validation
        if (!$this->isValidServiceName($service_name)) {
            $safe_name = preg_replace('/[^a-zA-Z0-9_-]/', '', $service_name);
            error_log("SSL Manager: Invalid service name attempted: " . $safe_name);
            return false;
        }

        // Execute restart with error checking
        exec('systemctl restart ' . escapeshellarg($service_name) . ' 2>&1', $output, $return_code);

        if ($return_code !== 0) {
            error_log("SSL Manager: Failed to restart {$service_name}: " . implode("\n", $output));
            return false;
        }

        error_log("SSL Manager: Successfully restarted {$service_name}");
        return true;
    }
    
    /**
     * Get SSL certificate information for a system service
     * 
     * @param string $service_name Service name
     * @param string $domain Domain name
     * @return array Certificate information
     */
    public function getSystemServiceSSLCertificateInfo($service_name, $domain) {
        try {
            $stmt = $this->db->prepare("
                SELECT * FROM whp.system_ssl_certificates 
                WHERE service_name = ? AND domain_name = ?
            ");
            $stmt->execute([$service_name, $domain]);
            $cert_info = $stmt->fetch();
            
            if ($cert_info) {
                // Add certificate expiration information
                if (!empty($cert_info['certificate_path']) && file_exists($cert_info['certificate_path'])) {
                    $cert_info['expiration_date'] = $this->getExpirationDate($cert_info['certificate_path']);
                    $cert_info['days_until_expiry'] = $this->getDaysUntilExpiry($cert_info['certificate_path']);
                } else {
                    $cert_info['expiration_date'] = null;
                    $cert_info['days_until_expiry'] = null;
                }
            }
            
            return $cert_info;
        } catch (PDOException $e) {
            error_log("SSL Manager: Failed to get system service SSL certificate info: " . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Get days until certificate expiry
     * 
     * @param string $certificate_path Path to certificate file
     * @return int|null Days until expiry (negative if expired)
     */
    private function getDaysUntilExpiry($certificate_path) {
        if (!$certificate_path || !file_exists($certificate_path)) {
            return null;
        }
        
        $cert_info = openssl_x509_parse(file_get_contents($certificate_path));
        if ($cert_info && isset($cert_info['validTo_time_t'])) {
            $expiry_timestamp = $cert_info['validTo_time_t'];
            $current_timestamp = time();
            $days_until_expiry = floor(($expiry_timestamp - $current_timestamp) / 86400);
            return $days_until_expiry;
        }
        
        return null;
    }
    
    /**
     * Disable SSL for a system service
     * 
     * @param string $service_name Service name
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function disable_system_service_ssl($service_name, $domain) {
        try {
            // Update database to mark SSL as disabled
            $stmt = $this->db->prepare("
                UPDATE whp.system_ssl_certificates 
                SET ssl_enabled = 0,
                    updated_at = CURRENT_TIMESTAMP
                WHERE service_name = ? AND domain_name = ?
            ");
            $stmt->execute([$service_name, $domain]);
            
            // Remove SSL configuration files
            $this->removeServiceSSLConfiguration($service_name, $domain);
            
            // Restart the service to apply changes
            $this->restartService($service_name);
            
            return [
                'success' => true,
                'message' => "SSL disabled for {$service_name} with domain {$domain}"
            ];
        } catch (PDOException $e) {
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Remove SSL configuration for a service
     * 
     * @param string $service_name Service name
     * @param string $domain Domain name
     */
    private function removeServiceSSLConfiguration($service_name, $domain) {
        switch ($service_name) {
            case 'httpd':
                $ssl_conf_file = "/etc/httpd/conf.d/{$domain}-ssl.conf";
                if (file_exists($ssl_conf_file)) {
                    unlink($ssl_conf_file);
                }
                break;
            case 'proftpd':
                $ssl_conf_file = "/etc/proftpd/conf.d/{$domain}-ssl.conf";
                if (file_exists($ssl_conf_file)) {
                    unlink($ssl_conf_file);
                }
                break;
            default:
                error_log("SSL Manager: Unknown service for SSL removal: {$service_name}");
        }
    }
    
    /**
     * Get all system service SSL certificates
     *
     * @return array System service SSL certificates
     */
    public function getSystemServiceSSLCertificates() {
        try {
            $stmt = $this->db->query("
                SELECT * FROM whp.system_ssl_certificates
                ORDER BY service_name, domain_name
            ");
            $certificates = $stmt->fetchAll();

            // Add expiration information for each certificate
            foreach ($certificates as &$cert) {
                if (!empty($cert['certificate_path']) && file_exists($cert['certificate_path'])) {
                    $cert['expiration_date'] = $this->getExpirationDate($cert['certificate_path']);
                    $cert['days_until_expiry'] = $this->getDaysUntilExpiry($cert['certificate_path']);
                } else {
                    $cert['expiration_date'] = null;
                    $cert['days_until_expiry'] = null;
                }
            }

            return $certificates;
        } catch (PDOException $e) {
            error_log("SSL Manager: Failed to get system service SSL certificates: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Refresh system service SSL certificate from HAProxy
     *
     * This method copies the existing certificate from HAProxy container to the host
     * and restarts the system service. It does NOT trigger a certificate renewal.
     * HAProxy automatically renews Let's Encrypt certificates - this just syncs them.
     *
     * @param string $service_name Service name
     * @param string $domain Domain name
     * @return array Result array with success/error status
     */
    public function refreshSystemServiceSSL($service_name, $domain) {
        try {
            // Validate inputs
            if (!$this->isValidServiceName($service_name)) {
                return [
                    'success' => false,
                    'error' => 'Invalid service name'
                ];
            }

            if (!$this->isValidDomain($domain)) {
                return [
                    'success' => false,
                    'error' => 'Invalid domain name format'
                ];
            }

            error_log("SSL Manager: Refreshing SSL for system service: {$service_name} with domain: {$domain}");

            // Check if this service/domain is configured
            $stmt = $this->db->prepare("
                SELECT * FROM whp.system_ssl_certificates
                WHERE service_name = ? AND domain_name = ? AND ssl_enabled = 1
            ");
            $stmt->execute([$service_name, $domain]);
            $cert_record = $stmt->fetch();

            if (!$cert_record) {
                return [
                    'success' => false,
                    'error' => "No active SSL configuration found for {$service_name} - {$domain}"
                ];
            }

            // Request HAProxy to regenerate the combined PEM file for this domain
            // This doesn't trigger Let's Encrypt renewal, just recreates the combined cert+key file
            error_log("SSL Manager: Requesting certificate file regeneration from HAProxy for {$domain}");
            $result = $this->haproxy_manager->requestCertificate($domain, false, false);

            if (!$result['success']) {
                error_log("SSL Manager: HAProxy certificate request failed: " . ($result['error'] ?? 'Unknown error'));
                // If request failed, try to copy existing certificate anyway
                error_log("SSL Manager: Attempting to copy existing certificate anyway for {$domain}");
            } else {
                error_log("SSL Manager: HAProxy certificate file regenerated successfully for {$domain}");
                // Wait briefly for file to be written
                sleep(1);
            }

            // Create certificate directory if it doesn't exist
            $cert_dir = "/etc/certs";
            if (!is_dir($cert_dir)) {
                if (!mkdir($cert_dir, 0755, true)) {
                    return [
                        'success' => false,
                        'error' => 'Failed to create certificate directory: ' . $cert_dir
                    ];
                }
            }

            // Copy certificate from HAProxy container
            $container_cert_path = "/etc/haproxy/certs/{$domain}.pem";
            $host_cert_path = "{$cert_dir}/{$domain}.pem";

            error_log("SSL Manager: Copying certificate from HAProxy container for {$domain}");

            // Check if certificate exists in container
            $safe_container_path = escapeshellarg($container_cert_path);
            $check_cmd = "docker exec haproxy-manager test -f " . $safe_container_path;
            exec($check_cmd . " 2>&1", $output, $return_code);

            if ($return_code !== 0) {
                error_log("SSL Manager: Certificate not found in container at {$container_cert_path}");
                return [
                    'success' => false,
                    'error' => 'Certificate not found in HAProxy container'
                ];
            }

            // Copy certificate from container to host
            $safe_host_path = escapeshellarg($host_cert_path);
            $copy_cmd = "docker cp haproxy-manager:" . $safe_container_path . " " . $safe_host_path;
            exec($copy_cmd . " 2>&1", $copy_output, $copy_return);

            if ($copy_return !== 0) {
                $error_msg = implode("\n", $copy_output);
                error_log("SSL Manager: Error copying certificate: " . $error_msg);
                return [
                    'success' => false,
                    'error' => 'Failed to copy certificate from container'
                ];
            }

            // Verify the certificate file was copied successfully
            if (!file_exists($host_cert_path)) {
                return [
                    'success' => false,
                    'error' => 'Certificate file was not copied successfully to: ' . $host_cert_path
                ];
            }

            // Set proper permissions
            chmod($host_cert_path, 0600);

            error_log("SSL Manager: Successfully copied certificate to {$host_cert_path}");

            // Update database record
            $stmt = $this->db->prepare("
                UPDATE whp.system_ssl_certificates
                SET last_renewal = CURRENT_TIMESTAMP,
                    certificate_path = ?,
                    private_key_path = ?
                WHERE service_name = ? AND domain_name = ?
            ");
            $stmt->execute([$host_cert_path, $host_cert_path, $service_name, $domain]);

            // Restart the service to use the new certificate
            error_log("SSL Manager: Restarting {$service_name} to apply refreshed certificate");
            $this->restartService($service_name);

            return [
                'success' => true,
                'message' => "SSL certificate refreshed and {$service_name} restarted for {$domain}"
            ];

        } catch (PDOException $e) {
            error_log("SSL Manager: Database error during refresh: " . $e->getMessage());
            return [
                'success' => false,
                'error' => 'Database error: ' . $e->getMessage()
            ];
        } catch (Exception $e) {
            error_log("SSL Manager: Error during refresh: " . $e->getMessage());
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Enable wildcard SSL certificate using DNS-01 challenge
     *
     * Initiates the ACME DNS-01 challenge flow for a wildcard domain.
     * The challenge TXT record is created in PowerDNS automatically.
     * A cron job will verify and complete the challenge.
     *
     * @param int $site_id Site ID
     * @param string $wildcard_fqdn Wildcard FQDN (e.g., *.domain.tld)
     * @return array Result with challenge info on success
     */
    public function enable_wildcard_ssl($site_id, $wildcard_fqdn) {
        try {
            // Extract base domain from *.domain.tld
            if (strpos($wildcard_fqdn, '*.') !== 0) {
                error_log("SSL Manager: enable_wildcard_ssl called with non-wildcard FQDN: $wildcard_fqdn");
                return ['success' => false, 'error' => 'Not a wildcard FQDN'];
            }
            $base_domain = substr($wildcard_fqdn, 2);

            error_log("SSL Manager: Requesting DNS-01 challenge for $wildcard_fqdn (base: $base_domain)");

            // Request DNS challenge from HAProxy manager
            $challenge_result = $this->haproxy_manager->requestDnsChallenge($wildcard_fqdn);

            error_log("SSL Manager: DNS challenge request result: " . json_encode($challenge_result));

            if (!$challenge_result['success']) {
                $error_msg = 'Failed to initiate DNS challenge: ' . ($challenge_result['error'] ?? 'Unknown error');
                error_log("SSL Manager: $error_msg");
                return [
                    'success' => false,
                    'error' => $error_msg
                ];
            }

            // HAProxy response is nested: makeApiRequest wraps the HAProxy JSON response in 'data'
            // HAProxy returns: {"success": true, "data": {"challenge_token": "xxx", "base_domain": "yyy"}}
            // makeApiRequest returns: {"success": true, "data": {"success": true, "data": {"challenge_token": "xxx"}}}
            $haproxy_data = $challenge_result['data']['data'] ?? $challenge_result['data'] ?? [];
            $challenge_token = $haproxy_data['challenge_token'] ?? null;
            if (empty($challenge_token)) {
                error_log("SSL Manager: No challenge token in response. Full result: " . json_encode($challenge_result));
                return ['success' => false, 'error' => 'No challenge token received from HAProxy'];
            }

            error_log("SSL Manager: Got challenge token for $wildcard_fqdn: $challenge_token");

            $challenge_fqdn = '_acme-challenge.' . $base_domain;

            // Store challenge in database
            $stmt = $this->db->prepare("
                INSERT INTO whp.dns_challenges (site_id, domain_name, challenge_fqdn, challenge_token, status)
                VALUES (?, ?, ?, ?, 'pending')
            ");
            $stmt->execute([$site_id, $base_domain, $challenge_fqdn, $challenge_token]);
            $challenge_id = $this->db->lastInsertId();

            // Create TXT record in PowerDNS
            require_once('/docker/whp/web/libs/pdns-integration.php');
            $pdns = new PowerDNSIntegration($this->db);
            $dns_result = $pdns->addDnsChallengeRecord($base_domain, $challenge_token);

            if ($dns_result) {
                // Update challenge status to 'created'
                $stmt = $this->db->prepare("UPDATE whp.dns_challenges SET status = 'created' WHERE id = ?");
                $stmt->execute([$challenge_id]);
            } else {
                // DNS record creation failed
                $stmt = $this->db->prepare("UPDATE whp.dns_challenges SET status = 'failed', error_message = 'Failed to create DNS TXT record' WHERE id = ?");
                $stmt->execute([$challenge_id]);
                return ['success' => false, 'error' => 'Failed to create DNS challenge TXT record'];
            }

            // Log the action
            $this->logSSLRenewal($wildcard_fqdn, 'pending', 'DNS-01 challenge initiated');

            return [
                'success' => true,
                'challenge_id' => $challenge_id,
                'challenge_fqdn' => $challenge_fqdn,
                'challenge_token' => $challenge_token,
                'status' => 'created',
                'message' => 'DNS-01 challenge created. TXT record added to PowerDNS. Verification will happen automatically.'
            ];

        } catch (PDOException $e) {
            error_log("SSL Manager: Wildcard SSL error: " . $e->getMessage());
            return ['success' => false, 'error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Verify a DNS-01 challenge and finalize wildcard certificate
     *
     * @param int $challenge_id DNS challenge ID
     * @return array Result with success/error status
     */
    public function verify_dns_challenge($challenge_id) {
        try {
            // Fetch challenge
            $stmt = $this->db->prepare("SELECT * FROM whp.dns_challenges WHERE id = ?");
            $stmt->execute([$challenge_id]);
            $challenge = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$challenge) {
                return ['success' => false, 'error' => 'Challenge not found'];
            }

            if ($challenge['status'] === 'completed') {
                return ['success' => true, 'message' => 'Challenge already completed'];
            }

            if (!in_array($challenge['status'], ['created', 'verified'])) {
                return ['success' => false, 'error' => 'Challenge in invalid state: ' . $challenge['status']];
            }

            $base_domain = $challenge['domain_name'];

            // Tell HAProxy to verify and finalize
            $verify_result = $this->haproxy_manager->verifyDnsChallenge($base_domain);

            if ($verify_result['success']) {
                // Update challenge status
                $stmt = $this->db->prepare("
                    UPDATE whp.dns_challenges
                    SET status = 'completed', verified_at = CURRENT_TIMESTAMP, completed_at = CURRENT_TIMESTAMP
                    WHERE id = ?
                ");
                $stmt->execute([$challenge_id]);

                // Clean up TXT record
                require_once('/docker/whp/web/libs/pdns-integration.php');
                $pdns = new PowerDNSIntegration($this->db);
                $pdns->removeDnsChallengeRecord($base_domain);

                // Update site SSL status
                $this->updateSiteSSLStatus($challenge['site_id'], 'letsencrypt');

                // Log success
                $this->logSSLRenewal('*.' . $base_domain, 'success', 'Wildcard SSL certificate issued via DNS-01');

                return ['success' => true, 'message' => 'Wildcard certificate issued successfully'];
            } else {
                $error_msg = $verify_result['error'] ?? 'Verification failed';

                // Update challenge with error
                $stmt = $this->db->prepare("
                    UPDATE whp.dns_challenges SET error_message = ? WHERE id = ?
                ");
                $stmt->execute([$error_msg, $challenge_id]);

                // If it's a permanent failure, mark as failed
                if (strpos($error_msg, 'invalid') !== false || strpos($error_msg, 'expired') !== false) {
                    $stmt = $this->db->prepare("UPDATE whp.dns_challenges SET status = 'failed' WHERE id = ?");
                    $stmt->execute([$challenge_id]);
                }

                return ['success' => false, 'error' => $error_msg];
            }

        } catch (PDOException $e) {
            error_log("SSL Manager: DNS challenge verification error: " . $e->getMessage());
            return ['success' => false, 'error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Refresh all system service SSL certificates from HAProxy
     * Used by cron to keep system service certificates up to date
     *
     * @return array Result summary
     */
    public function refreshAllSystemServiceSSL() {
        try {
            $stmt = $this->db->query("
                SELECT service_name, domain_name
                FROM whp.system_ssl_certificates
                WHERE ssl_enabled = 1
            ");
            $certificates = $stmt->fetchAll();

            $results = [
                'total' => count($certificates),
                'refreshed' => 0,
                'failed' => 0,
                'errors' => []
            ];

            foreach ($certificates as $cert) {
                error_log("SSL Manager: Processing refresh for {$cert['service_name']} - {$cert['domain_name']}");
                $result = $this->refreshSystemServiceSSL($cert['service_name'], $cert['domain_name']);

                if ($result['success']) {
                    $results['refreshed']++;
                } else {
                    $results['failed']++;
                    $results['errors'][] = "{$cert['service_name']} - {$cert['domain_name']}: " . $result['error'];
                }
            }

            return $results;

        } catch (PDOException $e) {
            error_log("SSL Manager: Failed to refresh all system service SSL certificates: " . $e->getMessage());
            return [
                'total' => 0,
                'refreshed' => 0,
                'failed' => 1,
                'errors' => ['Database error: ' . $e->getMessage()]
            ];
        }
    }
}
?> 