<?php
// Root-only page: enforced by the page-permission whitelist in
// libs/permission_manager.php AND this defensive in-file gate (pages must
// never trust that they were reached through index.php alone). Delegated
// sub-users of root also carry AUTH_USER === 'root', so a real system
// session is required too.
if (!defined('AUTH_USER') || AUTH_USER !== 'root'
    || ($_SESSION['user_type'] ?? '') !== 'system') {
    echo '<div class="alert alert-danger">Root access required.</div>';
    return;
}



$message = '';
$message_type = '';

// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['action'])) {
        switch ($_POST['action']) {
            case 'update_hostname':
                if (!empty($_POST['hostname'])) {
                    $hostname = trim($_POST['hostname']);
                    
                    // Validate hostname format
                    if (!filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
                        $message = 'Invalid hostname format';
                        $message_type = 'danger';
                        break;
                    }
                    
                    // Update system hostname
                    $result = shell_exec("hostnamectl set-hostname " . escapeshellarg($hostname) . " 2>&1");
                    
                    // Update /etc/hosts
                    $hosts_content = file_get_contents('/etc/hosts');
                    $hosts_lines = explode("\n", $hosts_content);
                    $updated = false;
                    
                    foreach ($hosts_lines as $i => $line) {
                        if (strpos($line, '127.0.1.1') === 0) {
                            $hosts_lines[$i] = "127.0.1.1\t" . $hostname;
                            $updated = true;
                            break;
                        }
                    }
                    
                    if (!$updated) {
                        $hosts_lines[] = "127.0.1.1\t" . $hostname;
                    }
                    
                    file_put_contents('/etc/hosts', implode("\n", $hosts_lines));
                    
                    // Save to configuration file
                    $config = [
                        'hostname' => $hostname,
                        'updated_at' => date('Y-m-d H:i:s')
                    ];
                    
                    if (file_exists('/docker/whp/settings.json')) {
                        $existing_config = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                        $config = array_merge($existing_config, $config);
                    }
                    
                    file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT));
                    chmod('/docker/whp/settings.json', 0600);
                    
                    $message = 'Hostname updated successfully: ' . $hostname;
                    $message_type = 'success';
                } else {
                    $message = 'Hostname is required';
                    $message_type = 'danger';
                }
                break;
                
            case 'update_nameservers':
                if (!empty($_POST['nameserver1'])) {
                    $nameservers = [
                        trim($_POST['nameserver1']),
                        !empty($_POST['nameserver2']) ? trim($_POST['nameserver2']) : null
                    ];
                    
                    // Filter out empty nameservers
                    $nameservers = array_filter($nameservers);
                    
                    // Validate nameserver IPs
                    $valid_nameservers = [];
                    foreach ($nameservers as $ns) {
                        if (filter_var($ns, FILTER_VALIDATE_IP)) {
                            $valid_nameservers[] = $ns;
                        } else {
                            $message = 'Invalid nameserver IP address: ' . $ns;
                            $message_type = 'danger';
                            break 2;
                        }
                    }
                    
                    if (empty($valid_nameservers)) {
                        $message = 'At least one valid nameserver is required';
                        $message_type = 'danger';
                        break;
                    }
                    
                    // Update resolv.conf
                    $resolv_content = "# Generated by WHP Server Settings\n";
                    foreach ($valid_nameservers as $ns) {
                        $resolv_content .= "nameserver $ns\n";
                    }
                    
                    file_put_contents('/etc/resolv.conf', $resolv_content);
                    
                    // Save to configuration file
                    $config = [
                        'nameservers' => $valid_nameservers,
                        'updated_at' => date('Y-m-d H:i:s')
                    ];
                    
                    if (file_exists('/docker/whp/settings.json')) {
                        $existing_config = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                        $config = array_merge($existing_config, $config);
                    }
                    
                    file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT));
                    chmod('/docker/whp/settings.json', 0600);
                    
                    $message = 'Nameservers updated successfully';
                    $message_type = 'success';
                } else {
                    $message = 'At least one nameserver is required';
                    $message_type = 'danger';
                }
                break;
                
            case 'update_timezone':
                if (!empty($_POST['timezone'])) {
                    $timezone = trim($_POST['timezone']);
                    
                    // Validate timezone
                    if (!in_array($timezone, timezone_identifiers_list())) {
                        $message = 'Invalid timezone';
                        $message_type = 'danger';
                        break;
                    }
                    
                    // Update system timezone
                    shell_exec("timedatectl set-timezone " . escapeshellarg($timezone));
                    
                    // Save to configuration file
                    $config = [
                        'timezone' => $timezone,
                        'updated_at' => date('Y-m-d H:i:s')
                    ];
                    
                    if (file_exists('/docker/whp/settings.json')) {
                        $existing_config = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                        $config = array_merge($existing_config, $config);
                    }
                    
                    file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT));
                    chmod('/docker/whp/settings.json', 0600);
                    
                    $message = 'Timezone updated successfully: ' . $timezone;
                    $message_type = 'success';
                } else {
                    $message = 'Timezone is required';
                    $message_type = 'danger';
                }
                break;
                
            case 'restart_services':
                if (!empty($_POST['services'])) {
                    $services = $_POST['services'];
                    $allowed_services = ['httpd', 'php-fpm', 'docker', 'network', 'proftpd', 'whp-backup-upload'];
                    $restarted_services = [];
                    $failed_services = [];
                    
                    foreach ($services as $service) {
                        if (in_array($service, $allowed_services)) {
                            if ($service === 'network') {
                                $result = shell_exec("systemctl restart NetworkManager 2>&1");
                            } else {
                                $result = shell_exec("systemctl restart $service 2>&1");
                            }
                            
                            // Check if service restarted successfully
                            $status = shell_exec("systemctl is-active $service 2>/dev/null");
                            if (trim($status) === 'active') {
                                $restarted_services[] = $service;
                            } else {
                                $failed_services[] = $service;
                            }
                        }
                    }
                    
                    if (!empty($restarted_services)) {
                        $message = 'Services restarted: ' . implode(', ', $restarted_services);
                        $message_type = 'success';
                        
                        if (!empty($failed_services)) {
                            $message .= '. Failed: ' . implode(', ', $failed_services);
                        }
                    } else {
                        $message = 'No services were restarted';
                        $message_type = 'danger';
                    }
                }
                break;
                
            case 'update_whp_nameservers':
                if (!empty($_POST['whp_ns1']) && !empty($_POST['whp_ns2'])) {
                    $whp_ns1 = trim($_POST['whp_ns1']);
                    $whp_ns2 = trim($_POST['whp_ns2']);
                    $whp_ns1_ip = trim($_POST['whp_ns1_ip']);
                    $whp_ns2_ip = trim($_POST['whp_ns2_ip']);
                    
                    // Validate nameserver hostnames
                    if (!filter_var($whp_ns1, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) ||
                        !filter_var($whp_ns2, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
                        $message = 'Invalid nameserver hostname format';
                        $message_type = 'danger';
                        break;
                    }
                    
                    // Validate IP addresses
                    if (!filter_var($whp_ns1_ip, FILTER_VALIDATE_IP) ||
                        !filter_var($whp_ns2_ip, FILTER_VALIDATE_IP)) {
                        $message = 'Invalid nameserver IP address format';
                        $message_type = 'danger';
                        break;
                    }
                    
                    // Save to configuration file
                    $config = [
                        'whp_nameservers' => [
                            'ns1' => $whp_ns1,
                            'ns1_ip' => $whp_ns1_ip,
                            'ns2' => $whp_ns2,
                            'ns2_ip' => $whp_ns2_ip
                        ],
                        'updated_at' => date('Y-m-d H:i:s')
                    ];
                    
                    if (file_exists('/docker/whp/settings.json')) {
                        $existing_config = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                        $config = array_merge($existing_config, $config);
                    }
                    
                    file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT));
                    chmod('/docker/whp/settings.json', 0600);
                    
                    $message = 'WHP nameserver configuration updated successfully';
                    $message_type = 'success';
                } else {
                    $message = 'Both primary and secondary nameserver hostnames and IPs are required';
                    $message_type = 'danger';
                }
                break;

            case 'update_backup_settings':
                if (!empty($_POST['max_concurrent_backups'])) {
                    $max_concurrent = intval($_POST['max_concurrent_backups']);

                    // Validate the value (1-10 concurrent backups)
                    if ($max_concurrent < 1 || $max_concurrent > 10) {
                        $message = 'Maximum concurrent backups must be between 1 and 10';
                        $message_type = 'danger';
                        break;
                    }

                    // Load existing config and merge
                    $config = [];
                    if (file_exists('/docker/whp/settings.json')) {
                        $existing_config = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                        if ($existing_config) {
                            $config = $existing_config;
                        }
                    }

                    $config['backup_max_concurrent'] = $max_concurrent;
                    $config['updated_at'] = date('Y-m-d H:i:s');

                    // Handle backup local storage limit if provided
                    if (isset($_POST['backup_upload_space_gb']) && $_POST['backup_upload_space_gb'] !== '') {
                        $space_gb = intval($_POST['backup_upload_space_gb']);
                        if ($space_gb < 10 || $space_gb > 10000) {
                            $message = 'Backup local storage limit must be between 10 and 10,000 GB';
                            $message_type = 'danger';
                            break;
                        }

                        // Update backup_local_storage table
                        try {
                            require_once('/docker/whp/web/libs/mysqlmgmt.php');
                            $mysql = new \mysqlmgmt();
                            $dbConn = $mysql->getMySQLConnection();
                            $dbConn->exec("USE whp");
                            $stmt = $dbConn->prepare("UPDATE backup_local_storage SET max_size_gb = ? WHERE id = 1");
                            $stmt->execute([$space_gb]);

                            $config['backup_upload_space_gb'] = $space_gb;
                        } catch (Exception $e) {
                            $message = 'Failed to update backup storage limit: ' . $e->getMessage();
                            $message_type = 'danger';
                            break;
                        }
                    }

                    // Save settings
                    $settings_json = json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
                    if (file_put_contents('/docker/whp/settings.json', $settings_json) === false) {
                        $message = 'Failed to save backup settings';
                        $message_type = 'danger';
                        break;
                    }

                    // Set proper permissions
                    chmod('/docker/whp/settings.json', 0600);

                    $message = "Backup settings updated successfully. Maximum concurrent backups set to $max_concurrent.";
                    $message_type = 'success';
                } else {
                    $message = 'Maximum concurrent backups value is required';
                    $message_type = 'danger';
                }
                break;

            case 'update_backup_throttle':
                // Validate the four cap inputs. All must be positive integers
                // within sane ranges; the values land in a systemd drop-in
                // applied by /root/whp/scripts/apply-backup-throttle.php.
                $read_bw  = isset($_POST['read_bw_mb'])  ? intval($_POST['read_bw_mb'])  : 0;
                $write_bw = isset($_POST['write_bw_mb']) ? intval($_POST['write_bw_mb']) : 0;
                $read_io  = isset($_POST['read_iops'])   ? intval($_POST['read_iops'])   : 0;
                $write_io = isset($_POST['write_iops'])  ? intval($_POST['write_iops'])  : 0;

                $errors = [];
                if ($read_bw  < 1 || $read_bw  > 10000) { $errors[] = 'Read bandwidth must be between 1 and 10,000 MB/s'; }
                if ($write_bw < 1 || $write_bw > 10000) { $errors[] = 'Write bandwidth must be between 1 and 10,000 MB/s'; }
                if ($read_io  < 100 || $read_io  > 200000) { $errors[] = 'Read IOPS must be between 100 and 200,000'; }
                if ($write_io < 100 || $write_io > 200000) { $errors[] = 'Write IOPS must be between 100 and 200,000'; }
                if (!empty($errors)) {
                    $message = implode(' / ', $errors);
                    $message_type = 'danger';
                    break;
                }

                // Merge into settings.json without clobbering other keys.
                $config = [];
                if (file_exists('/docker/whp/settings.json')) {
                    $existing = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                    if (is_array($existing)) { $config = $existing; }
                }
                $config['backup_throttle'] = [
                    'read_bw_mb'  => $read_bw,
                    'write_bw_mb' => $write_bw,
                    'read_iops'   => $read_io,
                    'write_iops'  => $write_io,
                ];
                $config['updated_at'] = date('Y-m-d H:i:s');

                $settings_json = json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
                if (file_put_contents('/docker/whp/settings.json', $settings_json) === false) {
                    $message = 'Failed to persist backup throttle settings to settings.json';
                    $message_type = 'danger';
                    break;
                }
                @chmod('/docker/whp/settings.json', 0600);

                // Materialize into the systemd drop-in. Capture the apply
                // script's stderr so we surface the real reason on failure
                // (missing slice unit, daemon-reload failure, etc).
                $apply = '/root/whp/scripts/apply-backup-throttle.php';
                if (!file_exists($apply)) {
                    $message = 'Saved to settings.json, but apply script missing — slice still on baseline caps until deploy completes.';
                    $message_type = 'warning';
                    break;
                }
                $descriptors = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
                $proc = proc_open(['/usr/bin/php', $apply], $descriptors, $pipes);
                if (!is_resource($proc)) {
                    $message = 'Saved settings, but failed to spawn apply script.';
                    $message_type = 'warning';
                    break;
                }
                $apply_stdout = stream_get_contents($pipes[1]); fclose($pipes[1]);
                $apply_stderr = stream_get_contents($pipes[2]); fclose($pipes[2]);
                $apply_rc = proc_close($proc);

                if ($apply_rc !== 0) {
                    $message = 'Saved settings, but apply step failed: ' . trim($apply_stderr ?: $apply_stdout ?: 'unknown error');
                    $message_type = 'warning';
                } else {
                    $message = "Backup I/O throttle updated: read={$read_bw} MB/s, write={$write_bw} MB/s, rIOPS={$read_io}, wIOPS={$write_io}.";
                    $message_type = 'success';
                }
                break;

            case 'update_coraza_waf':
                // Web Application Firewall mode toggle. Three states:
                //   off          - sidecar stopped; no SPOE in haproxy.cfg
                //   detect_only  - sidecar runs in detect mode; rules log only
                //   enforce      - sidecar runs with day-one enforce list
                // The apply script handles container lifecycle + recreates
                // haproxy-manager. Brief HAProxy outage (~3-5s) on save.
                $allowed_modes = ['off', 'detect_only', 'enforce'];
                $waf_mode = isset($_POST['waf_mode']) ? trim($_POST['waf_mode']) : '';
                if (!in_array($waf_mode, $allowed_modes, true)) {
                    $message = 'Invalid WAF mode: must be off, detect_only, or enforce';
                    $message_type = 'danger';
                    break;
                }

                // Merge into settings.json without clobbering other keys.
                $config = [];
                if (file_exists('/docker/whp/settings.json')) {
                    $existing = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                    if (is_array($existing)) { $config = $existing; }
                }
                $current_mode = $config['coraza_waf']['mode'] ?? 'off';
                if (!isset($config['coraza_waf']) || !is_array($config['coraza_waf'])) {
                    $config['coraza_waf'] = [];
                }
                $config['coraza_waf']['mode'] = $waf_mode;
                $config['updated_at'] = date('Y-m-d H:i:s');

                if (file_put_contents('/docker/whp/settings.json',
                        json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) === false) {
                    $message = 'Failed to persist WAF mode to settings.json';
                    $message_type = 'danger';
                    break;
                }
                @chmod('/docker/whp/settings.json', 0600);

                // Apply: starts/stops sidecar, recreates haproxy-manager.
                $apply = '/root/whp/scripts/apply-coraza-waf-mode.sh';
                if (!file_exists($apply)) {
                    $message = "WAF mode saved as '$waf_mode' in settings.json, but apply script missing — install via update.sh.";
                    $message_type = 'warning';
                    break;
                }
                $descriptors = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
                $proc = proc_open([$apply], $descriptors, $pipes);
                if (!is_resource($proc)) {
                    $message = "Saved settings, but failed to spawn apply script.";
                    $message_type = 'warning';
                    break;
                }
                $apply_stdout = stream_get_contents($pipes[1]); fclose($pipes[1]);
                $apply_stderr = stream_get_contents($pipes[2]); fclose($pipes[2]);
                $apply_rc = proc_close($proc);

                if ($apply_rc !== 0) {
                    $message = 'WAF mode saved, but apply step failed: ' . trim($apply_stderr ?: $apply_stdout ?: 'unknown error');
                    $message_type = 'warning';
                } else {
                    $transition = $current_mode === $waf_mode ? "re-applied" : "changed from {$current_mode} to {$waf_mode}";
                    $message = "Web Application Firewall {$transition}. HAProxy was briefly recreated to apply the change.";
                    $message_type = 'success';
                }
                break;

            case 'update_cpanel_import_sanitization':
                // cPanel Import sanitization sandbox toggle. Settings live
                // under cpanel_import.* in /docker/whp/settings.json; consumed
                // by libs/CpanelImportSanitizationContainer::getSettings()
                // at import time. No live container side-effects on save —
                // the next import picks up the new mode.
                if (AUTH_USER !== 'root') {
                    $message = 'Access denied. Only root can change sanitization mode.';
                    $message_type = 'danger';
                    break;
                }

                $allowed_cpimp_modes = ['direct', 'sanitization-container'];
                $cpimp_mode_in = isset($_POST['cpimp_mode']) ? trim($_POST['cpimp_mode']) : '';
                if (!in_array($cpimp_mode_in, $allowed_cpimp_modes, true)) {
                    $message = 'Invalid execution mode (expected direct or sanitization-container)';
                    $message_type = 'danger';
                    break;
                }
                $cpimp_fall_back_in = !empty($_POST['cpimp_fall_back']);

                // Merge without clobbering other cpanel_import.* defaults
                // the seed script may have written.
                $config = [];
                if (file_exists('/docker/whp/settings.json')) {
                    $existing = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                    if (is_array($existing)) { $config = $existing; }
                }
                if (!isset($config['cpanel_import']) || !is_array($config['cpanel_import'])) {
                    $config['cpanel_import'] = [];
                }
                $previous_mode = $config['cpanel_import']['execution_mode'] ?? 'direct';
                $config['cpanel_import']['execution_mode'] = $cpimp_mode_in;
                $config['cpanel_import']['fall_back_to_direct_on_image_unavailable'] = $cpimp_fall_back_in;
                $config['updated_at'] = date('Y-m-d H:i:s');

                if (file_put_contents('/docker/whp/settings.json',
                        json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
                        LOCK_EX) === false) {
                    $message = 'Failed to persist cPanel import settings to settings.json';
                    $message_type = 'danger';
                    break;
                }
                @chmod('/docker/whp/settings.json', 0600);

                if ($previous_mode === $cpimp_mode_in) {
                    $message = 'cPanel import sanitization settings updated (mode unchanged at ' . htmlspecialchars($cpimp_mode_in) . ').';
                } else {
                    $message = 'cPanel import execution mode changed: '
                             . htmlspecialchars($previous_mode) . ' -> '
                             . htmlspecialchars($cpimp_mode_in)
                             . '. The next import will use the new mode.';
                }
                $message_type = 'success';
                break;

            case 'manage_docker_container':
                if (!empty($_POST['container']) && !empty($_POST['operation'])) {
                    $container = trim($_POST['container']);
                    $operation = trim($_POST['operation']);
                    $allowed_containers = ['mysql', 'haproxy-manager', 'memcache', 'postgresql'];
                    $allowed_operations = ['start', 'stop', 'restart', 'pull_recreate'];
                    
                    if (!in_array($container, $allowed_containers) || !in_array($operation, $allowed_operations)) {
                        $message = 'Invalid container or operation';
                        $message_type = 'danger';
                        break;
                    }
                    
                    $result = '';
                    switch ($operation) {
                        case 'start':
                            $result = shell_exec("/root/whp/scripts/container-manager.sh start $container 2>&1");
                            $message = "Container $container started";
                            break;
                        case 'stop':
                            $result = shell_exec("/root/whp/scripts/container-manager.sh stop $container 2>&1");
                            $message = "Container $container stopped";
                            break;
                        case 'restart':
                            $result = shell_exec("/root/whp/scripts/container-manager.sh restart $container 2>&1");
                            $message = "Container $container restarted";
                            break;
                        case 'pull_recreate':
                            $result = shell_exec("/root/whp/scripts/container-manager.sh recreate $container 2>&1");
                            $message = "Container $container recreated with latest image";
                            break;
                    }
                    
                    $message_type = 'success';
                }
                break;
                
            case 'manage_shared_httpd':
                if (!empty($_POST['operation'])) {
                    $operation = trim($_POST['operation']);
                    $allowed_operations = ['scale', 'restart_all', 'pull_recreate_all', 'stop_all', 'start_all'];

                    if (!in_array($operation, $allowed_operations)) {
                        $message = 'Invalid shared httpd operation';
                        $message_type = 'danger';
                        break;
                    }

                    $result = '';
                    switch ($operation) {
                        case 'scale':
                            $count = max(0, intval($_POST['instance_count'] ?? 1));
                            $result = shell_exec("/root/whp/scripts/container-manager.sh scale shared-httpd $count 2>&1");
                            $message = "Shared httpd scaled to $count instance(s)";
                            break;
                        case 'restart_all':
                            $instances = trim(shell_exec("docker ps --filter 'name=shared-httpd-' --filter 'status=running' --format '{{.Names}}' 2>/dev/null"));
                            if (!empty($instances)) {
                                foreach (explode("\n", $instances) as $name) {
                                    shell_exec("docker restart " . escapeshellarg(trim($name)) . " 2>&1");
                                }
                                $message = "All shared httpd instances restarted";
                            } else {
                                $message = "No running shared httpd instances to restart";
                                $message_type = 'warning';
                                break 2;
                            }
                            break;
                        case 'pull_recreate_all':
                            $instances = trim(shell_exec("docker ps -a --filter 'name=shared-httpd-' --format '{{.Names}}' 2>/dev/null"));
                            if (!empty($instances)) {
                                foreach (explode("\n", $instances) as $name) {
                                    $instance_num = str_replace('shared-httpd-', '', trim($name));
                                    shell_exec("/root/whp/scripts/container-manager.sh recreate shared-httpd $instance_num 2>&1");
                                }
                                $message = "All shared httpd instances pulled and recreated";
                            } else {
                                $message = "No shared httpd instances found";
                                $message_type = 'warning';
                                break 2;
                            }
                            break;
                        case 'stop_all':
                            $instances = trim(shell_exec("docker ps --filter 'name=shared-httpd-' --filter 'status=running' --format '{{.Names}}' 2>/dev/null"));
                            if (!empty($instances)) {
                                foreach (explode("\n", $instances) as $name) {
                                    shell_exec("docker stop " . escapeshellarg(trim($name)) . " 2>&1");
                                }
                                $message = "All shared httpd instances stopped";
                            } else {
                                $message = "No running shared httpd instances to stop";
                                $message_type = 'warning';
                                break 2;
                            }
                            break;
                        case 'start_all':
                            $instances = trim(shell_exec("docker ps -a --filter 'name=shared-httpd-' --filter 'status=exited' --format '{{.Names}}' 2>/dev/null"));
                            if (!empty($instances)) {
                                foreach (explode("\n", $instances) as $name) {
                                    shell_exec("docker start " . escapeshellarg(trim($name)) . " 2>&1");
                                }
                                $message = "All shared httpd instances started";
                            } else {
                                $message = "No stopped shared httpd instances to start";
                                $message_type = 'warning';
                                break 2;
                            }
                            break;
                    }
                    $message_type = $message_type ?: 'success';
                }
                break;

            case 'update_shared_httpd_settings':
                $default_mode = ($_POST['default_mode'] ?? 'standalone') === 'shared_httpd' ? 'shared_httpd' : 'standalone';

                $config = [];
                if (file_exists('/docker/whp/settings.json')) {
                    $config = json_decode(file_get_contents('/docker/whp/settings.json'), true) ?: [];
                }
                $config['shared_httpd'] = [
                    'default_mode' => $default_mode,
                ];
                $config['updated_at'] = date('Y-m-d H:i:s');

                file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT));
                chmod('/docker/whp/settings.json', 0600);

                $message = 'Shared httpd settings updated successfully';
                $message_type = 'success';
                break;

            case 'update_ssl_renewal_settings':
                // Persist the ssl_renewal block of settings.json. All five toggles
                // are checkbox inputs — absent = false. The orchestrator reads
                // these (cached per-process) on every cron sweep.
                $config = [];
                if (file_exists('/docker/whp/settings.json')) {
                    $config = json_decode(file_get_contents('/docker/whp/settings.json'), true) ?: [];
                }
                $existing = is_array($config['ssl_renewal'] ?? null) ? $config['ssl_renewal'] : [];
                $config['ssl_renewal'] = array_merge($existing, [
                    'orchestrator_enabled'         => !empty($_POST['ssl_orchestrator_enabled']),
                    'email_unreachable_enabled'    => !empty($_POST['ssl_email_unreachable_enabled']),
                    'email_manual_review_enabled'  => !empty($_POST['ssl_email_manual_review_enabled']),
                    'email_recovered_enabled'      => !empty($_POST['ssl_email_recovered_enabled']),
                    'email_dns_validation_enabled' => !empty($_POST['ssl_email_dns_validation_enabled']),
                ]);
                $config['updated_at'] = date('Y-m-d H:i:s');
                file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT));
                chmod('/docker/whp/settings.json', 0600);

                $message = 'SSL renewal settings updated successfully';
                $message_type = 'success';
                break;

            case 'update_haproxy_api_key':
                if (!empty($_POST['haproxy_api_key'])) {
                    $api_key = trim($_POST['haproxy_api_key']);
                    
                    // Validate API key format (basic validation)
                    if (strlen($api_key) < 16) {
                        $message = 'API key must be at least 16 characters long';
                        $message_type = 'danger';
                        break;
                    }
                    
                    // Save to configuration file
                    $config = [
                        'haproxy_api_key' => $api_key,
                        'updated_at' => date('Y-m-d H:i:s')
                    ];
                    
                    if (file_exists('/docker/whp/settings.json')) {
                        $existing_config = json_decode(file_get_contents('/docker/whp/settings.json'), true);
                        $config = array_merge($existing_config, $config);
                    }
                    
                    file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT));
                    chmod('/docker/whp/settings.json', 0600);
                    
                    $message = 'HAProxy API key updated successfully. Restart the HAProxy container to apply changes.';
                    $message_type = 'success';
                } else {
                    $message = 'API key is required';
                    $message_type = 'danger';
                }
                break;
                
            case 'haproxy_regenerate_config':
                try {
                    require_once('/docker/whp/web/libs/haproxy_manager.php');
                    require_once('/docker/whp/web/libs/mysqlmgmt.php');
                    
                    $haproxy_manager = new haproxy_manager();
                    $MySQLMgmt = new mysqlmgmt();
                    $db = $MySQLMgmt->getMySQLConnection();
                    
                    if (!$db) {
                        throw new Exception('Failed to connect to database');
                    }
                    
                    // First, clear all existing HAProxy configurations
                    $stmt = $db->query("SELECT domain_name FROM whp.haproxy_domains");
                    $existing_domains = $stmt->fetchAll();
                    
                    foreach ($existing_domains as $domain) {
                        $haproxy_manager->removeDomain($domain['domain_name']);
                    }
                    
                    // Get all active sites and reconfigure HAProxy
                    $stmt = $db->query("SELECT id FROM whp.sites WHERE active = 1");
                    $sites = $stmt->fetchAll();
                    
                    $regenerate_errors = [];
                    foreach ($sites as $site) {
                        $result = $haproxy_manager->configureSite($site['id'], []);
                        if (!$result['success']) {
                            $regenerate_errors[] = "Site {$site['id']}: " . $result['error'];
                        }
                    }
                    
                    // Call HAProxy regenerate to rebuild configuration
                    $regenerate_result = $haproxy_manager->regenerateConfiguration();
                    if (!$regenerate_result['success']) {
                        $regenerate_errors[] = "HAProxy regenerate failed: " . $regenerate_result['error'];
                    }
                    
                    if (empty($regenerate_errors)) {
                        $message = 'HAProxy configuration regenerated successfully for all sites';
                        $message_type = 'success';
                    } else {
                        $message = 'HAProxy configuration regenerated with some errors: ' . implode(', ', $regenerate_errors);
                        $message_type = 'warning';
                    }
                } catch (Exception $e) {
                    $message = 'Error regenerating HAProxy configuration: ' . $e->getMessage();
                    $message_type = 'danger';
                }
                break;
                
            case 'haproxy_reload_config':
                try {
                    require_once('/docker/whp/web/libs/haproxy_manager.php');
                    $haproxy_manager = new haproxy_manager();
                    
                    $result = $haproxy_manager->reloadConfiguration();
                    
                    if ($result['success']) {
                        $message = 'HAProxy configuration reloaded successfully';
                        $message_type = 'success';
                    } else {
                        $message = 'Failed to reload HAProxy configuration: ' . $result['error'];
                        $message_type = 'danger';
                    }
                } catch (Exception $e) {
                    $message = 'Error reloading HAProxy configuration: ' . $e->getMessage();
                    $message_type = 'danger';
                }
                break;
                
            case 'haproxy_health_check':
                try {
                    require_once('/docker/whp/web/libs/haproxy_manager.php');
                    $haproxy_manager = new haproxy_manager();
                    
                    $result = $haproxy_manager->getHealthStatus();
                    
                    if ($result['success']) {
                        $health_data = $result['data'];
                        $message = 'HAProxy health check completed. Status: ' . ($health_data['haproxy_status'] ?? 'Unknown');
                        $message_type = 'success';
                    } else {
                        $message = 'HAProxy health check failed: ' . $result['error'];
                        $message_type = 'danger';
                    }
                } catch (Exception $e) {
                    $message = 'Error checking HAProxy health: ' . $e->getMessage();
                    $message_type = 'danger';
                }
                break;
                
            case 'enable_system_service_ssl':
                if (!empty($_POST['service_name']) && !empty($_POST['domain_name'])) {
                    $service_name = trim($_POST['service_name']);
                    $domain_name = trim($_POST['domain_name']);
                    
                    // Validate service name
                    $allowed_services = ['httpd', 'proftpd'];
                    if (!in_array($service_name, $allowed_services)) {
                        $message = 'Invalid service name';
                        $message_type = 'danger';
                        break;
                    }
                    
                    // Validate domain format
                    if (!filter_var('http://' . $domain_name, FILTER_VALIDATE_URL)) {
                        $message = 'Invalid domain format';
                        $message_type = 'danger';
                        break;
                    }
                    
                    try {
                        require_once('/docker/whp/web/libs/ssl_manager.php');
                        $ssl_manager = new ssl_manager();
                        $result = $ssl_manager->enable_system_service_ssl($service_name, $domain_name);
                        
                        if ($result['success']) {
                            $message = $result['message'];
                            $message_type = 'success';
                        } else {
                            $message = 'Failed to enable SSL for ' . $service_name . ': ' . $result['error'];
                            $message_type = 'danger';
                        }
                    } catch (Exception $e) {
                        $message = 'Error enabling SSL: ' . $e->getMessage();
                        $message_type = 'danger';
                    }
                } else {
                    $message = 'Service name and domain name are required';
                    $message_type = 'danger';
                }
                break;
                
            case 'update_mail_settings':
                try {
                    $updates = [];

                    $mail_server = trim($_POST['mail_server'] ?? '');
                    if ($mail_server === '' || !filter_var('http://' . $mail_server, FILTER_VALIDATE_URL)) {
                        throw new Exception('Invalid mail server hostname');
                    }
                    $updates['mail_server'] = $mail_server;

                    $mail_api_url = trim($_POST['mail_api_url'] ?? '');
                    if ($mail_api_url === '' || !filter_var($mail_api_url, FILTER_VALIDATE_URL)) {
                        throw new Exception('Invalid mail API URL');
                    }
                    $updates['mail_api_url'] = $mail_api_url;

                    $updates['mail_api_username']    = trim($_POST['mail_api_username'] ?? '');
                    $updates['mail_api_password']    = trim($_POST['mail_api_password'] ?? '');
                    $updates['mailserver_debug_log'] = !empty($_POST['mailserver_debug_log']);

                    $config = [];
                    if (file_exists('/docker/whp/settings.json')) {
                        $config = json_decode(file_get_contents('/docker/whp/settings.json'), true) ?: [];
                    }
                    $config = array_merge($config, $updates);
                    $config['updated_at'] = date('Y-m-d H:i:s');

                    if (file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX) === false) {
                        throw new Exception('Failed to save settings file');
                    }
                    chmod('/docker/whp/settings.json', 0600);

                    $message = 'Mail server settings updated successfully';
                    $message_type = 'success';
                } catch (Exception $e) {
                    $message = 'Error updating mail settings: ' . $e->getMessage();
                    $message_type = 'danger';
                }
                break;

            case 'apply_mail_discovery_records':
                // Backfill the autodiscover SRV + autoconfig CNAME (pointed at the
                // configured mail server) across every existing zone. Idempotent —
                // only adds what's missing; never overwrites existing records.
                try {
                    if (!file_exists('/docker/whp/web/libs/pdns-integration.php') ||
                        !file_exists('/docker/whp/web/libs/mysqlmgmt.php')) {
                        throw new Exception('PowerDNS integration is not available');
                    }
                    require_once('/docker/whp/web/libs/pdns-integration.php');
                    require_once('/docker/whp/web/libs/mysqlmgmt.php');
                    $MySQLMgmt = new mysqlmgmt();
                    $db = $MySQLMgmt->getMySQLConnection();
                    if (!$db) {
                        throw new Exception('Database connection failed');
                    }
                    $pdns = new PowerDNSIntegration($db);
                    $summary = $pdns->addMailDiscoveryRecordsToAllDomains();

                    if ($summary['mail_server'] === '') {
                        $message = 'No mail server is configured. Set the mail server hostname above first, then apply.';
                        $message_type = 'danger';
                    } else {
                        $message = sprintf(
                            'Checked %d domain(s); %d use our mail server (%s). %d record(s) added. Domains on external mail and those that already had the records were left unchanged.',
                            (int) $summary['domains'],
                            (int) $summary['eligible'],
                            $summary['mail_server'],
                            (int) $summary['records_added']
                        );
                        $message_type = 'success';
                    }
                } catch (Exception $e) {
                    $message = 'Error applying mail auto-discovery records: ' . $e->getMessage();
                    $message_type = 'danger';
                }
                break;

            case 'update_dns_settings':
                try {
                    $dns_settings = [];

                    $default_ttl = intval($_POST['default_ttl'] ?? 3600);
                    if ($default_ttl < 60 || $default_ttl > 86400) {
                        $default_ttl = 3600;
                    }
                    $dns_settings['default_ttl']        = $default_ttl;
                    $dns_settings['server_name']        = trim($_POST['server_name'] ?? 'WHP Server');
                    $dns_settings['server_description'] = trim($_POST['server_description'] ?? 'Web Hosting Panel Server');

                    $config = [];
                    if (file_exists('/docker/whp/settings.json')) {
                        $config = json_decode(file_get_contents('/docker/whp/settings.json'), true) ?: [];
                    }
                    $config = array_merge($config, $dns_settings);
                    $config['updated_at'] = date('Y-m-d H:i:s');

                    if (file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX) === false) {
                        throw new Exception('Failed to save settings file');
                    }
                    chmod('/docker/whp/settings.json', 0600);

                    $message = 'DNS settings updated successfully';
                    $message_type = 'success';

                    // Update NS records for all domains if PowerDNS integration is available
                    if (file_exists('/docker/whp/web/libs/pdns-integration.php') &&
                        file_exists('/docker/whp/web/libs/mysqlmgmt.php')) {
                        try {
                            require_once('/docker/whp/web/libs/pdns-integration.php');
                            require_once('/docker/whp/web/libs/mysqlmgmt.php');
                            $MySQLMgmt = new mysqlmgmt();
                            $db = $MySQLMgmt->getMySQLConnection();
                            if ($db) {
                                $pdns = new PowerDNSIntegration($db);
                                $pdns->updateAllDomainNSRecords();
                            }
                        } catch (Exception $e) {
                            $message .= ' (Note: PowerDNS update failed: ' . $e->getMessage() . ')';
                        }
                    }
                } catch (Exception $e) {
                    $message = 'Error updating DNS settings: ' . $e->getMessage();
                    $message_type = 'danger';
                }
                break;
                
            case 'create_api_key':
                // Only root users can manage API keys
                if (AUTH_USER !== 'root') {
                    $message = 'Access denied. Only root users can manage API keys.';
                    $message_type = 'danger';
                    break;
                }
                
                if (!empty($_POST['key_name'])) {
                    $keyName = trim($_POST['key_name']);
                    $permissions = [];
                    
                    // Collect permissions
                    $availablePermissions = ['user_management', 'resource_management', 'sso_access', 'system_stats', 'ai_monitor_management'];
                    foreach ($availablePermissions as $perm) {
                        if (!empty($_POST['permissions'][$perm])) {
                            $permissions[$perm] = true;
                        }
                    }
                    
                    if (empty($permissions)) {
                        $message = 'At least one permission must be selected';
                        $message_type = 'danger';
                        break;
                    }
                    
                    try {
                        require_once('/docker/whp/web/libs/ApiKeyManager.php');
                        $apiKeyManager = new ApiKeyManager();
                        
                        $options = [
                            'rate_limit_per_hour' => intval($_POST['rate_limit'] ?? 1000),
                            'ip_whitelist' => !empty($_POST['ip_whitelist']) ? trim($_POST['ip_whitelist']) : null,
                            'notes' => !empty($_POST['notes']) ? trim($_POST['notes']) : null
                        ];
                        
                        $result = $apiKeyManager->generateApiKey($keyName, AUTH_USER, $permissions, $options);
                        
                        if ($result['success']) {
                            // Store the secret temporarily in the session for display
                            $_SESSION['new_api_secret'] = $result['api_secret'];
                            $_SESSION['new_api_key'] = $result['api_key'];
                            
                            $message = 'API key created successfully. Please copy the secret now - it will not be shown again.';
                            $message_type = 'success';
                        } else {
                            $message = 'Failed to create API key: ' . $result['error'];
                            $message_type = 'danger';
                        }
                    } catch (Exception $e) {
                        $message = 'Error creating API key: ' . $e->getMessage();
                        $message_type = 'danger';
                    }
                } else {
                    $message = 'API key name is required';
                    $message_type = 'danger';
                }
                break;
                
            case 'deactivate_api_key':
                // Only root users can manage API keys
                if (AUTH_USER !== 'root') {
                    $message = 'Access denied. Only root users can manage API keys.';
                    $message_type = 'danger';
                    break;
                }
                
                if (!empty($_POST['key_id'])) {
                    try {
                        require_once('/docker/whp/web/libs/ApiKeyManager.php');
                        $apiKeyManager = new ApiKeyManager();
                        
                        $result = $apiKeyManager->deactivateApiKey($_POST['key_id'], AUTH_USER);
                        
                        if ($result['success']) {
                            $message = 'API key deactivated successfully';
                            $message_type = 'success';
                        } else {
                            $message = 'Failed to deactivate API key: ' . $result['error'];
                            $message_type = 'danger';
                        }
                    } catch (Exception $e) {
                        $message = 'Error deactivating API key: ' . $e->getMessage();
                        $message_type = 'danger';
                    }
                } else {
                    $message = 'API key ID is required';
                    $message_type = 'danger';
                }
                break;
                
            case 'cleanup_sso_tokens':
                // Only root users can manage API keys
                if (AUTH_USER !== 'root') {
                    $message = 'Access denied. Only root users can manage API keys.';
                    $message_type = 'danger';
                    break;
                }
                
                try {
                    require_once('/docker/whp/web/libs/ApiKeyManager.php');
                    $apiKeyManager = new ApiKeyManager();
                    
                    $result = $apiKeyManager->cleanupExpiredTokens();
                    
                    if ($result['success']) {
                        $deletedCount = $result['deleted_count'];
                        $message = "Cleaned up $deletedCount expired SSO tokens";
                        $message_type = 'success';
                    } else {
                        $message = 'Failed to cleanup SSO tokens: ' . $result['error'];
                        $message_type = 'danger';
                    }
                } catch (Exception $e) {
                    $message = 'Error cleaning up SSO tokens: ' . $e->getMessage();
                    $message_type = 'danger';
                }
                break;

            case 'update_smtp_settings':
                $smtp_host = trim($_POST['smtp_host'] ?? '');
                $smtp_port = intval($_POST['smtp_port'] ?? 587);
                $smtp_encryption = $_POST['smtp_encryption'] ?? 'tls';
                $smtp_username = trim($_POST['smtp_username'] ?? '');
                $smtp_password = $_POST['smtp_password'] ?? '';
                $smtp_from_email = trim($_POST['smtp_from_email'] ?? '');
                $smtp_from_name = trim($_POST['smtp_from_name'] ?? 'WHP AI Monitor');
                $smtp_enabled = !empty($_POST['smtp_enabled']);

                // Validate required fields when enabling
                if ($smtp_enabled) {
                    if (empty($smtp_host)) {
                        $message = 'SMTP host is required';
                        $message_type = 'danger';
                        break;
                    }
                    if ($smtp_port < 1 || $smtp_port > 65535) {
                        $message = 'SMTP port must be between 1 and 65535';
                        $message_type = 'danger';
                        break;
                    }
                    if (!in_array($smtp_encryption, ['none', 'tls', 'ssl'])) {
                        $message = 'Invalid encryption type';
                        $message_type = 'danger';
                        break;
                    }
                    if (empty($smtp_from_email) || !filter_var($smtp_from_email, FILTER_VALIDATE_EMAIL)) {
                        $message = 'A valid From email address is required';
                        $message_type = 'danger';
                        break;
                    }
                }

                // Load existing config
                $config = [];
                if (file_exists('/docker/whp/settings.json')) {
                    $config = json_decode(file_get_contents('/docker/whp/settings.json'), true) ?: [];
                }

                // Encrypt password if provided, otherwise keep existing
                require_once('/docker/whp/web/libs/smtp_mailer.php');
                $encrypted_password = '';
                if (!empty($smtp_password) && $smtp_password !== '********') {
                    $encrypted_password = SmtpMailer::encryptPassword($smtp_password);
                } elseif (isset($config['smtp']['password'])) {
                    $encrypted_password = $config['smtp']['password'];
                }

                $config['smtp'] = [
                    'host' => $smtp_host,
                    'port' => $smtp_port,
                    'encryption' => $smtp_encryption,
                    'username' => $smtp_username,
                    'password' => $encrypted_password,
                    'from_email' => $smtp_from_email,
                    'from_name' => $smtp_from_name,
                    'enabled' => $smtp_enabled
                ];
                $config['updated_at'] = date('Y-m-d H:i:s');

                file_put_contents('/docker/whp/settings.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
                chmod('/docker/whp/settings.json', 0600);

                $message = 'SMTP settings updated successfully';
                $message_type = 'success';
                break;

            case 'update_bichon_settings':
                // Only root users can manage Bichon archival settings
                if (AUTH_USER !== 'root') {
                    error_log('server-settings.php: non-root user ' . AUTH_USER . ' attempted update_bichon_settings');
                    $message = 'Access denied. Only root users can manage Bichon archival settings.';
                    $message_type = 'danger';
                    break;
                }

                $bichon_enabled = !empty($_POST['bichon_enabled']) ? 1 : 0;
                $bichon_url     = trim($_POST['bichon_url'] ?? '');
                // Read token raw — do NOT trim before the masked-value check
                $bichon_token_raw = $_POST['bichon_api_token'] ?? '';

                // Validate URL format only when a URL is supplied (matches the
                // FILTER_VALIDATE_URL precedent in update_dns_settings).
                if ($bichon_url !== '' && !filter_var($bichon_url, FILTER_VALIDATE_URL)) {
                    $message = 'Bichon URL is invalid';
                    $message_type = 'danger';
                    break;
                }

                // Load existing settings.json
                $config = [];
                if (file_exists('/docker/whp/settings.json')) {
                    $config = json_decode(file_get_contents('/docker/whp/settings.json'), true) ?: [];
                }

                // Preserve any existing bichon block (notably last_test_ok_at and
                // the encrypted api_token when the user kept the masked value).
                if (!isset($config['bichon']) || !is_array($config['bichon'])) {
                    $config['bichon'] = [];
                }

                $config['bichon']['enabled'] = (bool)$bichon_enabled;
                $config['bichon']['url']     = $bichon_url;

                // Only re-encrypt and overwrite the token if the user supplied a
                // new one (not the masked placeholder, not empty).
                if ($bichon_token_raw !== '' && $bichon_token_raw !== '********') {
                    require_once('/docker/whp/web/libs/smtp_mailer.php');
                    $config['bichon']['api_token'] = SmtpMailer::encryptPassword(trim($bichon_token_raw));
                }

                $config['updated_at'] = date('Y-m-d H:i:s');

                // Atomic write with LOCK_EX (matches the rest of this file's settings.json writes)
                $settings_json = json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
                if (file_put_contents('/docker/whp/settings.json', $settings_json, LOCK_EX) === false) {
                    $message = 'Failed to save Bichon settings';
                    $message_type = 'danger';
                    break;
                }
                chmod('/docker/whp/settings.json', 0600);

                $message = 'Bichon archival settings updated successfully';
                $message_type = 'success';
                break;

            case 'send_test_email':
                $test_email = trim($_POST['test_email'] ?? '');
                if (empty($test_email) || !filter_var($test_email, FILTER_VALIDATE_EMAIL)) {
                    $message = 'A valid test email address is required';
                    $message_type = 'danger';
                    break;
                }

                require_once('/docker/whp/web/libs/smtp_mailer.php');
                $result = SmtpMailer::sendTestEmail($test_email);
                if ($result['success']) {
                    $message = 'Test email sent successfully to ' . htmlspecialchars($test_email);
                    $message_type = 'success';
                } else {
                    $message = 'Failed to send test email: ' . htmlspecialchars($result['error']);
                    $message_type = 'danger';
                }
                break;
        }
    }
}

