<?php
// TODO: The .form-text under form boxes is hard to read on dark backgrounds. Consider using .text-light or a custom color for better contrast.
// Permission checking is handled by the permission system in auto-prepend.php

require_once('/docker/whp/web/libs/mysqlmgmt.php');
require_once('/docker/whp/web/libs/docker-api.php');
require_once('/docker/whp/web/libs/pdns-integration.php');
require_once('/docker/whp/web/libs/docker.php');
require_once('/docker/whp/web/libs/customer_state.php');
require_once('/docker/whp/web/libs/account_disable.php');

$message = '';
$message_type = '';
$is_root = (defined('AUTH_USER') && AUTH_USER === 'root');

// Build status_map (username -> ['autostart','reason']) from the accounts_disabled
// table — account-level is the unit of suspension. A site shows as Disabled iff
// its owning account is in accounts_disabled; otherwise Active. This is correct
// regardless of any per-site state-file entries: account suspension cascades to
// all sites, and an admin who individually disables a single site would still
// want it reflected, but for the listing the account-level rollup is what
// matters. (Per-site status is shown in the site-detail panel.)
$status_map = [];
if ($is_root) {
    try {
        $_status_mgmt = new mysqlmgmt();
        $_status_db = $_status_mgmt->getMySQLConnection();
        if ($_status_db) {
            $rows = $_status_db->query("SELECT username, reason FROM whp.accounts_disabled")
                               ->fetchAll(PDO::FETCH_ASSOC);
            foreach ($rows as $r) {
                $status_map[$r['username']] = [
                    'autostart' => 'false',
                    'reason'    => $r['reason'] ?? '',
                ];
            }
        }
    } catch (Throwable $_e) {
        // Table may not exist yet on first deploy — fall back to "all active"
        error_log("[sites.php] status_map query failed: " . $_e->getMessage());
    }
}

// Load shared httpd default mode from settings
$shared_httpd_default_mode = 'standalone';
if ($is_root && file_exists('/docker/whp/settings.json')) {
    $whp_settings = json_decode(file_get_contents('/docker/whp/settings.json'), true);
    $shared_httpd_default_mode = $whp_settings['shared_httpd']['default_mode'] ?? 'standalone';
}

// Initialize database connection
$MySQLMgmt = new mysqlmgmt();
$db = $MySQLMgmt->getMySQLConnection();

if (!$db) {
    $message = 'Database connection failed';
    $message_type = 'danger';
} else {
    // Initialize Docker API
    $DockerAPI = new docker_api();
    // Initialize PowerDNS Integration
    $PowerDNS = new PowerDNSIntegration($db);
}