// Load current configuration
$current_config = [];
if (file_exists('/docker/whp/settings.json')) {
    $current_config = json_decode(file_get_contents('/docker/whp/settings.json'), true) ?: [];
}

// Load backup local storage config with actual disk usage
$backup_storage_config = ['max_size_gb' => 100, 'current_size_bytes' => 0];
try {
    require_once('/docker/whp/web/libs/BackupLocalStorage.php');
    $localStorage = new \WHPBackup\BackupLocalStorage();
    $spaceInfo = $localStorage->getSpaceInfo();
    $backup_storage_config = [
        'max_size_gb' => $spaceInfo['max_size_gb'],
        'current_size_bytes' => $spaceInfo['current_size_bytes']
    ];
} catch (Exception $e) {
    error_log("Error loading backup storage config: " . $e->getMessage());
    // Fallback to database value if BackupLocalStorage fails
    try {
        require_once('/docker/whp/web/libs/mysqlmgmt.php');
        $mysql = new \mysqlmgmt();
        $dbConn = $mysql->getMySQLConnection();
        $dbConn->exec("USE whp");
        $stmt = $dbConn->query("SELECT max_size_gb, current_size_bytes FROM backup_local_storage WHERE id = 1");
        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
        if ($row) {
            $backup_storage_config = $row;
        }
    } catch (Exception $e2) {
        error_log("Error loading backup storage fallback: " . $e2->getMessage());
    }
}

// Load API keys (root only)
$apiKeys = [];
if (defined('AUTH_USER') && AUTH_USER === 'root') {
    try {
        require_once('/docker/whp/web/libs/ApiKeyManager.php');
        $apiKeyManager = new ApiKeyManager();
        $apiKeysResult = $apiKeyManager->getApiKeys();
        if ($apiKeysResult['success']) {
            $apiKeys = $apiKeysResult['keys'];
        }
    } catch (Exception $e) {
        error_log("Error loading API keys: " . $e->getMessage());
    }
}

// Get current system information
$current_hostname = trim(shell_exec('hostname'));
$current_timezone = trim(shell_exec('timedatectl show --property=Timezone --value'));

// Get current nameservers
$current_nameservers = [];
if (file_exists('/etc/resolv.conf')) {
    $resolv_content = file_get_contents('/etc/resolv.conf');
    preg_match_all('/nameserver\s+([^\s]+)/', $resolv_content, $matches);
    $current_nameservers = $matches[1];
}

// Get system status
$service_status = [];
$services = ['httpd', 'php-fpm', 'docker', 'proftpd', 'whp-backup-upload'];
foreach ($services as $service) {
    $status = trim(shell_exec("systemctl is-active $service 2>/dev/null"));
    $service_status[$service] = $status;
}