// Handle form submissions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_POST['action'])) {
        switch ($_POST['action']) {
            case 'create_site':
                if (!empty($_POST['site_name']) && !empty($_POST['primary_domain_id']) && !empty($_POST['container_type_id'])) {
                    try {
                        $site_name = trim($_POST['site_name']);
                        $primary_fqdn = trim($_POST['primary_domain_id']);
                        $container_type_id = intval($_POST['container_type_id']);
                        $container_count = intval($_POST['container_count'] ?? 1);
                        $cpu_per_container = floatval($_POST['cpu_per_container'] ?? 0.25);
                        $memory_per_container = intval($_POST['memory_per_container'] ?? 256);
                        
                        // Detect wildcard site
                        $is_wildcard_site = (strpos($primary_fqdn, '*.') === 0);
                        if ($is_wildcard_site) {
                            // Validate wildcard format: must be *.domain.tld with valid domain characters
                            $wildcard_base = substr($primary_fqdn, 2);
                            if (substr_count($wildcard_base, '.') < 1 || !preg_match('/^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?$/', $wildcard_base)) {
                                $message = 'Invalid wildcard format. Must be *.domain.tld with valid domain characters';
                                $message_type = 'danger';
                                break;
                            }
                        }
                        // Compute safe FQDN for directory and container names
                        $safe_fqdn = $is_wildcard_site ? str_replace('*.', 'wildcard.', $primary_fqdn) : $primary_fqdn;

                        // Validate container count
                        if ($container_count < 1 || $container_count > 10) {
                            $message = 'Container count must be between 1 and 10';
                            $message_type = 'danger';
                            break;
                        }

                        // Get domain info and check ownership
                        $primary_domain_row = null;
                        $all_domains = $db->query("SELECT id, domain_name, username FROM whp.domains WHERE active=1")->fetchAll(PDO::FETCH_ASSOC);
                        foreach ($all_domains as $row) {
                            if ($primary_fqdn === $row['domain_name'] || (str_ends_with($primary_fqdn, '.' . $row['domain_name']))) {
                                $primary_domain_row = $row;
                                break;
                            }
                            // Also match wildcard: *.domain.tld belongs to domain.tld
                            if ($is_wildcard_site && $primary_fqdn === '*.' . $row['domain_name']) {
                                $primary_domain_row = $row;
                                break;
                            }
                        }
                        if (!$primary_domain_row) {
                            $message = 'Primary domain/subdomain not found or not owned by you.';
                            $message_type = 'danger';
                            break;
                        }
                        if (!$is_root && $primary_domain_row['username'] !== AUTH_USER) {
                            $message = 'You can only create sites with your own domains/subdomains';
                            $message_type = 'danger';
                            break;
                        }
                        // Block site creation for suspended accounts
                        $target_username = $primary_domain_row['username'];
                        if (account_is_disabled($target_username)) {
                            $message = "Cannot create a new site: account '" . htmlspecialchars($target_username) . "' is currently suspended. Enable the account first via Admin > Account Suspensions.";
                            $message_type = 'danger';
                            break;
                        }
                        // Validate FQDN is a valid A or CNAME for this domain
                        $valid_fqdn = false;
                        $fqdn_records = $PowerDNS->getRecords($primary_domain_row['domain_name']);
                        foreach ($fqdn_records as $rec) {
                            if (strcasecmp($rec['name'], $primary_fqdn) === 0 && in_array(strtoupper($rec['type']), ['A','CNAME'])) {
                                $valid_fqdn = true;
                                break;
                            }
                        }
                        if (!$valid_fqdn) {
                            $message = 'Selected primary FQDN is not a valid A or CNAME record.';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Get container type info
                        $stmt = $db->prepare("SELECT * FROM whp.container_types WHERE id = ? AND active = 1");
                        $stmt->execute([$container_type_id]);
                        $container_type = $stmt->fetch();
                        
                        if (!$container_type) {
                            $message = 'Invalid or inactive container type';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Validate resource requirements
                        if ($cpu_per_container < $container_type['min_cpu']) {
                            $message = 'CPU per container must be at least ' . $container_type['min_cpu'] . ' cores';
                            $message_type = 'danger';
                            break;
                        }
                        
                        if ($memory_per_container < $container_type['min_memory']) {
                            $message = 'Memory per container must be at least ' . $container_type['min_memory'] . ' MB';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Check user resource limits (skipped entirely when uncapped)
                        $stmt = $db->prepare("SELECT max_cpu_allowance, max_memory_allowance, uncapped_resources FROM whp.client_allowances WHERE username = ?");
                        $stmt->execute([$primary_domain_row['username']]);
                        $user_limits = $stmt->fetch();

                        if ($user_limits && empty($user_limits['uncapped_resources'])) {
                            // Get current resource usage for the user (all existing sites/containers)
                            $usage = $MySQLMgmt->getUserResourceUsage($primary_domain_row['username']);
                            $current_cpu_used = floatval($usage['cpu_used'] ?? 0);
                            $current_memory_used = intval($usage['memory_used'] ?? 0);
                            // Calculate total resources needed for this new site
                            $new_site_cpu = $cpu_per_container * $container_count;
                            $new_site_memory = $memory_per_container * $container_count;
                            // Calculate projected totals if this site is created
                            $projected_cpu = $current_cpu_used + $new_site_cpu;
                            $projected_memory = $current_memory_used + $new_site_memory;
                            // Enforce total resource limits
                            if ($projected_cpu > $user_limits['max_cpu_allowance']) {
                                $message = 'Total CPU allocation after this site (' . $projected_cpu . ' cores) exceeds your limit (' . $user_limits['max_cpu_allowance'] . ' cores)';
                                $message_type = 'danger';
                                break;
                            }
                            if ($projected_memory > $user_limits['max_memory_allowance']) {
                                $message = 'Total memory allocation after this site (' . $projected_memory . ' MB) exceeds your limit (' . $user_limits['max_memory_allowance'] . ' MB)';
                                $message_type = 'danger';
                                break;
                            }
                        }
                        
                        // Process user variables if provided
                        $user_variables = [];
                        if (!empty($_POST['user_variables'])) {
                            $user_variables = json_decode($_POST['user_variables'], true) ?: [];
                        }
                        
                        // Debug: Log user variables for troubleshooting
                        error_log("User variables received: " . json_encode($user_variables));
                        error_log("Raw POST user_variables: " . ($_POST['user_variables'] ?? 'not set'));
                        
                        // Create site directory using safe FQDN (wildcard.domain.tld for wildcards)
                        $site_dir = '/docker/users/' . $primary_domain_row['username'] . '/' . $safe_fqdn;
                        if (!is_dir($site_dir)) {
                            if (!mkdir($site_dir, 0755, true)) {
                                $message = 'Failed to create site directory';
                                $message_type = 'danger';
                                break;
                            }
                            
                            // Set ownership
                            $uid = trim(shell_exec("id -u " . escapeshellarg($primary_domain_row['username'])));
                            $gid = trim(shell_exec("id -g " . escapeshellarg($primary_domain_row['username'])));
                            if (is_numeric($uid) && is_numeric($gid)) {
                                shell_exec("chown -R {$uid}:{$gid} " . escapeshellarg($site_dir));
                            }
                        }
                        
                        // Get auto-recreation setting
                        $auto_recreate_on_update = isset($_POST['auto_recreate_on_update']) ? 1 : 0;
                        
                        // Insert site into database (use parent domain_id for primary_domain_id)
                        $stmt = $db->prepare("
                            INSERT INTO whp.sites (site_name, primary_domain_id, username, container_type_id, container_count, cpu_per_container, memory_per_container, user_variables, auto_recreate_on_update, is_wildcard)
                            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                        ");
                        $stmt->execute([
                            $site_name,
                            $primary_domain_row['id'],
                            $primary_domain_row['username'],
                            $container_type_id,
                            $container_count,
                            $cpu_per_container,
                            $memory_per_container,
                            json_encode($user_variables),
                            $auto_recreate_on_update,
                            $is_wildcard_site ? 1 : 0
                        ]);
                        
                        $site_id = $db->lastInsertId();
                        
                        // Link primary FQDN to site
                        $stmt = $db->prepare("
                            INSERT INTO whp.site_domains (site_id, domain_id, fqdn, is_primary, is_wildcard)
                            VALUES (?, ?, ?, 1, ?)
                        ");
                        $stmt->execute([$site_id, $primary_domain_row['id'], $primary_fqdn, $is_wildcard_site ? 1 : 0]);
                        
                        // Store the safe FQDN for container naming (uses wildcard. prefix for wildcards)
                        $primary_fqdn_for_containers = $safe_fqdn;
                        
                        // Link additional FQDNs to site
                        $additional_domains_added = [];
                        if (!empty($_POST['additional_domains']) && is_array($_POST['additional_domains'])) {
                            foreach ($_POST['additional_domains'] as $fqdn) {
                                $fqdn = trim($fqdn);
                                if ($fqdn === $primary_fqdn) continue;
                                $parent_row = null;
                                foreach ($all_domains as $row) {
                                    if ($fqdn === $row['domain_name'] || (str_ends_with($fqdn, '.' . $row['domain_name']))) {
                                        $parent_row = $row;
                                        break;
                                    }
                                }
                                if (!$parent_row) continue;
                                if (!$is_root && $parent_row['username'] !== AUTH_USER) continue;
                                // Validate FQDN is a valid A or CNAME for this domain
                                $valid_fqdn = false;
                                $fqdn_records = $PowerDNS->getRecords($parent_row['domain_name']);
                                foreach ($fqdn_records as $rec) {
                                    if (strcasecmp($rec['name'], $fqdn) === 0 && in_array(strtoupper($rec['type']), ['A','CNAME'])) {
                                        $valid_fqdn = true;
                                        break;
                                    }
                                }
                                if (!$valid_fqdn) continue;
                                // Check if already linked
                                $stmt = $db->prepare("SELECT id FROM whp.site_domains WHERE site_id = ? AND domain_id = ? AND fqdn = ?");
                                $stmt->execute([$site_id, $parent_row['id'], $fqdn]);
                                if ($stmt->fetch()) continue;
                                // Link additional FQDN to site
                                $stmt = $db->prepare("
                                    INSERT INTO whp.site_domains (site_id, domain_id, fqdn, is_primary) 
                                    VALUES (?, ?, ?, 0)
                                ");
                                $stmt->execute([$site_id, $parent_row['id'], $fqdn]);
                                $additional_domains_added[] = $fqdn;
                            }
                        }
                        
                        // Create and start Docker containers for the site
                        $container_creation_success = true;
                        $container_errors = [];
                        
                        try {
                            // Get container type details for Docker creation
                            $stmt = $db->prepare("
                                SELECT ct.base_image, ct.startup_env, ct.mount_options, ct.listen_port 
                                FROM whp.container_types ct 
                                WHERE ct.id = ?
                            ");
                            $stmt->execute([$container_type_id]);
                            $container_type_details = $stmt->fetch();
                            
                            // Get user UID for container configuration
                            $uid = trim(shell_exec("id -u " . escapeshellarg($primary_domain_row['username'])));
                            if (!is_numeric($uid)) {
                                throw new Exception("Invalid user UID");
                            }
                            
                            // Create containers
                            for ($i = 1; $i <= $container_count; $i++) {
                                // Create container name using actual FQDN to prevent conflicts with apex domains
                                $container_name = $primary_fqdn_for_containers . '-' . str_pad($i, 2, '0', STR_PAD_LEFT);
                                
                                // Prepare environment variables
                                $env_vars = [
                                    'WHP_USER=' . $primary_domain_row['username'],
                                    'WHP_UID=' . $uid,
                                    'WHP_DOMAIN=' . $primary_fqdn,
                                    'WHP_CONTAINER_NUMBER=' . $i
                                ];
                                
                                // Add user variables
                                if (!empty($user_variables)) {
                                    foreach ($user_variables as $var_name => $var_value) {
                                        $env_vars[] = $var_name . '=' . $var_value;
                                    }
                                    error_log("Added user variables to container $i: " . json_encode($user_variables));
                                } else {
                                    error_log("No user variables for container $i");
                                }
                                
                                // Process startup environment variables
                                if (!empty($container_type_details['startup_env'])) {
                                    $startup_env = json_decode($container_type_details['startup_env'], true);
                                    if ($startup_env) {
                                        foreach ($startup_env as $var_name => $var_value) {
                                            // Replace dynamic variables using centralized function with actual FQDN
                                            $var_value = $MySQLMgmt->replaceDynamicVariables($var_value, $primary_domain_row['username'], $primary_fqdn, $uid, $i, $container_type_details['listen_port'] ?? 80);
                                            $env_vars[] = $var_name . '=' . $var_value;
                                        }
                                    }
                                }
                                
                                // Prepare volumes using safe FQDN path (wildcard.domain.tld for wildcards)
                                $volumes = [
                                    '/docker/users/' . $primary_domain_row['username'] . '/' . $safe_fqdn . ':/var/www/html'
                                ];
                                
                                // Process mount options
                                if (!empty($container_type_details['mount_options'])) {
                                    $mount_options = json_decode($container_type_details['mount_options'], true);
                                    if ($mount_options && isset($mount_options['volumes'])) {
                                        foreach ($mount_options['volumes'] as $volume) {
                                            // Replace dynamic variables in both source and target paths using safe FQDN (no * in paths)
                                            $source = $MySQLMgmt->replaceDynamicVariables($volume['source'], $primary_domain_row['username'], $safe_fqdn, $uid, $i, $container_type_details['listen_port'] ?? 80);
                                            $target = $MySQLMgmt->replaceDynamicVariables($volume['target'], $primary_domain_row['username'], $safe_fqdn, $uid, $i, $container_type_details['listen_port'] ?? 80);
                                            $volumes[] = $source . ':' . $target;
                                        }
                                    }
                                }
                                
                                // Create container config
                                $container_config = [
                                    'image' => $container_type_details['base_image'],
                                    'name' => $container_name,
                                    'labels' => [
                                        'whp.user' => $primary_domain_row['username'],
                                        'whp.domain' => $primary_fqdn,
                                        'whp.site_id' => strval($site_id),
                                        'whp.container_number' => strval($i)
                                    ],
                                    'env' => $env_vars,
                                    'volumes' => $volumes,
                                    'cpu' => $cpu_per_container,
                                    'memory' => $memory_per_container,
                                    'network' => 'client-net'
                                ];
                                
                                // Debug: Log container config for troubleshooting
                                error_log("Container config for container $i: " . json_encode($container_config));
                                
                                $result = $DockerAPI->create_container($container_config);
                                
                                // Debug: Log Docker API result
                                error_log("Docker API result for container $i: " . json_encode($result));
                                
                                if ($result['success']) {
                                    // Save container info to database
                                    $stmt = $db->prepare("
                                        INSERT INTO whp.site_containers (site_id, container_id, container_number, status) 
                                        VALUES (?, ?, ?, 'running')
                                    ");
                                    $stmt->execute([$site_id, $result['container_id'], $i]);
                                } else {
                                    $container_creation_success = false;
                                    $container_errors[] = "Failed to create container $i: " . $result['error'];
                                }
                            }
                        } catch (Exception $e) {
                            $container_creation_success = false;
                            $container_errors[] = "Container creation error: " . $e->getMessage();
                        }
                        
                        // Configure HAProxy if containers were created successfully
                        if ($container_creation_success) {
                            require_once('/docker/whp/web/libs/haproxy_manager.php');
                            $haproxy_manager = new haproxy_manager();
                            $haproxy_result = $haproxy_manager->configureSite($site_id, [
                                'username' => $primary_domain_row['username'],
                                'primary_domain' => $primary_domain_row['domain_name'],
                                'container_type_id' => $container_type_id
                            ]);
                            
                            if (!$haproxy_result['success']) {
                                $container_creation_success = false;
                                $container_errors[] = "HAProxy configuration failed: " . $haproxy_result['error'];
                            }
                        }
                        

                        
                        // Enable SSL if requested.
                        //
                        // New behavior (2026.05.NN):
                        //   - "Enable HTTPS" defaults checked in the UI; issuance fires synchronously here.
                        //   - "Use DNS validation" sub-checkbox routes to the wildcard helper (which also
                        //     handles non-wildcard DNS-01) instead of HTTP-01.
                        //   - Wildcard primary domains force dns_validation=1 (UI locks the checkbox).
                        //   - On failure, we DO NOT block site creation. The orchestrator picks the domain
                        //     up on its next sweep and retries with backoff; customer is notified by email
                        //     once the state machine decides it's worth surfacing (see ssl_renewal_orchestrator).
                        $ssl_errors = [];
                        $ssl_enabled_requested = isset($_POST['enable_ssl']) && $_POST['enable_ssl'] == '1';
                        $dns_validation_requested = isset($_POST['dns_validation']) && $_POST['dns_validation'] == '1';
                        // Wildcard always forces DNS-01 regardless of the checkbox.
                        $use_dns_validation = $dns_validation_requested || $is_wildcard_site;

                        if ($ssl_enabled_requested && $container_creation_success) {
                            require_once('/docker/whp/web/libs/ssl_manager.php');
                            $ssl_manager = new ssl_manager();

                            if ($use_dns_validation) {
                                // DNS-01 path covers wildcards and any non-wildcard where the user opted in.
                                // The helper handles both "*.foo.com" and "foo.com" forms.
                                $dns_target = $is_wildcard_site ? $primary_fqdn : $primary_fqdn;
                                $ssl_result = $ssl_manager->enable_wildcard_ssl($site_id, $dns_target);
                                if (!$ssl_result['success']) {
                                    $ssl_errors[] = 'SSL DNS-01 issuance could not start: ' . ($ssl_result['error'] ?? 'Unknown error') . '. We will retry automatically.';
                                } else {
                                    $ssl_errors[] = 'SSL DNS-01 challenge initiated. Certificate will be issued once DNS propagates; you will receive an email if action is required.';
                                }
                            } else {
                                // HTTP-01 path. Pre-flight DNS to give the user immediate feedback
                                // about which domains aren't pointing yet — but do NOT block, since
                                // the orchestrator will retry until DNS is in place.
                                require_once('/docker/whp/web/libs/dns_validator.php');
                                $dns_validator = new dns_validator();

                                $stmt = $db->prepare("
                                    SELECT d.domain_name
                                    FROM whp.site_domains sd
                                    JOIN whp.domains d ON sd.domain_id = d.id
                                    WHERE sd.site_id = ?
                                ");
                                $stmt->execute([$site_id]);
                                $site_domains_for_ssl = $stmt->fetchAll(PDO::FETCH_COLUMN);
                                if (!in_array($primary_domain_row['domain_name'], $site_domains_for_ssl)) {
                                    $site_domains_for_ssl[] = $primary_domain_row['domain_name'];
                                }

                                $validation_results = $dns_validator->validateDomains($site_domains_for_ssl);
                                $invalid_domains = [];
                                foreach ($validation_results['domains'] as $site_domain => $result) {
                                    if (!$result['valid']) {
                                        $invalid_domains[] = $site_domain;
                                    }
                                }

                                $ssl_result = $ssl_manager->enable_ssl($site_id, 'letsencrypt');

                                if (!$ssl_result['success']) {
                                    $ssl_errors[] = 'Initial SSL issuance did not complete: ' . $ssl_result['error'] . '. The system will retry automatically; you will receive an email if action is needed.';
                                } else if (!empty($invalid_domains)) {
                                    $ssl_errors[] = 'SSL issued for reachable domains. The following are not yet pointing to ' . $validation_results['server_ip'] . ' and will be retried automatically: ' . implode(', ', $invalid_domains) . '.';
                                }
                            }
                        }
                        
                        if ($container_creation_success) {
                            $domain_info = '';
                            if (!empty($additional_domains_added)) {
                                $domain_info = ' with additional domains: ' . implode(', ', $additional_domains_added);
                            }
                            
                            // Check if SSL was requested and determine appropriate message
                            $ssl_requested = isset($_POST['enable_ssl']) && $_POST['enable_ssl'] == '1';
                            
                            if ($ssl_requested && empty($ssl_errors)) {
                                $message = 'Site created successfully with ' . $container_count . ' container(s) started, HAProxy configured, and SSL enabled: ' . $site_name . $domain_info;
                                $message_type = 'success';
                            } else if ($ssl_requested && !empty($ssl_errors)) {
                                $message = 'Site created successfully with ' . $container_count . ' container(s) started and HAProxy configured: ' . $site_name . $domain_info . '. However, SSL issues: ' . implode(', ', $ssl_errors);
                                $message_type = 'warning';
                            } else {
                                $message = 'Site created successfully with ' . $container_count . ' container(s) started and HAProxy configured: ' . $site_name . $domain_info;
                                $message_type = 'success';
                            }
                            
                            
                        } else {
                            $message = 'Site created successfully: ' . $site_name . '. However, some operations failed: ' . implode(', ', $container_errors);
                            $message_type = 'warning';
                            
                        }
                    } catch (PDOException $e) {
                        $message = 'Error creating site: ' . $e->getMessage();
                        $message_type = 'danger';
                        
                    }
                }
                break;
                
            case 'delete_site':
                if (!empty($_POST['site_id'])) {
                    try {
                        $site_id = intval($_POST['site_id']);
                        
                        // Get site info
                        $stmt = $db->prepare("SELECT s.*, d.domain_name FROM whp.sites s JOIN whp.domains d ON s.primary_domain_id = d.id WHERE s.id = ?");
                        $stmt->execute([$site_id]);
                        $site = $stmt->fetch();
                        
                        if (!$site) {
                            $message = 'Site not found';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Check ownership
                        if (!$is_root && $site['username'] !== AUTH_USER) {
                            $message = 'You can only delete your own sites';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Stop and remove all containers for this site
                        require_once('/docker/whp/web/libs/docker-api.php');
                        $DockerAPI = new docker_api();
                        
                        $stmt = $db->prepare("SELECT container_id FROM whp.site_containers WHERE site_id = ?");
                        $stmt->execute([$site_id]);
                        $containers = $stmt->fetchAll();
                        
                        $container_errors = [];
                        foreach ($containers as $container) {
                            // Stop container first
                            $stop_result = $DockerAPI->stop_container($container['container_id']);
                            if (!$stop_result['success']) {
                                $container_errors[] = "Failed to stop container " . $container['container_id'] . ": " . $stop_result['error'];
                            }
                            
                            // Remove container
                            $remove_result = $DockerAPI->remove_container($container['container_id']);
                            if (!$remove_result['success']) {
                                $container_errors[] = "Failed to remove container " . $container['container_id'] . ": " . $remove_result['error'];
                            }
                        }
                        
                        // Remove HAProxy configuration
                        require_once('/docker/whp/web/libs/haproxy_manager.php');
                        $haproxy_manager = new haproxy_manager();
                        $haproxy_result = $haproxy_manager->removeSite($site_id);

                        if (!$haproxy_result['success']) {
                            $container_errors[] = "HAProxy cleanup failed: " . $haproxy_result['error'];
                        }

                        // Clean up wildcard DNS records and DNS challenges if this is a wildcard site
                        if (!empty($site['is_wildcard'])) {
                            // Get primary FQDN for the site
                            $stmt = $db->prepare("
                                SELECT sd.fqdn FROM whp.site_domains sd
                                WHERE sd.site_id = ? AND sd.is_primary = 1
                            ");
                            $stmt->execute([$site_id]);
                            $primary_fqdn_row = $stmt->fetch();
                            if ($primary_fqdn_row && strpos($primary_fqdn_row['fqdn'], '*.') === 0) {
                                $base_domain = substr($primary_fqdn_row['fqdn'], 2);
                                $PowerDNS->removeWildcardRecord($base_domain);
                                $PowerDNS->removeDnsChallengeRecord($base_domain);
                            }

                            // Clean up any pending dns_challenges entries
                            $stmt = $db->prepare("DELETE FROM whp.dns_challenges WHERE site_id = ?");
                            $stmt->execute([$site_id]);
                        }

                        // Delete site (cascades to site_domains and site_containers)
                        $stmt = $db->prepare("DELETE FROM whp.sites WHERE id = ?");
                        $stmt->execute([$site_id]);
                        
                        if (empty($container_errors)) {
                            $message = 'Site deleted successfully: ' . $site['site_name'] . '. All containers were stopped and removed, and HAProxy configuration was cleaned up.';
                            $message_type = 'success';
                        } else {
                            $message = 'Site deleted successfully: ' . $site['site_name'] . '. However, some operations had errors: ' . implode(', ', $container_errors);
                            $message_type = 'warning';
                        }
                    } catch (PDOException $e) {
                        $message = 'Error deleting site: ' . $e->getMessage();
                        $message_type = 'danger';
                    }
                }
                break;
                
            case 'add_domain_to_site':
                if (!empty($_POST['site_id']) && !empty($_POST['domain_id'])) {
                    try {
                        $site_id = intval($_POST['site_id']);
                        $domain_id = intval($_POST['domain_id']);
                        
                        // Get site info
                        $stmt = $db->prepare("SELECT username FROM whp.sites WHERE id = ?");
                        $stmt->execute([$site_id]);
                        $site = $stmt->fetch();
                        
                        if (!$site) {
                            $message = 'Site not found';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Check site ownership
                        if (!$is_root && $site['username'] !== AUTH_USER) {
                            $message = 'You can only modify your own sites';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Get domain info
                        $stmt = $db->prepare("SELECT domain_name, username FROM whp.domains WHERE id = ?");
                        $stmt->execute([$domain_id]);
                        $domain = $stmt->fetch();
                        
                        if (!$domain) {
                            $message = 'Domain not found';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Check domain ownership
                        if (!$is_root && $domain['username'] !== AUTH_USER) {
                            $message = 'You can only add your own domains to sites';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Check if domain is already linked to this site
                        $stmt = $db->prepare("SELECT id FROM whp.site_domains WHERE site_id = ? AND domain_id = ?");
                        $stmt->execute([$site_id, $domain_id]);
                        if ($stmt->fetch()) {
                            $message = 'Domain is already linked to this site';
                            $message_type = 'warning';
                            break;
                        }
                        
                        // Link domain to site
                        $stmt = $db->prepare("INSERT INTO whp.site_domains (site_id, domain_id, is_primary) VALUES (?, ?, 0)");
                        $stmt->execute([$site_id, $domain_id]);
                        
                        // Update HAProxy configuration for the site
                        require_once('/docker/whp/web/libs/haproxy_manager.php');
                        $haproxy_manager = new haproxy_manager();
                        $haproxy_result = $haproxy_manager->configureSite($site_id, [
                            'username' => $site['username'],
                            'domain_name' => $domain['domain_name']
                        ]);
                        
                        // Check if the site already has SSL enabled for other domains
                        $ssl_message = '';
                        if ($haproxy_result['success']) {
                            // Check if site has any existing SSL certificates
                            require_once('/docker/whp/web/libs/ssl_manager.php');
                            $ssl_manager = new ssl_manager();
                            
                            // Check if the site already has SSL enabled — derive from
                            // per-domain haproxy_domains.ssl_enabled, not sites.ssl_enabled.
                            // The orchestrator only maintains the per-domain column, so
                            // sites.ssl_enabled can drift; deriving avoids that. The
                            // condition is "any existing domain has a cert" (MAX),
                            // because the intent here is auto-enable on a NEWLY added
                            // domain when the site is already SSL-active for others.
                            $stmt = $db->prepare("
                                SELECT COALESCE(MAX(hd.ssl_enabled), 0) AS ssl_enabled
                                FROM whp.site_domains sd
                                LEFT JOIN whp.haproxy_domains hd
                                    ON hd.domain_name = COALESCE(sd.fqdn, '')
                                WHERE sd.site_id = ?
                            ");
                            $stmt->execute([$site_id]);
                            $ssl_result = $stmt->fetch();

                            if ($ssl_result && $ssl_result['ssl_enabled'] > 0) {
                                // Site has SSL enabled for other domains, automatically enable for new domain
                                $auto_ssl_result = $ssl_manager->enable_ssl($site_id, 'letsencrypt');
                                
                                if ($auto_ssl_result['success']) {
                                    $ssl_message = ' SSL automatically enabled for new domain.';
                                } else {
                                    $ssl_message = ' SSL auto-enable failed: ' . ($auto_ssl_result['error'] ?? 'Unknown error');
                                }
                            }
                            
                            $message = 'Domain added to site successfully and HAProxy updated: ' . $domain['domain_name'] . $ssl_message;
                            $message_type = 'success';
                        } else {
                            $message = 'Domain added to site successfully: ' . $domain['domain_name'] . '. However, HAProxy configuration failed: ' . $haproxy_result['error'];
                            $message_type = 'warning';
                        }
                    } catch (PDOException $e) {
                        $message = 'Error adding domain to site: ' . $e->getMessage();
                        $message_type = 'danger';
                    }
                }
                break;
                
            case 'remove_domain_from_site':
                if (!empty($_POST['site_id']) && !empty($_POST['domain_id'])) {
                    try {
                        $site_id = intval($_POST['site_id']);
                        $domain_id = intval($_POST['domain_id']);
                        
                        // Get site info
                        $stmt = $db->prepare("SELECT username FROM whp.sites WHERE id = ?");
                        $stmt->execute([$site_id]);
                        $site = $stmt->fetch();
                        
                        if (!$site) {
                            $message = 'Site not found';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Check site ownership
                        if (!$is_root && $site['username'] !== AUTH_USER) {
                            $message = 'You can only modify your own sites';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Check if this is the primary domain
                        $stmt = $db->prepare("SELECT is_primary FROM whp.site_domains WHERE site_id = ? AND domain_id = ?");
                        $stmt->execute([$site_id, $domain_id]);
                        $site_domain = $stmt->fetch();
                        
                        if (!$site_domain) {
                            $message = 'Domain is not linked to this site';
                            $message_type = 'warning';
                            break;
                        }
                        
                        if ($site_domain['is_primary']) {
                            $message = 'Cannot remove primary domain from site';
                            $message_type = 'danger';
                            break;
                        }
                        
                        // Remove domain from site
                        $stmt = $db->prepare("DELETE FROM whp.site_domains WHERE site_id = ? AND domain_id = ?");
                        $stmt->execute([$site_id, $domain_id]);
                        
                        // Update HAProxy configuration for the site
                        require_once('/docker/whp/web/libs/haproxy_manager.php');
                        $haproxy_manager = new haproxy_manager();
                        $haproxy_result = $haproxy_manager->configureSite($site_id, [
                            'username' => $site['username']
                        ]);
                        
                        if ($haproxy_result['success']) {
                            $message = 'Domain removed from site successfully and HAProxy updated';
                            $message_type = 'success';
                        } else {
                            $message = 'Domain removed from site successfully. However, HAProxy configuration failed: ' . $haproxy_result['error'];
                            $message_type = 'warning';
                        }
                    } catch (PDOException $e) {
                        $message = 'Error removing domain from site: ' . $e->getMessage();
                        $message_type = 'danger';
                    }
                }
                break;
        }
    }
}

// Get user's sites
try {
    if ($is_root) {
        // Root sees all sites
        $stmt = $db->query("
            SELECT 
                s.*,
                COALESCE(sd.fqdn, d.domain_name) as primary_domain,
                ct.name as container_type_name,
                ct.base_image,
                ct.min_cpu,
                ct.min_memory,
                ct.user_variables as container_user_variables
            FROM whp.sites s
            JOIN whp.domains d ON s.primary_domain_id = d.id
            LEFT JOIN whp.site_domains sd ON s.id = sd.site_id AND sd.is_primary = 1
            JOIN whp.container_types ct ON s.container_type_id = ct.id
            ORDER BY s.site_name ASC
        ");
    } else {
        // Regular users see only their sites
        $stmt = $db->prepare("
            SELECT 
                s.*,
                COALESCE(sd.fqdn, d.domain_name) as primary_domain,
                ct.name as container_type_name,
                ct.base_image,
                ct.min_cpu,
                ct.min_memory,
                ct.user_variables as container_user_variables
            FROM whp.sites s
            JOIN whp.domains d ON s.primary_domain_id = d.id
            LEFT JOIN whp.site_domains sd ON s.id = sd.site_id AND sd.is_primary = 1
            JOIN whp.container_types ct ON s.container_type_id = ct.id
            WHERE s.username = ?
            ORDER BY s.site_name ASC
        ");
        $stmt->execute([AUTH_USER]);
    }
    $sites = $stmt->fetchAll();
} catch (PDOException $e) {
    $sites = [];
    $message = 'Error loading sites: ' . $e->getMessage();
    $message_type = 'danger';
}

// NOTE: $site_owners (root-only typeahead data) and the All Sites Overview
// table moved to pages/sites-overview.php. The "All Sites Overview" button
// at the top of this page links there.

// Get user's domains for site creation
try {
    // Exclude domains already used as a primary domain in whp.sites
    $exclude_primary_domains_sql = "SELECT primary_domain_id FROM whp.sites";
    if ($is_root) {
        $stmt = $db->query("
            SELECT d.*, u.username as owner 
            FROM whp.domains d 
            JOIN whp.client_allowances u ON d.username = u.username
            WHERE d.active = 1
              AND d.id NOT IN ($exclude_primary_domains_sql)
            ORDER BY d.domain_name ASC
        ");
    } else {
        $stmt = $db->prepare("
            SELECT d.*, d.username as owner 
            FROM whp.domains d 
            WHERE d.username = ? AND d.active = 1
              AND d.id NOT IN ($exclude_primary_domains_sql)
            ORDER BY d.domain_name ASC
        ");
        $stmt->execute([AUTH_USER]);
    }
    $available_domains = $stmt->fetchAll();
} catch (PDOException $e) {
    $available_domains = [];
}

// Get user's domains for additional domain selection (can include domains already linked to sites)
try {
    if ($is_root) {
        $stmt = $db->query("
            SELECT d.*, u.username as owner 
            FROM whp.domains d 
            JOIN whp.client_allowances u ON d.username = u.username
            WHERE d.active = 1
            ORDER BY d.domain_name ASC
        ");
    } else {
        $stmt = $db->prepare("
            SELECT d.*, d.username as owner 
            FROM whp.domains d 
            WHERE d.username = ? AND d.active = 1
            ORDER BY d.domain_name ASC
        ");
        $stmt->execute([AUTH_USER]);
    }
    $all_available_domains = $stmt->fetchAll();
} catch (PDOException $e) {
    $all_available_domains = [];
}

// Get container types.
//
// Premium-tier (cac-litespeed) rows have is_premium = 1 and are hidden
// unless the viewer is root or has client_allowances.litespeed_enabled = 1.
// is_premium is COALESCE'd to 0 so this query keeps working on servers that
// haven't applied add-litespeed-tier-flags.sql yet (column missing → NULL
// from the planner's perspective only matters at definition time; at run
// time the missing column raises a fatal — so we wrap in try/catch and
// fall back to the legacy query without the gate, which preserves the
// pre-migration behavior of showing every active row).
try {
    if ($is_root) {
        $stmt = $db->query("SELECT * FROM whp.container_types WHERE active = 1 AND name NOT LIKE 'PHP-FPM%' ORDER BY name ASC");
        $container_types = $stmt->fetchAll();
    } else {
        try {
            $stmt = $db->prepare("
                SELECT ct.* FROM whp.container_types ct
                WHERE ct.active = 1
                  AND ct.name NOT LIKE 'PHP-FPM%'
                  AND (
                    COALESCE(ct.is_premium, 0) = 0
                    OR EXISTS (
                        SELECT 1 FROM whp.client_allowances ca
                        WHERE ca.username = ?
                          AND ca.litespeed_enabled = 1
                    )
                  )
                ORDER BY ct.name ASC
            ");
            $stmt->execute([AUTH_USER]);
            $container_types = $stmt->fetchAll();
        } catch (PDOException $e) {
            // Pre-migration deployment: is_premium / litespeed_enabled
            // columns don't exist yet. Fall back to the legacy query so the
            // create-site form keeps working through the migration window.
            if (str_contains($e->getMessage(), 'Unknown column')) {
                $stmt = $db->query("SELECT * FROM whp.container_types WHERE active = 1 AND name NOT LIKE 'PHP-FPM%' ORDER BY name ASC");
                $container_types = $stmt->fetchAll();
            } else {
                throw $e;
            }
        }
    }

    // Debug: Log container types for troubleshooting
    foreach ($container_types as $type) {
        error_log("Container type {$type['name']} user_variables: " . $type['user_variables']);
    }
} catch (PDOException $e) {
    $container_types = [];
}

// Get user resource limits (uses getEffectiveLimits so uncapped users
// see host totals as their per-container ceiling)
$user_limits = null;
try {
    $effective_limits = $MySQLMgmt->getEffectiveLimits(AUTH_USER);
    $user_limits = [
        'max_cpu_allowance'    => $effective_limits['cpu_limit'],
        'max_memory_allowance' => $effective_limits['memory_limit'],
        'uncapped_resources'   => $effective_limits['uncapped'] ? 1 : 0,
    ];
} catch (Throwable $e) {
    // User limits not found
}

// Get all FQDNs (A/CNAME) for the current user
$all_fqdns = [];
$available_fqdns_for_creation = [];
$available_fqdns_for_editing = [];
if ($db && isset($PowerDNS)) {
    $username = $is_root ? 'root' : AUTH_USER;
    $all_fqdns = $PowerDNS->getUserDomainFQDNs($username, ['A','CNAME']);
    
    // Get all FQDNs currently used by existing sites (both primary and additional)
    $used_fqdns = [];
    try {
        if ($is_root) {
            $stmt = $db->query("
                SELECT DISTINCT sd.fqdn
                FROM whp.site_domains sd
                JOIN whp.sites s ON sd.site_id = s.id
            ");
        } else {
            $stmt = $db->prepare("
                SELECT DISTINCT sd.fqdn
                FROM whp.site_domains sd
                JOIN whp.sites s ON sd.site_id = s.id
                WHERE s.username = ?
            ");
            $stmt->execute([AUTH_USER]);
        }
        $used_fqdns = $stmt->fetchAll(PDO::FETCH_COLUMN);
    } catch (PDOException $e) {
        // If query fails, assume no domains are used
        $used_fqdns = [];
    }
    
    // Filter out used FQDNs for site creation
    foreach ($all_fqdns as $fqdn) {
        if (!in_array($fqdn['fqdn'], $used_fqdns)) {
            $available_fqdns_for_creation[] = $fqdn;
        }
    }
    
    // For editing, we'll need to dynamically add back domains used by the current site
    // This will be handled via JavaScript when the edit modal opens
    $available_fqdns_for_editing = $available_fqdns_for_creation;
    

}
?>

<?php if (!empty($message)): ?>
<div class="alert alert-<?php echo $message_type; ?> alert-dismissible fade show" role="alert">
    <?php echo htmlspecialchars($message); ?>
    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>

<?php // Link to the multi-site bulk-management surface (separate page so the
       // regular Sites view stays per-user-scoped and uncluttered). Shown when
       // the user actually has sites; the page itself shows an empty-state for
       // first-time users. ?>
<?php if (!empty($sites)): ?>
<div class="d-flex justify-content-end mb-3">
    <a href="index.php?page=sites-overview" class="btn btn-sm btn-outline-primary">
        <i class="fa fa-list me-1"></i><?php echo $is_root ? 'All Sites Overview' : 'Sites Overview'; ?>
        <i class="fa fa-arrow-right ms-1"></i>
    </a>
</div>
<?php endif; ?>

<style>
/* Improve site card and list-group-item visibility */
.card.site-card, .list-group-item.site-list-item {
    background-color: #fff !important;
    /* Use utility class for text color */
    color: inherit !important;
}
.card.site-card .card-header {
    background-color: #f8f9fa !important;
    color: inherit !important;
}
/* Use Bootstrap primary blue for muted info on white */
.text-site-info {
    color: #0d6efd !important;
    font-weight: 500;
}
/* Choices.js select box: ensure readable text and background */
.choices__inner, .choices__list--dropdown, .choices__item--selectable {
    background-color: #fff !important;
    color: #212529 !important;
}
.choices__list--dropdown .choices__item--selectable.is-highlighted {
    background-color: #e9ecef !important;
    color: #212529 !important;
}
.choices__list--dropdown .choices__item--selectable {
    color: #212529 !important;
}
.choices[data-type*=select-one] .choices__inner {
    border-radius: 0.375rem;
    border: 1px solid #ced4da;
}
/* Section headers for site details */
.site-section-header {
    /* Use .text-light for dark background */
    color: #f8f9fa !important;
    background: #212529 !important;
    font-weight: 700;
    font-size: 1.1rem;
    border-radius: 0.375rem;
    padding: 0.5rem 1rem;
    margin-bottom: 0.75rem;
    margin-top: 1.25rem;
    border: none;
}
.form-text {
    color: #f8f9fa !important;
}
</style>

<div class="row">
    <div class="col-md-4">
        <div class="card site-card">
            <div class="card-header">
                <h5 class="card-title mb-0 text-dark">
                    Manage Sites
                </h5>
            </div>
            <div class="card-body">
                <?php if (empty($sites)): ?>
                <div class="alert alert-info">
                    <i class="fa fa-info-circle"></i>
                    <strong>No sites found</strong> - Create your first site using the form below.
                </div>
                <?php else: ?>
                <label for="siteDropdown" class="form-label text-dark">Select a Site</label>
                <select id="siteDropdown" class="form-control">
                    <option value="">-- Choose a site --</option>
                    <?php foreach ($sites as $site): ?>
                        <option value="<?php echo $site['id']; ?>">
                            <?php echo htmlspecialchars($site['site_name']); ?> (<?php echo htmlspecialchars($site['primary_domain']); ?>)<?php echo !empty($site['is_wildcard']) ? ' [Wildcard]' : ''; ?>
                        </option>
                    <?php endforeach; ?>
                </select>
                <div id="siteDetailsContainer" class="mt-3" style="display:none;"></div>
                <div id="siteActionsContainer" class="mt-2" style="display:none;">
                    <button id="manageSiteBtn" class="btn btn-primary me-2"><i class="fa fa-cog"></i> Manage</button>
                    <button id="restartSiteBtn" class="btn btn-warning me-2"><i class="fa fa-refresh"></i> Restart</button>
                    <button id="deleteSiteBtn" class="btn btn-danger"><i class="fa fa-trash"></i> Delete</button>
                </div>
                <?php endif; ?>
            </div>
        </div>
    </div>
    
    <div class="col-md-8">
        <div class="card">
            <div class="card-header">
                <h5 class="card-title mb-0" id="createSiteFormTitle">Create New Site</h5>
            </div>
            <div class="card-body">
                <form method="post" id="createSiteForm">
                    <input type="hidden" name="action" value="create_site">
                    <input type="hidden" name="csrf_token" value="<?php echo get_csrf_token(); ?>">
                    
                    <div class="row">
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="site_name" class="form-label">Site Name</label>
                                <input type="text" class="form-control" id="site_name" name="site_name" required 
                                       placeholder="My Website">
                                <div class="form-text">A friendly name for your site</div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="primary_domain_id" class="form-label">Primary Domain or Subdomain</label>
                                <select class="form-control" id="primary_domain_id" name="primary_domain_id" required>
                                    <option value="">Select a domain or subdomain...</option>
                                    <?php foreach ($available_fqdns_for_creation as $fqdn): ?>
                                    <option value="<?php echo htmlspecialchars($fqdn['fqdn']); ?>"
                                            data-is-wildcard="<?php echo !empty($fqdn['is_wildcard']) ? '1' : '0'; ?>">
                                        <?php echo htmlspecialchars($fqdn['fqdn']); ?><?php echo !empty($fqdn['is_wildcard']) ? ' (Wildcard)' : ''; ?>
                                    </option>
                                    <?php endforeach; ?>
                                </select>
                                <div class="form-text">The main domain or subdomain for this site (A or CNAME records only)</div>
                                <div id="wildcardSslNotice" class="alert alert-info mt-2" style="display:none;">
                                    <i class="fa fa-info-circle"></i> Wildcard certificates use DNS-01 validation. A DNS challenge record will be created automatically.
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="row">
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="container_type_id" class="form-label">Container Type</label>
                                <select class="form-control" id="container_type_id" name="container_type_id" required onchange="loadContainerTypeConfig()">
                                    <option value="">Select container type...</option>
                                    <?php foreach ($container_types as $type): ?>
                                    <?php
                                    $is_eol = !empty($type['eol_date']) && strtotime($type['eol_date']) < time();
                                    $eol_label = $is_eol ? ' ⚠ EOL' : '';
                                    ?>
                                    <option value="<?php echo $type['id']; ?>"
                                            data-min-cpu="<?php echo $type['min_cpu']; ?>"
                                            data-min-memory="<?php echo $type['min_memory']; ?>"
                                            data-user-variables="<?php echo htmlspecialchars($type['user_variables']); ?>"
                                            data-eol="<?php echo $is_eol ? '1' : '0'; ?>">
                                        <?php echo htmlspecialchars($type['name']) . $eol_label; ?>
                                    </option>
                                    <?php endforeach; ?>
                                </select>
                                <div id="phpEolWarning" class="alert alert-warning mt-2 py-2" style="display:none;">
                                    <i class="fa fa-exclamation-triangle"></i>
                                    <strong>Warning:</strong> This PHP version has reached End of Life and no longer receives security updates. We recommend upgrading to PHP 8.2 or newer.
                                </div>
                                <div class="form-text">The type of container to use for this site</div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="container_count" class="form-label">Number of Containers</label>
                                <input type="number" class="form-control" id="container_count" name="container_count" 
                                       min="1" max="10" value="1" onchange="updateResourceTotals()">
                                <div class="form-text">How many containers to run for this site (1-10)</div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Additional Domains Selection (typeahead-multiselect) -->
                    <div class="row">
                        <div class="col-md-12">
                            <div class="mb-3">
                                <label class="form-label">Additional Domains/Subdomains (Optional)</label>
                                <div id="additional_domains_create" class="whp-tams-host"></div>
                                <div class="form-text">Search and check the domains/subdomains to route to this site. All selections submit together when you save.</div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="row">
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="cpu_per_container" class="form-label">CPU per Container (cores)</label>
                                <input type="number" class="form-control" id="cpu_per_container" name="cpu_per_container"
                                       step="0.25" min="0.25" max="<?php echo number_format(($user_limits['max_cpu_allowance'] ?? 8), 2, '.', ''); ?>" value="0.25" onchange="updateResourceTotals()">
                                <div class="form-text">CPU cores allocated to each container</div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="memory_per_container" class="form-label">Memory per Container (MB)</label>
                                <input type="number" class="form-control" id="memory_per_container" name="memory_per_container"
                                       min="128" step="128" max="<?php echo intval($user_limits['max_memory_allowance'] ?? 8192); ?>" value="256" onchange="updateResourceTotals()">
                                <div class="form-text">Memory allocated to each container</div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- Advanced Tuning (create form, collapsed by default).
                         No 'Suggest from usage' button — that needs a running
                         container; a not-yet-created site has no usage data. -->
                    <div class="accordion mb-3" id="createAdvancedTuningAccordion" style="display: none;">
                        <div class="accordion-item">
                            <h2 class="accordion-header">
                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#createAdvancedTuningCollapse">
                                    <i class="fa fa-sliders-h me-2"></i> Advanced Tuning
                                </button>
                            </h2>
                            <div id="createAdvancedTuningCollapse" class="accordion-collapse collapse">
                                <div class="accordion-body">
                                    <label for="create_fpm_max_children_override" class="form-label">
                                        PHP worker pool size override
                                        <span class="text-muted small">(<code>pm.max_children</code> / <code>LSAPI_CHILDREN</code>)</span>
                                    </label>
                                    <div class="input-group mb-1">
                                        <input type="number" class="form-control" id="create_fpm_max_children_override" name="fpm_max_children_override"
                                               min="2" max="50" step="1" placeholder="leave blank for image default">
                                        <span class="input-group-text" id="create_fpm_recommended_badge" style="min-width: 140px;">
                                            Recommended: <strong class="ms-1" id="create_fpm_recommended_val">—</strong>
                                        </span>
                                    </div>
                                    <div class="alert alert-warning py-2 mb-2 d-none" id="create_fpm_warn">
                                        <i class="fa fa-exclamation-triangle"></i>
                                        <span id="create_fpm_warn_text"></span>
                                    </div>
                                    <div class="form-text text-light bg-dark px-2 py-1 rounded">
                                        <strong>When to lower this:</strong> if you expect this site to OOM-kill workers
                                        under load (visible to visitors as 500 errors), traffic will be low and you want to
                                        free RAM for other sites on the host, or you're sizing for a memory-tight tier.<br>
                                        <strong>When to raise this:</strong> if you expect burst traffic and the container
                                        has memory headroom — each extra worker costs roughly the per-worker baseline below.<br>
                                        <span class="opacity-75">
                                            Default formula: <code class="text-warning">(memory_per_container − 100) ÷ <span id="create_fpm_divisor">128</span></code>
                                            (<span id="create_fpm_image_family">PHP/PHP-FPM</span> images).
                                            Leave blank to use the calculated default.
                                            Tip: come back to Edit Site after the container is running and click <em>Suggest from usage</em>
                                            for a data-driven recommendation based on actual memory consumption.
                                        </span>
                                    </div>

                                    <hr class="my-3" id="create_opcache_sep" style="display:none">
                                    <div id="create_opcache_block" style="display:none">
                                        <div class="row g-2">
                                            <div class="col-md-6">
                                                <label for="create_opcache_memory_mb_override" class="form-label">
                                                    OpCache memory (MB)
                                                </label>
                                                <input type="number" class="form-control" id="create_opcache_memory_mb_override" name="opcache_memory_mb_override"
                                                       min="32" max="512" step="1" placeholder="default 64">
                                            </div>
                                            <div class="col-md-6">
                                                <label for="create_opcache_max_files_override" class="form-label">
                                                    OpCache max files
                                                </label>
                                                <input type="number" class="form-control" id="create_opcache_max_files_override" name="opcache_max_files_override"
                                                       min="2000" max="32000" step="500" placeholder="default 8000">
                                            </div>
                                        </div>
                                        <div class="form-text text-light bg-dark px-2 py-1 rounded mt-2">
                                            <strong>When to raise:</strong> heavy plugin stacks (Divi + WooCommerce can use
                                            ~6000 files together). Symptoms of "too low" are sustained high CPU + slow
                                            page renders even on cached sites — the bytecode cache is thrashing.<br>
                                            <strong>When to lower:</strong> low-traffic / static-mostly WP. Each MB of opcache
                                            costs ~1 MB shmem per lsphp instance (and OLS spawns ~one lsphp per CPU).<br>
                                            <span class="opacity-75">cac-litespeed only. Defaults: <code class="text-warning">64 MB / 8000 files</code>.
                                                Leave blank to use the image default.</span>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <!-- Resource Totals Display -->
                    <div class="alert alert-info" id="resourceTotals" style="display: none;">
                        <strong>Total Resource Allocation:</strong>
                        <span id="totalCpu">0</span> CPU cores,
                        <span id="totalMemory">0</span> MB RAM
                        <?php if ($user_limits): ?>
                        <br><small>
                            <?php if (!empty($user_limits['uncapped_resources'])): ?>
                                <span class="text-warning">&infin; Uncapped</span> &mdash;
                                per-container ceiling is host total:
                                <?php echo number_format($user_limits['max_cpu_allowance'], 2); ?> CPU cores,
                                <?php echo number_format($user_limits['max_memory_allowance']); ?> MB RAM
                            <?php else: ?>
                                Your limits: <?php echo $user_limits['max_cpu_allowance']; ?> CPU cores,
                                <?php echo $user_limits['max_memory_allowance']; ?> MB RAM
                            <?php endif; ?>
                        </small>
                        <?php endif; ?>
                        <div id="resourceTotalsWarn" class="mt-1" style="display: none;"></div>
                    </div>

                    <!-- Container Type Configuration -->
                    <div id="containerTypeConfig" style="display: none;">
                        <h6 class="fw-bold mb-3">Container Configuration</h6>
                        <div id="userVariablesContainer">
                            <!-- User variables will be loaded here -->
                        </div>
                        <textarea class="form-control d-none" id="user_variables" name="user_variables"></textarea>
                    </div>
                    
                    <!-- SSL/HAProxy Configuration -->
                    <div class="mt-4">
                        <h6 class="fw-bold mb-3">SSL/HAProxy Configuration</h6>
                        <?php
                        // Get server IP for display
                        require_once('/docker/whp/web/libs/dns_validator.php');
                        $dns_validator = new dns_validator();
                        $server_ip = $dns_validator->getServerIPAddress();
                        ?>
                        <div class="alert alert-warning">
                            <i class="fa fa-exclamation-triangle"></i>
                            <strong>Important:</strong> Before enabling SSL, ensure your domain(s) are pointed to our server (<?php echo htmlspecialchars($server_ip); ?>). SSL certificates cannot be issued for domains that are not properly configured.
                        </div>
                        <div class="mt-3">
                            <div class="form-check">
                                <input class="form-check-input" type="checkbox" value="1" id="enable_ssl" name="enable_ssl" checked>
                                <label class="form-check-label" for="enable_ssl">
                                    <i class="fa fa-lock"></i> Enable HTTPS (issue SSL certificate on save)
                                </label>
                            </div>
                            <div class="form-text">Default. Issuance is attempted as soon as you save. If it can't complete now (DNS not pointed yet, etc.), the system will retry automatically and email you when it succeeds.</div>
                        </div>
                        <div class="mt-2 ms-4" id="dns_validation_wrapper">
                            <div class="form-check">
                                <input class="form-check-input" type="checkbox" value="1" id="dns_validation" name="dns_validation">
                                <label class="form-check-label" for="dns_validation">
                                    Use DNS validation
                                </label>
                            </div>
                            <div class="form-text" id="dns_validation_help">
                                Use this when port 80 isn't reachable from the public internet, or when you can't point DNS at our server yet but can add a TXT record. <strong>Required for wildcard certificates</strong> (auto-selected for <code>*.example.com</code>).
                            </div>
                        </div>
                    </div>
                    
                    <!-- Container Management Options -->
                    <div class="mt-4">
                        <h6 class="fw-bold mb-3">Container Management</h6>
                        <div class="row">
                            <div class="col-md-6">
                                <div class="mb-3">
                                    <div class="form-check">
                                        <input class="form-check-input" type="checkbox" value="1" id="auto_recreate_on_update" name="auto_recreate_on_update">
                                        <label class="form-check-label" for="auto_recreate_on_update">
                                            Auto-recreate containers when image updates are available
                                        </label>
                                    </div>
                                    <div class="form-text">When enabled, containers will be automatically recreated when new image versions are pulled</div>
                                </div>
                            </div>
                            <?php if ($is_root): ?>
                            <div class="col-md-6">
                                <div class="mb-3" id="shared_httpd_create_wrapper">
                                    <div class="form-check">
                                        <input class="form-check-input" type="checkbox" value="shared_httpd" id="container_mode" name="container_mode" <?php echo ($shared_httpd_default_mode === 'shared_httpd') ? 'checked' : ''; ?>>
                                        <label class="form-check-label" for="container_mode">
                                            Use shared Apache (PHP-FPM mode)
                                        </label>
                                    </div>
                                    <div class="form-text">Routes through shared Apache httpd instead of per-container Apache. Reduces memory overhead. Only applies to PHP container types.</div>
                                </div>
                            </div>
                            <?php endif; ?>
                        </div>
                    </div>

                    <div class="d-grid gap-2 mt-4">
                        <button type="submit" class="btn btn-primary">
                            <i class="fa fa-plus"></i> Create Site
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<!-- Site Details Modal -->
<div class="modal fade" id="siteDetailsModal" tabindex="-1" aria-labelledby="siteDetailsModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="siteDetailsModalLabel">Site Details</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body" id="siteDetailsContent">
                <!-- Content will be loaded here -->
            </div>
        </div>
    </div>
</div>

<!-- Delete Site Modal -->
<div class="modal fade" id="deleteSiteModal" tabindex="-1" aria-labelledby="deleteSiteModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="deleteSiteModalLabel">Delete Site</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <p>Are you sure you want to delete the site "<span id="deleteSiteName"></span>"?</p>
                <p class="text-danger"><strong>Warning:</strong> This action cannot be undone. All containers and configurations will be permanently removed.</p>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <form method="post" style="display: inline;">
                    <input type="hidden" name="action" value="delete_site">
                    <input type="hidden" name="csrf_token" value="<?php echo get_csrf_token(); ?>">
                    <input type="hidden" name="site_id" id="deleteSiteId">
                    <button type="submit" class="btn btn-danger">Delete Site</button>
                </form>
            </div>
        </div>
    </div>
</div>

<!-- SSL Action Result Modal — shared by per-domain Retry/Issue and edit-modal renew. -->
<div class="modal fade" id="sslActionResultModal" tabindex="-1" aria-labelledby="sslActionResultLabel" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="sslActionResultLabel">SSL Action Result</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <div id="sslActionAlert" class="alert mb-3" role="alert"></div>
                <div id="sslActionDetails" class="small"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-outline-primary" id="sslActionRefreshBtn">
                    <i class="fa fa-refresh"></i> Refresh details
                </button>
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>

<!-- Edit Site Modal -->
<div class="modal fade" id="editSiteModal" tabindex="-1" aria-labelledby="editSiteModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="editSiteModalLabel">Edit Site</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <form id="editSiteForm">
                    <div class="row">
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="edit_site_name" class="form-label">Site Name</label>
                                <input type="text" class="form-control" id="edit_site_name" name="site_name" required>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="edit_primary_domain" class="form-label">Primary Domain</label>
                                <input type="text" class="form-control" id="edit_primary_domain" readonly>
                                <div class="form-text">The main domain for this site (cannot be changed)</div>
                            </div>
                        </div>
                    </div>
                    <!-- Additional Domains Selection (typeahead-multiselect) -->
                    <div class="row">
                        <div class="col-md-12">
                            <div class="mb-3">
                                <label class="form-label">Additional Domains/Subdomains (Optional)</label>
                                <div id="additional_domains_edit" class="whp-tams-host"></div>
                                <div class="form-text">Search and check the domains/subdomains to route to this site. All selections submit together when you save.</div>
                            </div>
                        </div>
                    </div>
                    
                    <div class="row">
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="edit_container_type_id" class="form-label">Container Type</label>
                                <select class="form-control" id="edit_container_type_id" name="container_type_id" required onchange="var w=document.getElementById('editPhpEolWarning');if(w)w.style.display=this.options[this.selectedIndex].dataset.eol==='1'?'':'none';">
                                    <?php foreach ($container_types as $type):
                                    $is_eol = !empty($type['eol_date']) && strtotime($type['eol_date']) < time();
                                    $eol_label = $is_eol ? ' ⚠ EOL' : '';
                                    ?>
                                    <option value="<?php echo $type['id']; ?>"
                                            data-min-cpu="<?php echo $type['min_cpu']; ?>"
                                            data-min-memory="<?php echo $type['min_memory']; ?>"
                                            data-eol="<?php echo $is_eol ? '1' : '0'; ?>">
                                        <?php echo htmlspecialchars($type['name']) . $eol_label; ?>
                                    </option>
                                    <?php endforeach; ?>
                                </select>
                                <div id="editPhpEolWarning" class="alert alert-warning mt-2 py-2" style="display:none;">
                                    <i class="fa fa-exclamation-triangle"></i>
                                    <strong>Warning:</strong> This PHP version has reached End of Life and no longer receives security updates. We recommend upgrading to PHP 8.2 or newer.
                                </div>
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="edit_container_count" class="form-label">Number of Containers</label>
                                <input type="number" class="form-control" id="edit_container_count" name="container_count" 
                                       min="1" max="10" value="1" onchange="updateEditResourceTotals()">
                            </div>
                        </div>
                    </div>
                    
                    <div class="row">
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="edit_cpu_per_container" class="form-label">CPU per Container (cores)</label>
                                <input type="number" class="form-control" id="edit_cpu_per_container" name="cpu_per_container"
                                       step="0.25" min="0.25" max="<?php echo number_format(($user_limits['max_cpu_allowance'] ?? 8), 2, '.', ''); ?>" value="0.25" onchange="updateEditResourceTotals()">
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="mb-3">
                                <label for="edit_memory_per_container" class="form-label">Memory per Container (MB)</label>
                                <input type="number" class="form-control" id="edit_memory_per_container" name="memory_per_container"
                                       min="128" step="128" max="<?php echo intval($user_limits['max_memory_allowance'] ?? 8192); ?>" value="256" onchange="updateEditResourceTotals()">
                            </div>
                        </div>
                    </div>

                    <!-- Advanced Tuning (collapsed by default) -->
                    <div class="accordion mb-3" id="editAdvancedTuningAccordion" style="display: none;">
                        <div class="accordion-item">
                            <h2 class="accordion-header">
                                <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#editAdvancedTuningCollapse">
                                    <i class="fa fa-sliders-h me-2"></i> Advanced Tuning
                                </button>
                            </h2>
                            <div id="editAdvancedTuningCollapse" class="accordion-collapse collapse">
                                <div class="accordion-body">
                                    <label for="edit_fpm_max_children_override" class="form-label">
                                        PHP worker pool size override
                                        <span class="text-muted small">(<code>pm.max_children</code> / <code>LSAPI_CHILDREN</code>)</span>
                                    </label>
                                    <div class="input-group mb-1">
                                        <input type="number" class="form-control" id="edit_fpm_max_children_override" name="fpm_max_children_override"
                                               min="2" max="50" step="1" placeholder="leave blank for image default">
                                        <span class="input-group-text" id="edit_fpm_recommended_badge" style="min-width: 140px;">
                                            Recommended: <strong class="ms-1" id="edit_fpm_recommended_val">—</strong>
                                        </span>
                                        <button type="button" class="btn btn-outline-primary" id="edit_fpm_suggest_btn" title="Read the running container's actual memory usage and suggest a value">
                                            <i class="fa fa-magic"></i> Suggest from usage
                                        </button>
                                    </div>
                                    <div class="alert alert-warning py-2 mb-2 d-none" id="edit_fpm_warn">
                                        <i class="fa fa-exclamation-triangle"></i>
                                        <span id="edit_fpm_warn_text"></span>
                                    </div>
                                    <div class="alert alert-info py-2 mb-2 d-none" id="edit_fpm_suggest_result"></div>
                                    <div class="form-text text-light bg-dark px-2 py-1 rounded">
                                        <strong>When to lower this:</strong> the container is OOM-killing workers (visible to visitors as 500 errors),
                                        memory pressure is chronic, or traffic is low and you want to free RAM for other sites on the host.<br>
                                        <strong>When to raise this:</strong> requests are queueing during traffic bursts (slow response times)
                                        and the container has memory headroom — each extra worker costs roughly the per-worker baseline below.<br>
                                        <span class="opacity-75">
                                            Default formula: <code class="text-warning">(memory_per_container − 100) ÷ <span id="edit_fpm_divisor">128</span></code>
                                            (<span id="edit_fpm_image_family">PHP/PHP-FPM</span> images).
                                            Leave blank to use the calculated default. <strong>Changing this value recreates the container.</strong>
                                        </span>
                                    </div>

                                    <hr class="my-3" id="edit_opcache_sep" style="display:none">
                                    <div id="edit_opcache_block" style="display:none">
                                        <div class="row g-2">
                                            <div class="col-md-6">
                                                <label for="edit_opcache_memory_mb_override" class="form-label">
                                                    OpCache memory (MB)
                                                </label>
                                                <input type="number" class="form-control" id="edit_opcache_memory_mb_override" name="opcache_memory_mb_override"
                                                       min="32" max="512" step="1" placeholder="default 64">
                                            </div>
                                            <div class="col-md-6">
                                                <label for="edit_opcache_max_files_override" class="form-label">
                                                    OpCache max files
                                                </label>
                                                <input type="number" class="form-control" id="edit_opcache_max_files_override" name="opcache_max_files_override"
                                                       min="2000" max="32000" step="500" placeholder="default 8000">
                                            </div>
                                        </div>
                                        <div class="form-text text-light bg-dark px-2 py-1 rounded mt-2">
                                            <strong>When to raise:</strong> heavy plugin stacks (Divi + WooCommerce can use
                                            ~6000 files together). Symptoms of "too low" are sustained high CPU + slow
                                            page renders even on cached sites — the bytecode cache is thrashing.<br>
                                            <strong>When to lower:</strong> low-traffic / static-mostly WP. Each MB of opcache
                                            costs ~1 MB shmem per lsphp instance (and OLS spawns ~one lsphp per CPU).<br>
                                            <span class="opacity-75">cac-litespeed only. Defaults: <code class="text-warning">64 MB / 8000 files</code>.
                                                Leave blank to use the image default. <strong>Changing these recreates the container.</strong></span>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <!-- Resource Totals Display -->
                    <div class="alert alert-info" id="editResourceTotals" style="display:none;">
                        <strong>Total Resources:</strong> <span id="editTotalCpu">0</span> CPU cores, <span id="editTotalMemory">0</span> MB memory
                    </div>
                    
                    <div id="editUserVariablesContainer" style="display:none;"></div>
                    
                    <!-- Container Management Options -->
                    <div class="mt-3">
                        <h6>Container Management</h6>
                        <div class="row">
                            <div class="col-md-6">
                                <div class="mb-3">
                                    <div class="form-check">
                                        <input class="form-check-input" type="checkbox" value="1" id="edit_auto_recreate_on_update" name="auto_recreate_on_update">
                                        <label class="form-check-label" for="edit_auto_recreate_on_update">
                                            Auto-recreate containers when image updates are available
                                        </label>
                                    </div>
                                    <div class="form-text">When enabled, containers will be automatically recreated when new image versions are pulled</div>
                                </div>
                            </div>
                            <div class="col-md-6">
                                <div class="mb-3">
                                    <button type="button" class="btn btn-info" id="recreateContainersBtn">
                                        <i class="fa fa-refresh"></i> Recreate All Containers
                                    </button>
                                    <div class="form-text">Manually recreate all containers with latest images</div>
                                </div>
                            </div>
                        </div>
                        <?php if ($is_root): ?>
                        <div class="row">
                            <div class="col-md-6">
                                <div class="mb-3" id="shared_httpd_edit_wrapper">
                                    <div class="form-check">
                                        <input class="form-check-input" type="checkbox" value="shared_httpd" id="edit_container_mode" name="container_mode">
                                        <label class="form-check-label" for="edit_container_mode">
                                            Use shared Apache (PHP-FPM mode)
                                        </label>
                                    </div>
                                    <div class="form-text">Routes through shared Apache httpd instead of per-container Apache. Reduces memory overhead. Only applies to PHP container types.</div>
                                </div>
                            </div>
                        </div>
                        <?php endif; ?>
                    </div>
                    
                    <!-- SSL Configuration -->
                    <div class="mt-3 mb-3">
                        <h6>SSL Configuration</h6>
                        <div class="form-check mb-2">
                            <input class="form-check-input" type="checkbox" value="1" id="edit_enable_ssl" name="enable_ssl">
                            <label class="form-check-label" for="edit_enable_ssl">
                                <i class="fa fa-lock"></i> Enable HTTPS
                            </label>
                            <div class="form-text">
                                Saving issues/renews the certificate via the orchestrator. The current state for
                                this site's domains is shown in the site detail panel; per-domain "Retry SSL" /
                                "Issue SSL" buttons are also available there.
                            </div>
                        </div>
                        <div class="ms-4 mb-3" id="edit_dns_validation_wrapper">
                            <div class="form-check">
                                <input class="form-check-input" type="checkbox" value="1" id="edit_dns_validation" name="dns_validation">
                                <label class="form-check-label" for="edit_dns_validation">
                                    Use DNS validation
                                </label>
                            </div>
                            <div class="form-text" id="edit_dns_validation_help">
                                Use this when port 80 isn't reachable from the public internet, or when DNS isn't
                                pointed at our server yet but you can add a TXT record.
                                <strong>Required for wildcard certificates</strong> (auto-selected for <code>*.example.com</code>).
                            </div>
                        </div>
                        <div class="d-flex gap-3 mb-3">
                            <button type="button" class="btn btn-info btn-sm" onclick="renewSSL()">
                                <i class="fa fa-refresh"></i> Force renew now
                            </button>
                        </div>
                        <div class="alert alert-info small mb-0">
                            <i class="fa fa-info-circle"></i>
                            Per-site cert bundling is on — one Let's Encrypt order per site covers all the site's
                            domains as SANs. Domains pointing elsewhere are skipped automatically.
                        </div>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <button type="button" class="btn btn-primary" id="saveSiteBtn">
                    <i class="fa fa-save"></i> Save Changes
                </button>
            </div>
        </div>
    </div>
</div>

<?php // All Sites Overview moved to pages/sites-overview.php in 2026.05.199.
       // The per-user "Manage Sites" picker + "Create New Site" form above is
       // the canonical per-user surface on this page again. The button at the
       // top of the page links to the overview for users who want the bulk view. ?>

<!-- Task 19: Disable Site Modal -->
<div class="modal fade" id="disableSiteModal" tabindex="-1" aria-labelledby="disableSiteModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="disableSiteModalLabel"><i class="fa fa-ban me-2"></i>Disable Site</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <p>Disable site: <strong id="disableSiteName"></strong></p>
                <input type="hidden" id="disableSiteId" value="">
                <div class="mb-3">
                    <label for="disableSiteReason" class="form-label">Reason <span class="text-danger">*</span></label>
                    <select class="form-select" id="disableSiteReason" required>
                        <option value="">-- Select reason --</option>
                        <option value="unpaid">Unpaid invoice</option>
                        <option value="tos_violation">ToS violation</option>
                        <option value="maintenance">Maintenance</option>
                        <option value="admin_other">Other (admin)</option>
                    </select>
                </div>
                <div class="mb-3">
                    <label for="disableSiteNote" class="form-label">Note (optional)</label>
                    <textarea class="form-control" id="disableSiteNote" rows="2" placeholder="Internal note&hellip;"></textarea>
                </div>
                <div id="disableSiteResult" class="mt-2" style="display:none;"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <button type="button" class="btn btn-danger" id="disableSiteConfirmBtn">
                    <i class="fa fa-ban me-1"></i>Disable Site
                </button>
            </div>
        </div>
    </div>
</div>

<!-- Task 19: Enable Site Modal -->
<div class="modal fade" id="enableSiteModal" tabindex="-1" aria-labelledby="enableSiteModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="enableSiteModalLabel"><i class="fa fa-check-circle me-2"></i>Enable Site</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <p>Re-enable site: <strong id="enableSiteName"></strong></p>
                <p class="text-muted small">This will start the container and restore normal HAProxy routing.</p>
                <input type="hidden" id="enableSiteId" value="">
                <div id="enableSiteResult" class="mt-2" style="display:none;"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <button type="button" class="btn btn-success" id="enableSiteConfirmBtn">
                    <i class="fa fa-check-circle me-1"></i>Enable Site
                </button>
            </div>
        </div>
    </div>
</div>

<!-- Correct Choices.js/CSS URLs for document root -->
<link rel="stylesheet" href="/css/choices.min.css">
<script src="/js/choices.min.js"></script>

<script>
// Define CSRF token for AJAX requests
const nonce = '<?php echo get_csrf_token(); ?>';

let currentEditSiteId = null;
let currentEditSiteData = null;

// Show/hide wildcard SSL notice when primary domain changes,
// and force-toggle the DNS validation checkbox for wildcards.
document.addEventListener('DOMContentLoaded', function() {
    const primaryDomainSelect = document.getElementById('primary_domain_id');
    const wildcardNotice = document.getElementById('wildcardSslNotice');
    const enableSslCb = document.getElementById('enable_ssl');
    const dnsValidationCb = document.getElementById('dns_validation');
    const dnsValidationHelp = document.getElementById('dns_validation_help');

    function applyWildcardRule() {
        if (!primaryDomainSelect) return;
        const selected = primaryDomainSelect.options[primaryDomainSelect.selectedIndex];
        const isWildcard = !!(selected && selected.getAttribute('data-is-wildcard') === '1');

        if (wildcardNotice) {
            wildcardNotice.style.display = isWildcard ? 'block' : 'none';
        }

        if (dnsValidationCb) {
            if (isWildcard && enableSslCb && enableSslCb.checked) {
                // Wildcards require DNS-01. Force on + lock.
                dnsValidationCb.checked = true;
                dnsValidationCb.disabled = true;
                if (dnsValidationHelp) {
                    dnsValidationHelp.classList.add('text-warning');
                }
            } else {
                // Restore user control.
                dnsValidationCb.disabled = !enableSslCb || !enableSslCb.checked;
                if (dnsValidationHelp) {
                    dnsValidationHelp.classList.remove('text-warning');
                }
            }
        }
    }

    if (primaryDomainSelect) {
        primaryDomainSelect.addEventListener('change', applyWildcardRule);
    }
    if (enableSslCb) {
        enableSslCb.addEventListener('change', applyWildcardRule);
    }

    // Apply once on load to honor the default-checked state of "Enable HTTPS"
    // and any pre-selected primary domain.
    applyWildcardRule();
});

function loadContainerTypeConfig() {
    const select = document.getElementById('container_type_id');
    const configDiv = document.getElementById('containerTypeConfig');
    const userVariablesContainer = document.getElementById('userVariablesContainer');
    const userVariablesInput = document.getElementById('user_variables');

    // Show/hide shared httpd checkbox based on PHP container type
    const sharedHttpdWrapper = document.getElementById('shared_httpd_create_wrapper');
    if (sharedHttpdWrapper) {
        const selectedText = select.options[select.selectedIndex]?.text || '';
        sharedHttpdWrapper.style.display = selectedText.match(/^PHP\s/) ? '' : 'none';
        // Uncheck if hidden (non-PHP type selected)
        if (!selectedText.match(/^PHP\s/)) {
            const cb = document.getElementById('container_mode');
            if (cb) cb.checked = false;
        }
    }

    // Show/hide EOL warning based on selected container type
    const eolWarning = document.getElementById('phpEolWarning');
    if (eolWarning) {
        const selectedOption = select.options[select.selectedIndex];
        eolWarning.style.display = (selectedOption && selectedOption.dataset.eol === '1') ? '' : 'none';
    }
    
    if (select.value) {
        const option = select.options[select.selectedIndex];
        const userVariables = option.getAttribute('data-user-variables');
        
        if (userVariables && userVariables !== '{}') {
            try {
                const variables = JSON.parse(userVariables);
                console.log('Container type user variables:', variables);
                let html = '';
                
                for (const [varName, config] of Object.entries(variables)) {
                    html += '<div class="mb-3">';
                    html += '<label for="var_' + varName + '" class="form-label">' + config.label + '</label>';
                    
                    if (config.type === 'select') {
                        html += '<select class="form-control" id="var_' + varName + '" name="var_' + varName + '"';
                        if (config.required) html += ' required';
                        html += '>';
                        for (const option of config.options) {
                            const selected = option === config.default ? ' selected' : '';
                            html += '<option value="' + option + '"' + selected + '>' + option + '</option>';
                        }
                        html += '</select>';
                    } else if (config.type === 'number') {
                        html += '<input type="number" class="form-control" id="var_' + varName + '" name="var_' + varName + '"';
                        if (config.default) html += ' value="' + config.default + '"';
                        if (config.min) html += ' min="' + config.min + '"';
                        if (config.max) html += ' max="' + config.max + '"';
                        if (config.required) html += ' required';
                        html += '>';
                    } else if (config.type === 'checkbox') {
                        html += '<div class="form-check">';
                        html += '<input type="checkbox" class="form-check-input" id="var_' + varName + '" name="var_' + varName + '"';
                        if (config.default) html += ' checked';
                        html += '>';
                        html += '<label class="form-check-label" for="var_' + varName + '">' + config.label + '</label>';
                        html += '</div>';
                    } else {
                        html += '<input type="text" class="form-control" id="var_' + varName + '" name="var_' + varName + '"';
                        if (config.default) html += ' value="' + config.default + '"';
                        if (config.required) html += ' required';
                        html += '>';
                    }
                    
                    if (config.description) {
                        html += '<div class="form-text">' + config.description + '</div>';
                    }
                    
                    html += '</div>';
                }
                
                userVariablesContainer.innerHTML = html;
                configDiv.style.display = 'block';
                
                // Update user variables JSON when form is submitted
                // Store the variables in a global scope so they can be accessed during form submission
                window.currentUserVariables = variables;
                
                // Set up a function to collect user variables
                window.collectUserVariables = function() {
                    const collectedVariables = {};
                    
                    console.log('Processing user variables:', window.currentUserVariables);
                    
                    for (const [varName, config] of Object.entries(window.currentUserVariables)) {
                        const input = document.getElementById('var_' + varName);
                        if (input) {
                            if (config.type === 'checkbox') {
                                collectedVariables[varName] = input.checked;
                            } else {
                                collectedVariables[varName] = input.value;
                            }
                        }
                    }
                    
                    console.log('Collected user variables:', collectedVariables);
                    return collectedVariables;
                };
                
            } catch (e) {
                console.error('Error parsing user variables:', e);
                configDiv.style.display = 'none';
            }
        } else {
            configDiv.style.display = 'none';
        }
        
        updateResourceTotals();
    } else {
        configDiv.style.display = 'none';
    }
}

function updateResourceTotals() {
    // Calculate total resources for all containers (not just per-container)
    const containerCount = parseInt(document.getElementById('container_count').value) || 0;
    const cpuPerContainer = parseFloat(document.getElementById('cpu_per_container').value) || 0;
    const memoryPerContainer = parseInt(document.getElementById('memory_per_container').value) || 0;

    const totalCpu = containerCount * cpuPerContainer;
    const totalMemory = containerCount * memoryPerContainer;

    document.getElementById('totalCpu').textContent = totalCpu.toFixed(2);
    document.getElementById('totalMemory').textContent = totalMemory;

    const totalsDiv = document.getElementById('resourceTotals');
    if (containerCount > 0) {
        totalsDiv.style.display = 'block';
        // Check against user limits
        <?php if ($user_limits): ?>
        const userCpuLimit = <?php echo $user_limits['max_cpu_allowance']; ?>;
        const userMemoryLimit = <?php echo $user_limits['max_memory_allowance']; ?>;
        const userIsUncapped = <?php echo !empty($user_limits['uncapped_resources']) ? 'true' : 'false'; ?>;
        const warnDiv = document.getElementById('resourceTotalsWarn');
        // Enforce total resource limits
        if (totalCpu > userCpuLimit || totalMemory > userMemoryLimit) {
            totalsDiv.className = 'alert alert-warning';
            if (warnDiv) {
                warnDiv.style.display = '';
                while (warnDiv.firstChild) { warnDiv.removeChild(warnDiv.firstChild); }
                const strongEl = document.createElement('strong');
                strongEl.textContent = 'Warning:';
                warnDiv.appendChild(strongEl);
                const msgText = userIsUncapped
                    ? ' exceeds host capacity (' + userCpuLimit + ' cores / ' + userMemoryLimit + ' MB). Allocation may fail at container start.'
                    : ' exceeds your account limits (' + userCpuLimit + ' cores / ' + userMemoryLimit + ' MB).';
                warnDiv.appendChild(document.createTextNode(msgText));
            }
        } else {
            totalsDiv.className = 'alert alert-info';
            if (warnDiv) {
                warnDiv.style.display = 'none';
                while (warnDiv.firstChild) { warnDiv.removeChild(warnDiv.firstChild); }
            }
        }
        <?php endif; ?>
    } else {
        totalsDiv.style.display = 'none';
    }
}

function viewSiteDetails(siteId) {
    // Load site details via AJAX and show modal
    fetch('api/sites.php?action=get_site_details&site_id=' + siteId)
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                document.getElementById('siteDetailsContent').innerHTML = data.html;
                new bootstrap.Modal(document.getElementById('siteDetailsModal')).show();
            } else {
                alert('Error loading site details: ' + data.message);
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert('Error loading site details');
        });
}

function restartSite(siteId, siteName) {
    if (!confirm('Are you sure you want to restart all containers for site "' + siteName + '"?')) {
        return;
    }

    const btn = document.getElementById('restartSiteBtn');
    const originalHtml = btn.innerHTML;
    btn.disabled = true;
    btn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Restarting...';

    fetch('api/sites.php?action=restart_site', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': nonce
        },
        body: JSON.stringify({
            site_id: siteId
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.status === 'success') {
            alert('Site containers restarted successfully.');
        } else {
            alert('Error restarting site: ' + data.message);
        }
    })
    .catch(error => {
        console.error('Error:', error);
        alert('Error restarting site');
    })
    .finally(() => {
        btn.disabled = false;
        btn.innerHTML = originalHtml;
    });
}

function deleteSite(siteId, siteName) {
    document.getElementById('deleteSiteId').value = siteId;
    document.getElementById('deleteSiteName').textContent = siteName;
    new bootstrap.Modal(document.getElementById('deleteSiteModal')).show();
}

// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
    const createSiteForm = document.getElementById('createSiteForm');
    const originalFormHtml = createSiteForm.innerHTML;
    // Initialize Choices.js for the site dropdown
    const siteDropdown = document.getElementById('siteDropdown');
    if (siteDropdown) {
        const choices = new Choices(siteDropdown, {
            searchEnabled: true,
            itemSelectText: '',
            shouldSort: false
        });
        siteDropdown.addEventListener('change', function() {
            const siteId = this.value;
            if (siteId) {
                fetch('api/sites.php?action=get_site_details&site_id=' + siteId)
                    .then(response => response.json())
                    .then(data => {
                        if (data.status === 'success') {
                            document.getElementById('siteDetailsContainer').innerHTML = data.html;
                            document.getElementById('siteDetailsContainer').style.display = '';
                            document.getElementById('siteActionsContainer').style.display = '';
                            // Set up Manage button
                            document.getElementById('manageSiteBtn').onclick = function() {
                                const site = data.site;
                                showEditSiteModal(site);
                            };
                            // Set up Restart button
                            document.getElementById('restartSiteBtn').onclick = function() {
                                restartSite(siteId, data.site.site_name || '');
                            };
                            // Set up Delete button
                            document.getElementById('deleteSiteBtn').onclick = function() {
                                deleteSite(siteId, data.site.site_name || '');
                            };
                            // Wire per-domain "Retry SSL" / "Issue SSL" buttons. POST to
                            // ssl_retry_domain and surface the outcome in a modal — no page
                            // or panel reload, so scroll position / context is preserved.
                            // The modal has a "Refresh details" button so the user can opt
                            // in to re-fetching the panel after a successful action.
                            document.querySelectorAll('#siteDetailsContainer .ssl-retry-btn').forEach(btn => {
                                btn.addEventListener('click', function() {
                                    const dom = this.getAttribute('data-domain');
                                    if (!dom) return;
                                    const origText = this.textContent;
                                    this.disabled = true;
                                    this.textContent = 'Working…';
                                    fetch('api/sites.php', {
                                        method: 'POST',
                                        headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': nonce },
                                        body: JSON.stringify({ action: 'ssl_retry_domain', domain: dom, nonce: nonce })
                                    })
                                    .then(r => r.json().then(j => ({ ok: r.ok, body: j })))
                                    .then(({ ok, body }) => {
                                        showSslActionResult(ok && body.status === 'success', dom, body);
                                    })
                                    .catch(err => {
                                        showSslActionResult(false, dom, { message: err.message || 'Request failed' });
                                    })
                                    .finally(() => {
                                        this.disabled = false;
                                        this.textContent = origText;
                                    });
                                });
                            });
                        } else {
                            document.getElementById('siteDetailsContainer').innerHTML = '<div class="alert alert-danger">Error loading site details: ' + data.message + '</div>';
                            document.getElementById('siteDetailsContainer').style.display = '';
                            document.getElementById('siteActionsContainer').style.display = 'none';
                        }
                    })
                    .catch(error => {
                        document.getElementById('siteDetailsContainer').innerHTML = '<div class="alert alert-danger">Error loading site details</div>';
                        document.getElementById('siteDetailsContainer').style.display = '';
                        document.getElementById('siteActionsContainer').style.display = 'none';
                    });
            } else {
                document.getElementById('siteDetailsContainer').style.display = 'none';
                document.getElementById('siteDetailsContainer').innerHTML = '';
                document.getElementById('siteActionsContainer').style.display = 'none';
            }
        });
    }
    // Edit Site Modal Functions
    
    // Build the item pool for the edit-modal typeahead. Includes all
    // currently-available FQDNs for this user plus the additional FQDNs
    // already attached to this site (so the operator can untick them too).
    // Primary FQDN is added as a disabled row.
    const editAvailableFqdns = <?php echo json_encode(
        array_values(array_map(function ($row) { return $row['fqdn']; }, $available_fqdns_for_editing))
    ); ?>;

    function buildEditDomainItems(site) {
        const seen = new Set();
        const items = [];
        const primary = site && site.primary_domain ? site.primary_domain : '';
        if (primary) {
            items.push({
                value: primary,
                label: primary,
                disabledReason: 'primary domain',
            });
            seen.add(primary);
        }
        if (site && Array.isArray(site.fqdns)) {
            site.fqdns.forEach(function (obj) {
                if (!obj || !obj.fqdn) return;
                if (seen.has(obj.fqdn)) return;
                items.push({ value: obj.fqdn, label: obj.fqdn });
                seen.add(obj.fqdn);
            });
        }
        editAvailableFqdns.forEach(function (fqdn) {
            if (seen.has(fqdn)) return;
            items.push({ value: fqdn, label: fqdn });
            seen.add(fqdn);
        });
        return items;
    }

    function ensureEditTypeahead() {
        if (window.whpAdditionalDomainsEdit) return window.whpAdditionalDomainsEdit;
        const host = document.getElementById('additional_domains_edit');
        if (!host || !window.TypeaheadMultiselect) return null;
        window.whpAdditionalDomainsEdit = new TypeaheadMultiselect(host, {
            items: [],
            placeholder: 'Search additional domains/subdomains...',
            emptyMessage: 'No matching domains. Add A or CNAME records in DNS to expand this list.',
            noItemsMessage: 'No additional domains available. Add A or CNAME records in DNS to populate this list.',
        });
        return window.whpAdditionalDomainsEdit;
    }


    function showEditSiteModal(site) {
        currentEditSiteId = site.id;
        currentEditSiteData = site;
        
        // Populate modal form
        document.getElementById('edit_site_name').value = site.site_name;
        document.getElementById('edit_primary_domain').value = site.primary_domain;
        document.getElementById('edit_container_type_id').value = site.container_type_id;
        // Show/hide EOL warning for edit form
        const editSelect = document.getElementById('edit_container_type_id');
        const editEolWarning = document.getElementById('editPhpEolWarning');
        if (editEolWarning && editSelect) {
            const selectedOpt = editSelect.options[editSelect.selectedIndex];
            editEolWarning.style.display = (selectedOpt && selectedOpt.dataset.eol === '1') ? '' : 'none';
        }
        document.getElementById('edit_container_count').value = site.container_count;
        document.getElementById('edit_cpu_per_container').value = site.cpu_per_container;
        document.getElementById('edit_memory_per_container').value = site.memory_per_container;
        document.getElementById('edit_auto_recreate_on_update').checked = site.auto_recreate_on_update == 1;

        // Prefill the FPM/LSAPI override field + show the whole Advanced Tuning
        // accordion for any image that has a PHP worker pool. Gate on base_image
        // not on container_type name — "LiteSpeed PHP 8.X" name doesn't match
        // /^PHP[\s-]/ but the cac-litespeed image absolutely uses LSAPI_CHILDREN.
        const fpmOverrideInput  = document.getElementById('edit_fpm_max_children_override');
        const advancedAccordion = document.getElementById('editAdvancedTuningAccordion');
        if (fpmOverrideInput) {
            fpmOverrideInput.value = (site.fpm_max_children_override != null && site.fpm_max_children_override !== '')
                ? site.fpm_max_children_override
                : '';
        }
        if (advancedAccordion) {
            const baseImage = site.base_image || '';
            const hasPhpPool = /(cac|cac-fpm|cac-litespeed):php\d+/.test(baseImage);
            advancedAccordion.style.display = hasPhpPool ? '' : 'none';
            // Update the help text to reflect which divisor applies to this image.
            const isLitespeed = /cac-litespeed:php\d+/.test(baseImage);
            const divEl = document.getElementById('edit_fpm_divisor');
            const famEl = document.getElementById('edit_fpm_image_family');
            if (divEl) divEl.textContent = isLitespeed ? '130' : '128';
            if (famEl) famEl.textContent = isLitespeed ? 'cac-litespeed (OLS + lsphp)' : 'cac / cac-fpm (Apache + PHP-FPM)';
            // OpCache block: cac-litespeed only (entrypoint hook lives there).
            const opcacheSep   = document.getElementById('edit_opcache_sep');
            const opcacheBlock = document.getElementById('edit_opcache_block');
            const opcacheMem   = document.getElementById('edit_opcache_memory_mb_override');
            const opcacheFiles = document.getElementById('edit_opcache_max_files_override');
            if (opcacheSep)   opcacheSep.style.display   = isLitespeed ? '' : 'none';
            if (opcacheBlock) opcacheBlock.style.display = isLitespeed ? '' : 'none';
            if (opcacheMem) {
                opcacheMem.value = (site.opcache_memory_mb_override != null && site.opcache_memory_mb_override !== '')
                    ? site.opcache_memory_mb_override : '';
            }
            if (opcacheFiles) {
                opcacheFiles.value = (site.opcache_max_files_override != null && site.opcache_max_files_override !== '')
                    ? site.opcache_max_files_override : '';
            }
        }
        // Compute initial recommendation + wire warning behavior.
        whpUpdateFpmRecommendation('edit', site.base_image || '', parseInt(site.memory_per_container, 10) || 256);

        // Populate container_mode checkbox (root-only)
        const editContainerModeCheckbox = document.getElementById('edit_container_mode');
        if (editContainerModeCheckbox) {
            editContainerModeCheckbox.checked = (site.container_mode === 'shared_httpd');
            // Show/hide based on whether this is a PHP container type
            const wrapper = document.getElementById('shared_httpd_edit_wrapper');
            if (wrapper) {
                const typeName = site.container_type_name || '';
                wrapper.style.display = typeName.match(/^PHP[\s-]/) ? '' : 'none';
            }
        }

        // SSL state — derive from site.ssl_enabled (the panel sites table flag).
        // Wildcards force DNS-01 + lock the checkbox, mirroring the create form.
        const editEnableSsl = document.getElementById('edit_enable_ssl');
        const editDnsValidation = document.getElementById('edit_dns_validation');
        const editDnsHelp = document.getElementById('edit_dns_validation_help');
        const isWildcardSite = String(site.primary_domain || '').startsWith('*.');

        if (editEnableSsl) {
            // ssl_enabled tinyint: 0=No, 1=Let's Encrypt, 2=Custom
            editEnableSsl.checked = parseInt(site.ssl_enabled || 0, 10) > 0;
        }
        if (editDnsValidation) {
            editDnsValidation.checked = isWildcardSite;
        }

        function applyEditWildcardRule() {
            if (!editDnsValidation || !editEnableSsl) return;
            if (isWildcardSite && editEnableSsl.checked) {
                editDnsValidation.checked = true;
                editDnsValidation.disabled = true;
                if (editDnsHelp) editDnsHelp.classList.add('text-warning');
            } else {
                editDnsValidation.disabled = !editEnableSsl.checked;
                if (editDnsHelp) editDnsHelp.classList.remove('text-warning');
            }
        }
        if (editEnableSsl) editEnableSsl.addEventListener('change', applyEditWildcardRule);
        applyEditWildcardRule();

        // Update resource totals
        updateEditResourceTotals();
        
        // Set up recreate containers button
        document.getElementById('recreateContainersBtn').onclick = function() {
            if (!confirm('Are you sure you want to recreate all containers for this site? This will stop and recreate all containers with the current image.')) {
                return;
            }
            
            const btn = document.getElementById('recreateContainersBtn');
            const originalText = btn.innerHTML;
            btn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Recreating...';
            btn.disabled = true;
            
            fetch('api/sites.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-Token': nonce
                },
                body: JSON.stringify({
                    action: 'recreate_all_containers',
                    site_id: site.id
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    alert('All containers recreated successfully');
                    location.reload();
                } else {
                    alert('Error recreating containers: ' + data.message);
                }
            })
            .catch(error => {
                alert('Network error: ' + error.message);
            })
            .finally(() => {
                btn.innerHTML = originalText;
                btn.disabled = false;
            });
        };
        
        // Show modal
        new bootstrap.Modal(document.getElementById('editSiteModal')).show();

        // Initialize / repopulate the typeahead with this site's domain set.
        // Pre-select the existing non-primary FQDNs so unticking a row means
        // "remove this domain on save" (the update_site action already
        // treats the additional_domains[] list as the full target state).
        const tams = ensureEditTypeahead();
        if (tams) {
            tams.setItems(buildEditDomainItems(site));
            tams.setDisabled([site.primary_domain].filter(Boolean));
            const preselected = (site.fqdns || [])
                .filter(function (f) { return f && !f.is_primary && f.fqdn; })
                .map(function (f) { return f.fqdn; });
            tams.setSelected(preselected);
        }
    }
    
    window.updateEditResourceTotals = function() {
        const containerCount = parseInt(document.getElementById('edit_container_count').value) || 0;
        const cpuPerContainer = parseFloat(document.getElementById('edit_cpu_per_container').value) || 0;
        const memoryPerContainer = parseInt(document.getElementById('edit_memory_per_container').value) || 0;

        const totalCpu = containerCount * cpuPerContainer;
        const totalMemory = containerCount * memoryPerContainer;

        document.getElementById('editTotalCpu').textContent = totalCpu.toFixed(2);
        document.getElementById('editTotalMemory').textContent = totalMemory;

        const totalsDiv = document.getElementById('editResourceTotals');
        if (containerCount > 0) {
            totalsDiv.style.display = 'block';
        } else {
            totalsDiv.style.display = 'none';
        }
    }
    
    // Handle save button click
    document.getElementById('saveSiteBtn').onclick = function() {
        const formData = new FormData(document.getElementById('editSiteForm'));
        formData.append('action', 'update_site');
        formData.append('site_id', currentEditSiteId);
        const primaryFqdn = document.getElementById('edit_primary_domain').value;
        formData.set('primary_domain_id', primaryFqdn);
        // Collect all additional domains from the typeahead-multiselect component.
        const editTams = window.whpAdditionalDomainsEdit;
        const additionalFqdns = editTams ? editTams.getSelected() : [];
        const uniqueFqdns = [...new Set(additionalFqdns.filter(fqdn => fqdn && fqdn !== primaryFqdn))];
        // Remove any existing additional_domains from the form
        formData.delete('additional_domains[]');
        uniqueFqdns.forEach(fqdn => formData.append('additional_domains[]', fqdn));

        // Handle container_mode checkbox (root-only shared httpd toggle)
        const editContainerModeCheckbox = document.getElementById('edit_container_mode');
        if (editContainerModeCheckbox && editContainerModeCheckbox.checked) {
            formData.set('container_mode', 'shared_httpd');
        } else {
            formData.set('container_mode', 'standalone');
        }

        // Disable save button and show loading
        const saveBtn = document.getElementById('saveSiteBtn');
        const originalText = saveBtn.innerHTML;
        saveBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Saving...';
        saveBtn.disabled = true;
        
        fetch('api/sites.php', {
            method: 'POST',
            headers: {
                'X-CSRF-Token': nonce
            },
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === 'success') {
                // Close modal
                bootstrap.Modal.getInstance(document.getElementById('editSiteModal')).hide();
                
                // Show success message
                const alertDiv = document.createElement('div');
                alertDiv.className = 'alert alert-success alert-dismissible fade show';
                alertDiv.innerHTML = `
                    Site updated successfully. Containers will be recreated.
                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                `;
                document.querySelector('.container-fluid').insertBefore(alertDiv, document.querySelector('.container-fluid').firstChild);
                
                // Refresh site list
                location.reload();
            } else {
                alert('Error updating site: ' + data.message);
            }
        })
        .catch(error => {
            alert('Error updating site');
        })
        .finally(() => {
            // Re-enable save button
            saveBtn.innerHTML = originalText;
            saveBtn.disabled = false;
        });
    };
    // Initialize Choices.js for primary domain dropdown
    const primaryDomainDropdown = document.getElementById('primary_domain_id');
    if (primaryDomainDropdown) {
        window.primaryDomainChoices = new Choices(primaryDomainDropdown, {
            searchEnabled: true,
            itemSelectText: '',
            shouldSort: false
        });
        
        // No longer needed - SSL is now a checkbox
    }
});

// SSL Management Functions
//
// Both the per-domain Retry/Issue buttons (in the site details panel) and the
// edit-modal "Force renew now" button funnel results through showSslActionResult,
// which pops a Bootstrap modal with the outcome instead of reloading the page.

function showSslActionResult(success, label, body) {
    const modalEl = document.getElementById('sslActionResultModal');
    const titleEl = document.getElementById('sslActionResultLabel');
    const alertEl = document.getElementById('sslActionAlert');
    const detailsEl = document.getElementById('sslActionDetails');
    const refreshBtn = document.getElementById('sslActionRefreshBtn');
    if (!modalEl || !alertEl) return;

    titleEl.textContent = success
        ? 'SSL action succeeded'
        : 'SSL action failed';
    alertEl.classList.remove('alert-success', 'alert-danger', 'alert-warning');
    alertEl.classList.add(success ? 'alert-success' : 'alert-danger');

    const lead = success
        ? 'Action completed for ' + label + '.'
        : 'Action failed for ' + label + '.';
    const msg = (body && body.message) ? body.message : '';
    alertEl.textContent = lead + (msg ? ' ' + msg : '');

    // Detail block — shows per-domain bucket info from the orchestrator when
    // available (state, classification, or per_domain_buckets from a site bundle).
    detailsEl.textContent = '';
    if (body && body.result) {
        const r = body.result;
        const bits = [];
        if (r.state) bits.push('state=' + r.state);
        if (r.class) bits.push('class=' + r.class);
        if (r.bucket) bits.push('bucket=' + r.bucket);
        if (r.bundle_success !== undefined) bits.push('bundle_success=' + (r.bundle_success ? 'true' : 'false'));
        if (r.san_count !== undefined) bits.push('san_count=' + r.san_count);
        if (Array.isArray(r.per_domain_buckets)) bits.push('per_domain=' + r.per_domain_buckets.join(','));
        if (bits.length) detailsEl.textContent = bits.join('  ·  ');
    }
    // Make the refresh button only meaningful when the user is on a site
    // dropdown (i.e. the details panel is visible). It re-triggers the
    // change handler that fetches get_site_details.
    refreshBtn.onclick = function () {
        const sel = document.getElementById('siteDropdown');
        if (sel) sel.dispatchEvent(new Event('change'));
        bootstrap.Modal.getInstance(modalEl)?.hide();
    };

    bootstrap.Modal.getOrCreateInstance(modalEl).show();
}

function enableSSL() {
    if (!currentEditSiteId) {
        showSslActionResult(false, 'this site', { message: 'No site selected.' });
        return;
    }
    if (!confirm('Enable SSL for this site? Will request Let\'s Encrypt certs for all domains.')) return;

    fetch('/api/sites.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': nonce },
        body: JSON.stringify({ action: 'ssl_enable', site_id: currentEditSiteId, nonce: nonce })
    })
    .then(r => r.json().then(j => ({ ok: r.ok, body: j })))
    .then(({ ok, body }) => {
        showSslActionResult(ok && body.status === 'success', 'this site', body);
    })
    .catch(err => showSslActionResult(false, 'this site', { message: err.message || 'Request failed' }));
}

function renewSSL() {
    if (!currentEditSiteId) {
        showSslActionResult(false, 'this site', { message: 'No site selected.' });
        return;
    }
    if (!confirm('Renew SSL certificates for this site now?')) return;

    fetch('/api/sites.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': nonce },
        body: JSON.stringify({ action: 'ssl_renew', site_id: currentEditSiteId, nonce: nonce })
    })
    .then(r => r.json().then(j => ({ ok: r.ok, body: j })))
    .then(({ ok, body }) => {
        showSslActionResult(ok && body.status === 'success', 'this site', body);
    })
    .catch(err => showSslActionResult(false, 'this site', { message: err.message || 'Request failed' }));
}

// ----- Additional Domains: typeahead + multi-select (typeahead-multiselect.js) -----
//
// Replaces the old per-row "pick one, click to add, repeat" UX. Same data
// source ($available_fqdns_for_creation / $available_fqdns_for_editing) but
// the operator searches, ticks N boxes, and submits once.
//
// The component instance for each modal lives on:
//   window.whpAdditionalDomainsCreate  (create modal)
//   window.whpAdditionalDomainsEdit    (edit modal)
//
// Init is wrapped in DOMContentLoaded because bootstrap.bundle/init runs in
// the footer (see feedback_whp_inline_scripts_need_dom_ready).
const additionalDomainsAvailable = <?php echo json_encode(
    array_values(array_map(function ($row) { return ['value' => $row['fqdn'], 'label' => $row['fqdn']]; }, $available_fqdns_for_creation))
); ?>;
const primaryDomainSelect = document.getElementById('primary_domain_id');

document.addEventListener('DOMContentLoaded', function () {
    const hostCreate = document.getElementById('additional_domains_create');
    if (hostCreate && window.TypeaheadMultiselect) {
        window.whpAdditionalDomainsCreate = new TypeaheadMultiselect(hostCreate, {
            items: additionalDomainsAvailable,
            placeholder: 'Search additional domains/subdomains...',
            emptyMessage: 'No matching domains. Add A or CNAME records in DNS to expand this list.',
            noItemsMessage: 'No additional domains available. Add A or CNAME records in DNS to populate this list.',
            disabledValues: primaryDomainSelect ? [primaryDomainSelect.value].filter(Boolean) : [],
        });
    }
});

// Keep the primary domain disabled in the create-modal picker so it can't
// double-attach.
if (primaryDomainSelect) {
    primaryDomainSelect.addEventListener('change', function () {
        const tams = window.whpAdditionalDomainsCreate;
        if (!tams) return;
        tams.setDisabled([primaryDomainSelect.value].filter(Boolean));
    });
}

// Container readiness checking function
function checkContainerReadiness(siteId, expectedContainerCount, attempt = 1) {
    const maxAttempts = 30; // 30 attempts = 15 seconds maximum wait
    const checkInterval = 500; // Check every 500ms
    
    if (attempt > maxAttempts) {
        // Timeout - containers taking too long to start
        const submitBtn = document.querySelector('#createSiteForm button[type="submit"]');
        submitBtn.innerHTML = '<i class="fa fa-check"></i> Site Created (containers starting...)';
        submitBtn.disabled = false;
        
        // Show a message and reload after short delay
        alert('Site created successfully! Containers are still starting up. The page will reload in a moment.');
        setTimeout(() => location.reload(), 2000);
        return;
    }
    
    fetch(`api/container-status.php?site_id=${siteId}`)
        .then(response => response.json())
        .then(data => {
            if (data.success && data.all_ready) {
                // All containers are ready!
                const submitBtn = document.querySelector('#createSiteForm button[type="submit"]');
                submitBtn.innerHTML = '<i class="fa fa-check"></i> Site Ready!';
                submitBtn.disabled = false;
                
                // Brief success feedback before reload
                setTimeout(() => location.reload(), 1000);
            } else if (data.success) {
                // Containers still starting, show progress
                const submitBtn = document.querySelector('#createSiteForm button[type="submit"]');
                const readyCount = data.ready_count || 0;
                const totalCount = data.container_count || expectedContainerCount;
                submitBtn.innerHTML = `<i class="fa fa-spinner fa-spin"></i> Containers starting (${readyCount}/${totalCount})...`;
                
                // Check again after delay
                setTimeout(() => checkContainerReadiness(siteId, expectedContainerCount, attempt + 1), checkInterval);
            } else {
                // Error checking container status
                console.warn('Container status check failed:', data.error);
                setTimeout(() => checkContainerReadiness(siteId, expectedContainerCount, attempt + 1), checkInterval);
            }
        })
        .catch(error => {
            console.warn('Container status check error:', error);
            // Continue trying despite errors
            setTimeout(() => checkContainerReadiness(siteId, expectedContainerCount, attempt + 1), checkInterval);
        });
}

// 1. After successful site creation, fetch and display new site details
// Find the form submission handler for createSiteForm (look for 'onsubmit' or submit event)
createSiteForm.onsubmit = function(e) {
    e.preventDefault();
    const formData = new FormData(createSiteForm);
    formData.append('action', 'create_site');

    // Collect additional domains from the typeahead-multiselect component.
    const tams = window.whpAdditionalDomainsCreate;
    const primaryFqdnCreate = primaryDomainSelect ? primaryDomainSelect.value : '';
    const additionalFqdns = tams
        ? tams.getSelected().filter(fqdn => fqdn && fqdn !== primaryFqdnCreate)
        : [];

    // Remove any existing additional_domains from the form
    formData.delete('additional_domains[]');
    additionalFqdns.forEach(fqdn => formData.append('additional_domains[]', fqdn));
    
    // Collect user variables if available
    if (window.collectUserVariables) {
        const userVariables = window.collectUserVariables();
        formData.set('user_variables', JSON.stringify(userVariables));
    }

    // Handle container_mode checkbox (root-only shared httpd toggle)
    const containerModeCheckbox = document.getElementById('container_mode');
    if (containerModeCheckbox && containerModeCheckbox.checked) {
        formData.set('container_mode', 'shared_httpd');
    } else {
        formData.set('container_mode', 'standalone');
    }

    // Debug: Log form data for troubleshooting
    console.log('Form data being sent:');
    for (let [key, value] of formData.entries()) {
        console.log(key + ': ' + value);
    }

    // Disable submit button
    const submitBtn = createSiteForm.querySelector('button[type="submit"]');
    const originalText = submitBtn.innerHTML;
    submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Creating...';
    submitBtn.disabled = true;
    fetch('api/sites.php', {
        method: 'POST',
        headers: {
            'X-CSRF-Token': nonce
        },
        body: formData
    })
    .then(response => {
        // Check if response is JSON (API) or HTML (fallback)
        const contentType = response.headers.get('content-type');
        if (contentType && contentType.includes('application/json')) {
            return response.json();
        } else {
            // Fall back to regular form submission if we got HTML response
            throw new Error('Received HTML response, falling back to form submission');
        }
    })
    .then(data => {
        if (data.success && data.site_id) {
            // Site created successfully, now check container readiness
            submitBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Waiting for containers...';
            checkContainerReadiness(data.site_id, data.container_count);
        } else {
            // Site creation failed or partial failure
            alert(data.message || data.error || 'Error creating site');
            location.reload();
        }
    })
    .catch(error => {
        console.log('AJAX failed, falling back to regular form submission:', error.message);
        // Reset button before form submission
        submitBtn.innerHTML = originalText;
        submitBtn.disabled = false;
        // Fall back to regular form submission
        createSiteForm.submit();
    });
};

// 2. In the container status display, use the real container name from the database
// Find the section that renders container status (look for 'Container 1', 'Container 2', etc.)
// Replace with:
// site_containers.forEach(function(container) {
//   html += '<div>' + container.name + ' (' + container.status + ')</div>';
// });

// ---- Shared helper: show message in a result div using safe DOM construction ----
function whpSetResultMsg(divId, isSuccess, msg) {
    const el = document.getElementById(divId);
    if (!el) return;
    el.style.display = '';
    const wrapper = document.createElement('div');
    wrapper.className = 'alert alert-' + (isSuccess ? 'success' : 'danger');
    wrapper.textContent = msg;
    while (el.firstChild) el.removeChild(el.firstChild);
    el.appendChild(wrapper);
}
function whpClearResultDiv(divId) {
    const el = document.getElementById(divId);
    if (!el) return;
    el.style.display = 'none';
    while (el.firstChild) el.removeChild(el.firstChild);
}


// ---- Task 19: Disable / Enable site from detail panel ----
function openDisableSiteModal(siteId, siteName) {
    document.getElementById('disableSiteId').value = siteId;
    document.getElementById('disableSiteName').textContent = siteName;
    document.getElementById('disableSiteReason').value = '';
    document.getElementById('disableSiteNote').value = '';
    whpClearResultDiv('disableSiteResult');
    new bootstrap.Modal(document.getElementById('disableSiteModal')).show();
}

function openEnableSiteModal(siteId, siteName) {
    document.getElementById('enableSiteId').value = siteId;
    document.getElementById('enableSiteName').textContent = siteName;
    whpClearResultDiv('enableSiteResult');
    new bootstrap.Modal(document.getElementById('enableSiteModal')).show();
}

// ---- Advanced Tuning helpers (shared by create + edit forms) ----
// Compute the formula-based recommendation and update the inline badge
// + warning state. Called on memory change, container_type change, and
// on form open. prefix is 'create' or 'edit'.
function whpUpdateFpmRecommendation(prefix, baseImage, memoryMb) {
    const recVal  = document.getElementById(prefix + '_fpm_recommended_val');
    if (!recVal) return;  // accordion isn't on this form
    if (!memoryMb || memoryMb < 128) { recVal.textContent = '—'; return; }

    // Per detect-memory-litespeed.sh: divisor is 130 for cac-litespeed (the
    // bumped value matching active per-worker cost), 128 elsewhere. Plus a
    // ~90 MB reserve for OS/overhead.
    const isLitespeed = /cac-litespeed:php\d+/.test(baseImage || '');
    const divisor = isLitespeed ? 130 : 128;
    const recommended = Math.max(2, Math.min(50, Math.floor((memoryMb - 90) / divisor)));
    recVal.textContent = recommended;

    // Update help text divisor + family label too (covers the create form
    // case where the gate didn't fire yet).
    const divEl = document.getElementById(prefix + '_fpm_divisor');
    const famEl = document.getElementById(prefix + '_fpm_image_family');
    if (divEl) divEl.textContent = String(divisor);
    if (famEl) famEl.textContent = isLitespeed ? 'cac-litespeed (OLS + lsphp)' : 'cac / cac-fpm (Apache + PHP-FPM)';

    // Re-evaluate warning based on the new recommendation.
    whpCheckFpmOverrideWarning(prefix, recommended);
}

// Show/hide the warning if the user-entered value exceeds the recommended.
function whpCheckFpmOverrideWarning(prefix, recommended) {
    const input   = document.getElementById(prefix + '_fpm_max_children_override');
    const warnBox = document.getElementById(prefix + '_fpm_warn');
    const warnTxt = document.getElementById(prefix + '_fpm_warn_text');
    if (!input || !warnBox || !warnTxt) return;
    const v = parseInt(input.value, 10);
    if (!isNaN(v) && recommended && v > recommended) {
        warnTxt.textContent = `Value ${v} exceeds recommended ${recommended}. Each worker uses roughly the per-worker baseline — overshoot is the most common cause of OOM-killed workers and 500-error spikes for visitors.`;
        warnBox.classList.remove('d-none');
    } else {
        warnBox.classList.add('d-none');
    }
}

// Wire 'input' listeners on both forms so warnings recompute as the user types.
document.addEventListener('DOMContentLoaded', function() {
    ['create', 'edit'].forEach(function(prefix) {
        const input = document.getElementById(prefix + '_fpm_max_children_override');
        if (!input) return;
        input.addEventListener('input', function() {
            const recEl = document.getElementById(prefix + '_fpm_recommended_val');
            const rec = recEl ? parseInt(recEl.textContent, 10) : NaN;
            whpCheckFpmOverrideWarning(prefix, rec);
        });
    });

    // Create-form: recompute recommendation when memory or container_type changes.
    const createMem  = document.getElementById('memory_per_container');
    const createType = document.getElementById('container_type_id');
    const createAccordion = document.getElementById('createAdvancedTuningAccordion');
    function createRecompute() {
        if (!createAccordion) return;
        const memMb = parseInt(createMem ? createMem.value : '256', 10) || 256;
        // Find the selected container_type's base_image from the option text/dataset.
        let baseImage = '';
        if (createType && createType.selectedIndex >= 0) {
            const opt = createType.options[createType.selectedIndex];
            baseImage = (opt && (opt.dataset.baseImage || opt.dataset.image || '')) || '';
            // Fallback: derive from option text if data attr isn't set.
            if (!baseImage) {
                const txt = (opt && opt.text) || '';
                if (/LiteSpeed PHP/i.test(txt))    baseImage = 'cac-litespeed:php';
                else if (/^PHP-FPM/i.test(txt))    baseImage = 'cac-fpm:php';
                else if (/^PHP /i.test(txt))       baseImage = 'cac:php';
            }
        }
        const hasPhpPool   = /(cac|cac-fpm|cac-litespeed):php/.test(baseImage);
        const isLitespeed  = /cac-litespeed:php/.test(baseImage);
        createAccordion.style.display = hasPhpPool ? '' : 'none';
        const cOpcacheSep   = document.getElementById('create_opcache_sep');
        const cOpcacheBlock = document.getElementById('create_opcache_block');
        if (cOpcacheSep)   cOpcacheSep.style.display   = isLitespeed ? '' : 'none';
        if (cOpcacheBlock) cOpcacheBlock.style.display = isLitespeed ? '' : 'none';
        whpUpdateFpmRecommendation('create', baseImage, memMb);
    }
    if (createMem)  createMem.addEventListener('input', createRecompute);
    if (createType) createType.addEventListener('change', createRecompute);
    createRecompute();  // initial

    // Edit-form: recompute when memory changes (container_type also affects it
    // but is read-only for non-root in the edit modal in practice).
    const editMem = document.getElementById('edit_memory_per_container');
    if (editMem) {
        editMem.addEventListener('input', function() {
            const baseImage = (window.currentEditSiteData && window.currentEditSiteData.base_image) || '';
            whpUpdateFpmRecommendation('edit', baseImage, parseInt(editMem.value, 10) || 256);
        });
    }

    // Edit-form: Suggest from usage button hits api/sites.php?action=suggest_worker_count
    const suggestBtn = document.getElementById('edit_fpm_suggest_btn');
    if (suggestBtn) {
        // Avoid innerHTML for button state swaps — build via DOM nodes so an
        // audit doesn't have to reason about whether the strings are static.
        function whpSetSuggestBtnState(btn, state) {
            while (btn.firstChild) btn.removeChild(btn.firstChild);
            const icon = document.createElement('i');
            icon.className = (state === 'loading') ? 'fa fa-spinner fa-spin' : 'fa fa-magic';
            btn.appendChild(icon);
            btn.appendChild(document.createTextNode(
                (state === 'loading') ? ' Reading container…' : ' Suggest from usage'
            ));
        }
        suggestBtn.addEventListener('click', async function() {
            // currentEditSiteId is declared `let` at module top-level (line 1807).
            // `let` does NOT attach to window — using window.currentEditSiteId
            // returned undefined and silently no-op'd this button for weeks.
            // Use the bare identifier, same as every other reference in the file.
            const siteId = currentEditSiteId;
            const resultBox = document.getElementById('edit_fpm_suggest_result');
            const input     = document.getElementById('edit_fpm_max_children_override');
            console.log('whp:suggest-worker-count click', { siteId: siteId });
            // Move feedback BEFORE any guard so an unexpected no-siteId state
            // is visible to the user instead of failing silently.
            this.disabled = true;
            whpSetSuggestBtnState(this, 'loading');
            resultBox.classList.add('d-none');
            resultBox.classList.remove('alert-info', 'alert-warning', 'alert-danger');
            if (!siteId) {
                resultBox.textContent = 'No site selected — close and re-open the edit modal.';
                resultBox.classList.remove('d-none');
                resultBox.classList.add('alert-warning');
                this.disabled = false;
                whpSetSuggestBtnState(this, 'idle');
                return;
            }
            try {
                const resp = await fetch('api/sites.php?action=suggest_worker_count&site_id=' + encodeURIComponent(siteId), {
                    method: 'GET',
                    headers: { 'X-CSRF-Token': nonce }
                });
                const data = await resp.json();
                console.log('whp:suggest-worker-count response', { status: resp.status, data: data });
                if (data.status === 'success') {
                    input.value = data.recommended_workers;
                    // Build the result panel via DOM nodes (no innerHTML with untrusted strings).
                    while (resultBox.firstChild) resultBox.removeChild(resultBox.firstChild);
                    const strong = document.createElement('strong');
                    strong.textContent = 'Recommended: ' + data.recommended_workers + ' workers';
                    resultBox.appendChild(strong);
                    if (data.observed) {
                        const o = data.observed;
                        const detail = document.createElement('div');
                        detail.className = 'small mt-1';
                        detail.textContent = `Observed: ${o.workers} workers alive, per-worker ≈ ${o.per_worker_mb} MB; peak ${o.peak_mb}/${o.cap_mb} MB; events.max=${o.events_max}; oom_kill=${o.oom_kill}`;
                        resultBox.appendChild(detail);
                    }
                    if (data.rationale) {
                        const r = document.createElement('div');
                        r.className = 'small mt-1 text-muted';
                        r.textContent = data.rationale;
                        resultBox.appendChild(r);
                    }
                    resultBox.classList.remove('d-none');
                    resultBox.classList.add('alert-info');
                    // Refresh warning state immediately.
                    const recEl = document.getElementById('edit_fpm_recommended_val');
                    const rec = recEl ? parseInt(recEl.textContent, 10) : NaN;
                    whpCheckFpmOverrideWarning('edit', rec);
                } else if (data.status === 'no_container') {
                    resultBox.textContent = data.message || 'No running container.';
                    resultBox.classList.remove('d-none');
                    resultBox.classList.add('alert-warning');
                } else {
                    resultBox.textContent = data.message || 'Suggest failed.';
                    resultBox.classList.remove('d-none');
                    resultBox.classList.add('alert-danger');
                }
            } catch (e) {
                console.log('whp:suggest-worker-count error', e);
                resultBox.textContent = 'Network error: ' + e.message;
                resultBox.classList.remove('d-none');
                resultBox.classList.add('alert-danger');
            } finally {
                this.disabled = false;
                whpSetSuggestBtnState(this, 'idle');
            }
        });
    }
});

(function() {
    const disableConfirmBtn = document.getElementById('disableSiteConfirmBtn');
    if (disableConfirmBtn) {
        disableConfirmBtn.addEventListener('click', function() {
            const siteId = document.getElementById('disableSiteId').value;
            const reason = document.getElementById('disableSiteReason').value;
            if (!reason) { alert('Please select a reason.'); return; }
            const note = document.getElementById('disableSiteNote').value;
            disableConfirmBtn.disabled = true;
            disableConfirmBtn.textContent = 'Disabling…';
            const fd = new FormData();
            fd.append('csrf_token', nonce);
            fd.append('site_id', siteId);
            fd.append('reason', reason);
            if (note) fd.append('note', note);
            fetch('api/site-disable.php', { method: 'POST', body: fd })
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data.ok) {
                        whpSetResultMsg('disableSiteResult', true, 'Site disabled successfully.');
                        setTimeout(function() { location.reload(); }, 1200);
                    } else {
                        whpSetResultMsg('disableSiteResult', false, 'Error: ' + (data.error || 'unknown'));
                        disableConfirmBtn.disabled = false;
                        disableConfirmBtn.textContent = 'Disable Site';
                    }
                })
                .catch(function() {
                    whpSetResultMsg('disableSiteResult', false, 'Request failed.');
                    disableConfirmBtn.disabled = false;
                    disableConfirmBtn.textContent = 'Disable Site';
                });
        });
    }

    const enableConfirmBtn = document.getElementById('enableSiteConfirmBtn');
    if (enableConfirmBtn) {
        enableConfirmBtn.addEventListener('click', function() {
            const siteId = document.getElementById('enableSiteId').value;
            enableConfirmBtn.disabled = true;
            enableConfirmBtn.textContent = 'Enabling…';
            const fd = new FormData();
            fd.append('csrf_token', nonce);
            fd.append('site_id', siteId);
            fetch('api/site-enable.php', { method: 'POST', body: fd })
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    if (data.ok) {
                        whpSetResultMsg('enableSiteResult', true, 'Site enabled successfully.');
                        setTimeout(function() { location.reload(); }, 1200);
                    } else {
                        whpSetResultMsg('enableSiteResult', false, 'Error: ' + (data.error || 'unknown'));
                        enableConfirmBtn.disabled = false;
                        enableConfirmBtn.textContent = 'Enable Site';
                    }
                })
                .catch(function() {
                    whpSetResultMsg('enableSiteResult', false, 'Request failed.');
                    enableConfirmBtn.disabled = false;
                    enableConfirmBtn.textContent = 'Enable Site';
                });
        });
    }
})();

</script> 