//Get Server IP Address
require_once('/docker/whp/web/libs/dns_validator.php');
$dns_validator = new dns_validator();
$server_ip = $dns_validator->getServerIPAddress();

// Get Docker container status
$container_status = [];
$containers = ['mysql', 'haproxy-manager', 'memcache', 'postgresql'];
foreach ($containers as $container) {
    $status = trim(shell_exec("docker ps --filter name=$container --format '{{.Status}}' 2>/dev/null"));
    if (!empty($status)) {
        $container_status[$container] = 'running';
    } else {
        // Check if container exists but is stopped
        $exists = trim(shell_exec("docker ps -a --filter name=$container --format '{{.Names}}' 2>/dev/null"));
        $container_status[$container] = !empty($exists) ? 'stopped' : 'not_found';
    }
}

// Get shared-httpd instance status
$shared_httpd_instances = [];
$shared_httpd_output = trim(shell_exec("docker ps -a --filter 'name=shared-httpd-' --format '{{.Names}}|{{.Status}}' 2>/dev/null"));
if (!empty($shared_httpd_output)) {
    foreach (explode("\n", $shared_httpd_output) as $line) {
        $parts = explode('|', $line, 2);
        if (count($parts) === 2) {
            $shared_httpd_instances[trim($parts[0])] = strpos($parts[1], 'Up') !== false ? 'running' : 'stopped';
        }
    }
}
ksort($shared_httpd_instances);

// Load shared httpd settings from config
$shared_httpd_settings = $current_config['shared_httpd'] ?? [
    'default_mode' => 'standalone',
];

// Get system information
$system_info = [
    'uptime' => trim(shell_exec('uptime -p')),
    'load_avg' => trim(shell_exec('uptime | grep -oP "load average: \K[^;]+$"')),
    'disk_usage' => trim(shell_exec("df -h / | awk 'NR==2 {print $5}'")),
    'memory_usage' => trim(shell_exec("free | grep Mem | awk '{printf \"%.1f\", $3/$2 * 100.0}'")),
    'kernel' => trim(shell_exec('uname -r')),
    'os' => trim(shell_exec('cat /etc/redhat-release 2>/dev/null || cat /etc/os-release | grep PRETTY_NAME | cut -d"=" -f2 | tr -d \'"\''))
];



// Get available timezones (common ones)
$common_timezones = [
    'America/New_York',
    'America/Chicago',
    'America/Denver',
    'America/Los_Angeles',
    'America/Phoenix',
    'America/Anchorage',
    'Pacific/Honolulu',
    'Europe/London',
    'Europe/Paris',
    'Europe/Berlin',
    'Europe/Rome',
    'Asia/Tokyo',
    'Asia/Shanghai',
    'Asia/Kolkata',
    'Australia/Sydney',
    'UTC'
];

// Get DNS settings with defaults
$mail_server = $current_config['mail_server'] ?? 'mail.example.com';
        $mail_api_url = $current_config['mail_api_url'] ?? 'https://your-mail-server.com/admin/api/v1/';
$mail_api_username = $current_config['mail_api_username'] ?? 'your-api-username';
$mail_api_password = $current_config['mail_api_password'] ?? 'your-api-password';
$server_name = $current_config['server_name'] ?? 'WHP Server';
$server_description = $current_config['server_description'] ?? 'Web Hosting Panel Server';
$default_ttl = $current_config['default_ttl'] ?? 3600;
?>

<div class="row">
    <div class="col-md-12">
        <?php if (!empty($message)): ?>
        <div class="alert alert-<?php echo $message_type; ?> alert-dismissible" role="alert">
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
            <i class="fa fa-<?php echo $message_type === 'success' ? 'check' : 'exclamation-triangle'; ?>"></i>
            <?php echo htmlspecialchars($message); ?>
        </div>
        <?php endif; ?>
    </div>
</div>

<div class="row mt-3">
  <!-- Left rail -->
  <div class="col-md-3">
    <div class="nav flex-column nav-pills" role="tablist" id="server-settings-tabs">
      <button class="nav-link active" data-bs-toggle="pill" data-bs-target="#tab-system"       type="button" role="tab"><i class="fa fa-server"></i> System</button>
      <button class="nav-link"        data-bs-toggle="pill" data-bs-target="#tab-services"     type="button" role="tab"><i class="fa fa-cogs"></i> Services</button>
      <button class="nav-link"        data-bs-toggle="pill" data-bs-target="#tab-mail"         type="button" role="tab"><i class="fa fa-envelope"></i> Mail</button>
      <button class="nav-link"        data-bs-toggle="pill" data-bs-target="#tab-dns"          type="button" role="tab"><i class="fa fa-globe"></i> DNS</button>
      <button class="nav-link"        data-bs-toggle="pill" data-bs-target="#tab-network"      type="button" role="tab"><i class="fa fa-shield"></i> Network &amp; SSL</button>
      <button class="nav-link"        data-bs-toggle="pill" data-bs-target="#tab-security"     type="button" role="tab"><i class="fa fa-lock"></i> Security</button>
    </div>
  </div>
  <!-- Content -->
  <div class="col-md-9">
    <div class="tab-content">
      <div class="tab-pane fade show active" id="tab-system"       role="tabpanel"><?php include __DIR__ . '/server-settings/tabs/system.php'; ?></div>
      <div class="tab-pane fade"             id="tab-services"     role="tabpanel"><?php include __DIR__ . '/server-settings/tabs/services.php'; ?></div>
      <div class="tab-pane fade"             id="tab-mail"         role="tabpanel"><?php include __DIR__ . '/server-settings/tabs/mail.php'; ?></div>
      <div class="tab-pane fade"             id="tab-dns"          role="tabpanel"><?php include __DIR__ . '/server-settings/tabs/dns.php'; ?></div>
      <div class="tab-pane fade"             id="tab-network"      role="tabpanel"><?php include __DIR__ . '/server-settings/tabs/network-ssl.php'; ?></div>
      <div class="tab-pane fade"             id="tab-security"     role="tabpanel"><?php include __DIR__ . '/server-settings/tabs/security.php'; ?></div>
    </div>
  </div>
</div>

<script>
// Persist active server-settings tab in localStorage so reloads (e.g. after a
// form POST) restore the tab the user was on. Bootstrap 5, no jQuery.
(function () {
    var STORAGE_KEY = 'whp_server_settings_active_tab';
    var tablist = document.getElementById('server-settings-tabs');
    if (!tablist) return;

    // Save on tab show
    tablist.querySelectorAll('[data-bs-toggle="pill"]').forEach(function (btn) {
        btn.addEventListener('shown.bs.tab', function (event) {
            var target = event.target.getAttribute('data-bs-target');
            if (target) {
                try { localStorage.setItem(STORAGE_KEY, target); } catch (e) { /* ignore */ }
            }
        });
    });

    // Restore on DOMContentLoaded
    document.addEventListener('DOMContentLoaded', function () {
        var saved = null;
        try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) { /* ignore */ }
        if (!saved) return;
        var btn = tablist.querySelector('[data-bs-target="' + saved + '"]');
        if (btn && window.bootstrap && bootstrap.Tab) {
            bootstrap.Tab.getOrCreateInstance(btn).show();
        }
    });
})();
</script>

<script>
// Bootstrap 5 toast notification function
function showToast(message, type = 'success') {
    // Create toast container if it doesn't exist
    let toastContainer = document.getElementById('toast-container');
    if (!toastContainer) {
        toastContainer = document.createElement('div');
        toastContainer.id = 'toast-container';
        toastContainer.className = 'toast-container position-fixed bottom-0 end-0 p-3';
        toastContainer.style.zIndex = '1055';
        document.body.appendChild(toastContainer);
    }
    
    // Create toast HTML
    const toastId = 'toast-' + Date.now();
    const iconClass = type === 'success' ? 'fa-check-circle text-success' : 
                     type === 'danger' ? 'fa-exclamation-triangle text-danger' : 
                     'fa-info-circle text-info';
    
    const toastHTML = `
        <div id="${toastId}" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
            <div class="toast-header">
                <i class="fa ${iconClass} me-2"></i>
                <strong class="me-auto">Notification</strong>
                <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
            </div>
            <div class="toast-body">
                ${message}
            </div>
        </div>
    `;
    
    // Add toast to container
    toastContainer.insertAdjacentHTML('beforeend', toastHTML);
    
    // Initialize and show toast
    const toastElement = document.getElementById(toastId);
    const toast = new bootstrap.Toast(toastElement, {
        autohide: true,
        delay: 3000
    });
    
    // Remove toast element after it's hidden
    toastElement.addEventListener('hidden.bs.toast', function() {
        toastElement.remove();
    });
    
    toast.show();
}

// Copy to clipboard function with fallback
function copyToClipboard(text) {
    if (typeof text !== 'string') {
        // If text is an element ID, get the text content
        const element = document.getElementById(text);
        text = element ? element.textContent : text;
    }
    
    // Check if modern clipboard API is available and we're in secure context
    if (navigator.clipboard && window.isSecureContext) {
        navigator.clipboard.writeText(text).then(function() {
            showToast('Copied to clipboard!', 'success');
        }).catch(function(err) {
            console.error('Clipboard API failed: ', err);
            fallbackCopyToClipboard(text);
        });
    } else {
        // Fallback for older browsers or non-HTTPS
        fallbackCopyToClipboard(text);
    }
}

// Fallback copy method using execCommand
function fallbackCopyToClipboard(text) {
    const textArea = document.createElement('textarea');
    textArea.value = text;
    textArea.style.position = 'fixed';
    textArea.style.left = '-999999px';
    textArea.style.top = '-999999px';
    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();
    
    try {
        const successful = document.execCommand('copy');
        if (successful) {
            showToast('Copied to clipboard!', 'success');
        } else {
            showToast('Failed to copy to clipboard', 'danger');
        }
    } catch (err) {
        console.error('Fallback copy failed: ', err);
        showToast('Copy to clipboard not supported in this browser', 'danger');
    } finally {
        document.body.removeChild(textArea);
    }
}
</script>

<script>
// AJAX-based service management
document.addEventListener('DOMContentLoaded', function() {
    // CSRF token for AJAX requests
    const csrfToken = '<?php echo get_csrf_token(); ?>';
    
    // Service restart form handling
    const serviceRestartForm = document.getElementById('serviceRestartForm');
    if (serviceRestartForm) {
        serviceRestartForm.addEventListener('submit', function(e) {
            e.preventDefault();
            
            const checkedServices = this.querySelectorAll('input[name="services[]"]:checked');
            if (checkedServices.length === 0) {
                showAlert('Please select at least one service to restart.', 'warning');
                return;
            }
            
            if (!confirm('Are you sure you want to restart the selected services? This may cause temporary service interruption.')) {
                return;
            }
            
            const formData = new FormData(this);
            const submitBtn = document.getElementById('restartServicesBtn');
            const originalText = submitBtn.innerHTML;
            
            // Show loading state
            submitBtn.disabled = true;
            submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Restarting...';
            
            // Make AJAX request
            fetch('index.php?page=server-settings', {
                method: 'POST',
                headers: {
                    'X-CSRF-Token': csrfToken
                },
                body: formData
            })
            .then(response => response.text())
            .then(data => {
                // Parse the response to extract message
                const parser = new DOMParser();
                const doc = parser.parseFromString(data, 'text/html');
                const alertElement = doc.querySelector('.alert');
                
                if (alertElement) {
                    const message = alertElement.textContent.trim();
                    const alertClass = alertElement.className.includes('alert-success') ? 'success' : 
                                     alertElement.className.includes('alert-danger') ? 'danger' : 'info';
                    showAlert(message, alertClass);
                    
                    if (alertClass === 'success') {
                        // Update service status after successful restart
                        setTimeout(updateServiceStatus, 2000);
                    }
                } else {
                    showAlert('Service restart completed. Status will update shortly.', 'success');
                    setTimeout(updateServiceStatus, 2000);
                }
            })
            .catch(error => {
                console.error('Error:', error);
                showAlert('An error occurred while restarting services.', 'danger');
            })
            .finally(() => {
                // Restore button state
                submitBtn.disabled = false;
                submitBtn.innerHTML = originalText;
            });
        });
    }
    
    // Docker container form handling
    const dockerContainerForm = document.getElementById('dockerContainerForm');
    if (dockerContainerForm) {
        dockerContainerForm.addEventListener('submit', function(e) {
            e.preventDefault();
            
            const container = this.querySelector('select[name="container"]').value;
            const operation = this.querySelector('select[name="operation"]').value;
            
            if (!container || !operation) {
                showAlert('Please select both container and operation.', 'warning');
                return;
            }
            
            let message = `Are you sure you want to ${operation} the ${container} container?`;
            if (operation === 'pull_recreate') {
                message = `Are you sure you want to pull the latest image and recreate the ${container} container? This will stop the current container and start a new one.`;
            }
            
            if (!confirm(message)) {
                return;
            }
            
            const formData = new FormData(this);
            const submitBtn = document.getElementById('executeDockerBtn');
            const originalText = submitBtn.innerHTML;
            
            // Show loading state
            submitBtn.disabled = true;
            submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Executing...';
            
            // Make AJAX request
            fetch('index.php?page=server-settings', {
                method: 'POST',
                headers: {
                    'X-CSRF-Token': csrfToken
                },
                body: formData
            })
            .then(response => response.text())
            .then(data => {
                // Parse the response to extract message
                const parser = new DOMParser();
                const doc = parser.parseFromString(data, 'text/html');
                const alertElement = doc.querySelector('.alert');
                
                if (alertElement) {
                    const message = alertElement.textContent.trim();
                    const alertClass = alertElement.className.includes('alert-success') ? 'success' : 
                                     alertElement.className.includes('alert-danger') ? 'danger' : 'info';
                    showAlert(message, alertClass);
                    
                    if (alertClass === 'success') {
                        // Update container status after successful operation
                        setTimeout(updateContainerStatus, 2000);
                    }
                } else {
                    showAlert('Container operation completed. Status will update shortly.', 'success');
                    setTimeout(updateContainerStatus, 2000);
                }
            })
            .catch(error => {
                console.error('Error:', error);
                showAlert('An error occurred while executing container operation.', 'danger');
            })
            .finally(() => {
                // Restore button state
                submitBtn.disabled = false;
                submitBtn.innerHTML = originalText;
                
                // Reset form
                this.reset();
            });
        });
    }

    // Shared httpd pool operations form
    const sharedHttpdForm = document.getElementById('sharedHttpdForm');
    if (sharedHttpdForm) {
        sharedHttpdForm.addEventListener('submit', function(e) {
            e.preventDefault();

            // Find which button was clicked
            const clickedBtn = e.submitter;
            if (!clickedBtn) return;

            const operation = clickedBtn.value;
            const operationLabels = {
                'scale': 'scale the shared httpd pool',
                'restart_all': 'restart all shared httpd instances',
                'pull_recreate_all': 'pull the latest image and recreate all shared httpd instances',
                'stop_all': 'stop all shared httpd instances',
                'start_all': 'start all shared httpd instances'
            };

            if (!confirm('Are you sure you want to ' + (operationLabels[operation] || operation) + '?')) {
                return;
            }

            const formData = new FormData(this);
            formData.set('operation', operation);
            const originalText = clickedBtn.innerHTML;
            clickedBtn.disabled = true;
            clickedBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Working...';

            // Disable all buttons during operation
            const allBtns = this.querySelectorAll('.shared-httpd-btn');
            allBtns.forEach(btn => btn.disabled = true);

            fetch('index.php?page=server-settings', {
                method: 'POST',
                headers: { 'X-CSRF-Token': csrfToken },
                body: formData
            })
            .then(response => response.text())
            .then(data => {
                const parser = new DOMParser();
                const doc = parser.parseFromString(data, 'text/html');
                const alertElement = doc.querySelector('.alert');
                if (alertElement) {
                    const message = alertElement.textContent.trim();
                    const alertClass = alertElement.className.includes('alert-success') ? 'success' :
                                     alertElement.className.includes('alert-danger') ? 'danger' : 'info';
                    showAlert(message, alertClass);
                } else {
                    showAlert('Shared httpd operation completed.', 'success');
                }
                // Reload page after a moment to update instance status
                setTimeout(() => location.reload(), 2000);
            })
            .catch(error => {
                console.error('Error:', error);
                showAlert('An error occurred during the shared httpd operation.', 'danger');
            })
            .finally(() => {
                clickedBtn.disabled = false;
                clickedBtn.innerHTML = originalText;
                allBtns.forEach(btn => btn.disabled = false);
            });
        });
    }

    // Shared httpd settings form
    const sharedHttpdSettingsForm = document.getElementById('sharedHttpdSettingsForm');
    if (sharedHttpdSettingsForm) {
        sharedHttpdSettingsForm.addEventListener('submit', function(e) {
            e.preventDefault();
            const formData = new FormData(this);
            const saveBtn = document.getElementById('saveSharedHttpdSettingsBtn');
            const originalText = saveBtn.innerHTML;
            saveBtn.disabled = true;
            saveBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Saving...';

            fetch('index.php?page=server-settings', {
                method: 'POST',
                headers: { 'X-CSRF-Token': csrfToken },
                body: formData
            })
            .then(response => response.text())
            .then(data => {
                const parser = new DOMParser();
                const doc = parser.parseFromString(data, 'text/html');
                const alertElement = doc.querySelector('.alert');
                if (alertElement) {
                    const message = alertElement.textContent.trim();
                    const alertClass = alertElement.className.includes('alert-success') ? 'success' : 'danger';
                    showAlert(message, alertClass);
                } else {
                    showAlert('Shared httpd settings saved.', 'success');
                }
            })
            .catch(error => {
                console.error('Error:', error);
                showAlert('An error occurred saving shared httpd settings.', 'danger');
            })
            .finally(() => {
                saveBtn.disabled = false;
                saveBtn.innerHTML = originalText;
            });
        });
    }

    // Function to show alerts
    function showAlert(message, type = 'info') {
        // Remove existing alerts
        const existingAlerts = document.querySelectorAll('.alert');
        existingAlerts.forEach(alert => alert.remove());
        
        // Create new alert
        const alertDiv = document.createElement('div');
        alertDiv.className = `alert alert-${type} alert-dismissible`;
        alertDiv.innerHTML = `
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
            <i class="fa fa-${type === 'success' ? 'check' : type === 'danger' ? 'exclamation-triangle' : 'info-circle'}"></i>
            ${message}
        `;
        
        // Insert at the top of the page
        const container = document.querySelector('.row:first-child');
        container.insertBefore(alertDiv, container.firstChild);
        
        // Auto-dismiss success messages after 5 seconds
        if (type === 'success') {
            setTimeout(() => {
                if (alertDiv.parentNode) {
                    alertDiv.remove();
                }
            }, 5000);
        }
    }
    
    // Function to update service status
    function updateServiceStatus() {
        fetch('ajax-status.php')
            .then(response => {
                console.log('Response status:', response.status);
                console.log('Response headers:', response.headers);
                return response.text();
            })
            .then(text => {
                console.log('Raw response:', text.substring(0, 200) + '...');
                try {
                    const data = JSON.parse(text);
                    console.log('Parsed data:', data);
                    if (data.service_status) {
                        Object.keys(data.service_status).forEach(service => {
                            const status = data.service_status[service];
                            const statusElement = document.querySelector(`[data-service="${service}"]`);
                            if (statusElement) {
                                const badge = statusElement.querySelector('.badge');
                                if (badge) {
                                    badge.className = status === 'active' ? 'badge bg-success fs-6' : 'badge bg-danger fs-6';
                                    badge.textContent = status === 'active' ? 'Active' : 'Inactive';
                                }
                            }
                        });
                    }
                } catch (e) {
                    console.error('JSON parse error:', e);
                    console.error('Response text:', text);
                }
            })
            .catch(error => console.error('Error updating service status:', error));
    }
    
    // Function to update container status
    function updateContainerStatus() {
        fetch('ajax-status.php')
            .then(response => {
                console.log('Container status response status:', response.status);
                return response.text();
            })
            .then(text => {
                console.log('Container status raw response:', text.substring(0, 200) + '...');
                try {
                    const data = JSON.parse(text);
                    console.log('Container status parsed data:', data);
                    if (data.container_status) {
                        Object.keys(data.container_status).forEach(container => {
                            const status = data.container_status[container];
                            const statusElement = document.querySelector(`[data-container="${container}"]`);
                            if (statusElement) {
                                const badge = statusElement.querySelector('.badge');
                                if (badge) {
                                    let statusClass = 'bg-success';
                                    let statusText = 'Running';
                                    
                                    if (status === 'stopped') {
                                        statusClass = 'bg-warning';
                                        statusText = 'Stopped';
                                    } else if (status === 'not_found') {
                                        statusClass = 'bg-danger';
                                        statusText = 'Not Found';
                                    }
                                    
                                    badge.className = `badge ${statusClass} fs-6`;
                                    badge.textContent = statusText;
                                }
                            }
                        });
                    }
                } catch (e) {
                    console.error('Container status JSON parse error:', e);
                    console.error('Container status response text:', text);
                }
            })
            .catch(error => console.error('Error updating container status:', error));
    }
});
</script> 