{include file='modules/servers/puqWebHosting/templates/header.tpl'}

<div class="puq-dash-header" style="display:flex;align-items:center;gap:10px;margin-bottom:14px;">
    <a class="puq-btn puq-btn-outline puq-btn-sm" href="clientarea.php?action=productdetails&id={$service_id}"><i class="fa fa-arrow-left"></i> {$L.wf_back_to_overview}</a>
    <h2 style="margin:0;font-size:20px;"><i class="fa fa-globe" style="color:var(--puq-primary);"></i> {$L.wf_web_settings} &nbsp;<small style="color:var(--puq-muted);">— {$domain}</small></h2>
</div>

<div class="puq-card" data-puq-lock-zone="web">
    <div class="puq-card-header" style="display:flex;align-items:center;gap:8px;">
        <i class="fas fa-cogs"></i> {$L.wf_hosting_configuration}
        <span style="flex:1;"></span>
        <span class="puq-sync-toolbar" data-role="web"></span>
    </div>
    <div class="puq-card-body">
        <div id="puq-web-state" class="puq-loader"><i class="fas fa-circle-notch fa-spin"></i> {$L.wf_loading_settings}</div>

        <div id="puq-web-form" style="display:none;">
            {* --- PHP version dropdown — sourced from cached server inventory
                   (status_data['sys_php'], refreshed by syncServerStatus cron). *}
            <div class="form-group" style="margin-bottom:18px;">
                <label style="font-weight:600;display:block;margin-bottom:4px;">
                    <i class="fab fa-php" style="color:#777bb4;"></i> {$L.wf_php_version}
                </label>
                <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
                    <select id="puq-web-php-select" class="form-control" style="width:auto;flex:0 0 200px;"></select>
                    <button type="button" id="puq-web-php-save" class="puq-btn puq-btn-primary puq-btn-sm">
                        <i class="fa fa-save"></i> {$L.wf_apply}
                    </button>
                    <span id="puq-web-php-status" style="display:none;"></span>
                </div>
            </div>

            <hr>

            {* --- HTTPS redirect --- *}
            <div class="form-group" style="margin-bottom:8px;">
                <label style="font-weight:600;display:block;margin-bottom:4px;">
                    <i class="fa fa-lock" style="color:var(--puq-success);"></i> {$L.wf_https_redirection}
                </label>
                <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
                    <label class="puq-toggle" style="display:inline-flex;align-items:center;gap:8px;font-weight:normal;">
                        <input type="checkbox" id="puq-web-https-redirect">
                        <span id="puq-web-https-label">{$L.wf_auto_redirect_http_https}</span>
                    </label>
                    <button type="button" id="puq-web-https-save" class="puq-btn puq-btn-primary puq-btn-sm">
                        <i class="fa fa-save"></i> {$L.wf_apply}
                    </button>
                    <span id="puq-web-https-status" style="display:none;"></span>
                </div>
                <p id="puq-web-https-help" style="margin-top:6px;font-size:11px;color:var(--puq-muted);">
                    {$L.wf_https_help}
                </p>
            </div>

            <hr>

            {* --- Proxy extensions --- *}
            <div class="form-group" style="margin-bottom:8px;">
                <label style="font-weight:600;display:block;margin-bottom:4px;">
                    <i class="fa fa-bolt" style="color:#f0ad4e;"></i> {$L.wf_proxy_extensions}
                    <span id="puq-web-proxy-state" style="font-weight:normal;font-size:11px;margin-left:6px;"></span>
                </label>
                <div style="display:flex;gap:10px;align-items:flex-start;flex-wrap:wrap;">
                    <textarea id="puq-web-proxy-ext"
                        class="form-control"
                        rows="3"
                        spellcheck="false"
                        style="flex:1 1 380px;min-width:280px;font-family:monospace;font-size:12px;"
                        placeholder="jpg, png, css, js, …"></textarea>
                    <div style="display:flex;flex-direction:column;gap:6px;">
                        <button type="button" id="puq-web-proxy-save" class="puq-btn puq-btn-primary puq-btn-sm">
                            <i class="fa fa-save"></i> {$L.wf_apply}
                        </button>
                        <button type="button" id="puq-web-proxy-reset" class="puq-btn puq-btn-outline puq-btn-sm" title="{$L.wf_restore_default_list}">
                            <i class="fa fa-undo"></i> {$L.wf_defaults}
                        </button>
                    </div>
                    <span id="puq-web-proxy-status" style="display:none;align-self:center;"></span>
                </div>
                <p style="margin-top:6px;font-size:11px;color:var(--puq-muted);">
                    {$L.wf_proxy_help} <code>jpg, png, css, js, woff2</code>.
                </p>
            </div>
        </div>

        <div id="puq-web-msg" style="margin-top:10px;"></div>
    </div>
</div>

<script>window.puqServiceId = {$service_id}; window.puqPageRoles = ['web'];</script>
{literal}
<script>
(function(){
    var pollTimer = null;

    // Minimal status indicator next to the control:
    //   queued/running → spinning circle (no task ids, no internal names)
    //   error          → red ⚠ with generic tooltip
    //   success / cancelled / (none) → nothing
    // Apply button itself stays plain "Apply" — only `disabled` toggles.
    function renderTaskBadge($el, task) {
        if (!task) { $el.hide().empty(); return; }
        if (task.state === 'queued' || task.state === 'running') {
            $el.show().html(
                '<i class="fa fa-spinner fa-spin" style="color:var(--puq-primary);font-size:16px;"></i>'
            );
            return;
        }
        if (task.state === 'error') {
            $el.show().html(
                '<i class="fas fa-exclamation-triangle" '
                + 'style="color:var(--puq-danger);cursor:help;font-size:16px;" '
                + 'title="' + puqT('wf_change_failed_tooltip') + '"></i>'
            );
            return;
        }
        // success / cancelled / unknown → hide
        $el.hide().empty();
    }

    // Should the control be locked? Only when task is queued or running.
    // success/error/cancelled → UNLOCK so user can retry.
    function isTaskBusy(task) {
        return task && (task.state === 'queued' || task.state === 'running');
    }

    function load() {
        puqClientAjax('webSettingsData', {}, function(r){
            var $state = $('#puq-web-state');
            var $form  = $('#puq-web-form');
            if (r.status !== 'success' || !r.data) {
                $state.removeClass('puq-loader').html(
                    '<div class="puq-msg puq-msg-error"><i class="fa fa-exclamation-triangle"></i> '
                    + puqEsc(r.msg||puqT('wf_failed_to_load')) + '</div>'
                );
                return;
            }
            var d = r.data;
            var phpServerBusy   = isTaskBusy(d.php_task);
            var httpsServerBusy = isTaskBusy(d.https_task);
            var proxyServerBusy = isTaskBusy(d.proxy_task) || isTaskBusy(d.proxy_off_task);

            // Toast transitions: queued/running → success/error/cancelled.
            // We can no longer key by task id (intentionally not exposed),
            // so we use the (updated_at, state) tuple — a NEW change shows
            // up as a state-back-to-queued with a fresher updated_at, which
            // means the prior cycle has ended and a new one started. First
            // load is suppressed (initial snapshot, not a transition).
            if (!firstLoad) {
                if (d.php_task && (phpLastState === 'queued' || phpLastState === 'running')
                    && d.php_task.state !== phpLastState
                    && d.php_task.updated_at !== phpLastUpdatedAt) {
                    if (d.php_task.state === 'success') {
                        puqToast('success', puqT('wf_php_applied'));
                    } else if (d.php_task.state === 'error') {
                        puqToast('error', puqT('wf_php_change_failed'));
                    }
                }
                if (d.https_task && (httpsLastState === 'queued' || httpsLastState === 'running')
                    && d.https_task.state !== httpsLastState
                    && d.https_task.updated_at !== httpsLastUpdatedAt) {
                    if (d.https_task.state === 'success') {
                        puqToast('success', puqT('wf_https_applied'));
                    } else if (d.https_task.state === 'error') {
                        puqToast('error', puqT('wf_https_change_failed'));
                    }
                }
                var proxyLatest = d.proxy_task;
                if (d.proxy_off_task && (!proxyLatest || (d.proxy_off_task.updated_at || '') > (proxyLatest.updated_at || ''))) {
                    proxyLatest = d.proxy_off_task;
                }
                if (proxyLatest && (proxyLastState === 'queued' || proxyLastState === 'running')
                    && proxyLatest.state !== proxyLastState
                    && proxyLatest.updated_at !== proxyLastUpdatedAt) {
                    if (proxyLatest.state === 'success') {
                        puqToast('success', puqT('wf_proxy_applied'));
                    } else if (proxyLatest.state === 'error') {
                        puqToast('error', puqT('wf_proxy_update_failed'));
                    }
                }
            }
            // Snapshot for next poll
            phpLastState        = d.php_task   ? d.php_task.state      : null;
            phpLastUpdatedAt    = d.php_task   ? d.php_task.updated_at : null;
            httpsLastState      = d.https_task ? d.https_task.state      : null;
            httpsLastUpdatedAt  = d.https_task ? d.https_task.updated_at : null;
            var proxyLatestSnap = d.proxy_task;
            if (d.proxy_off_task && (!proxyLatestSnap || (d.proxy_off_task.updated_at || '') > (proxyLatestSnap.updated_at || ''))) {
                proxyLatestSnap = d.proxy_off_task;
            }
            proxyLastState     = proxyLatestSnap ? proxyLatestSnap.state      : null;
            proxyLastUpdatedAt = proxyLatestSnap ? proxyLatestSnap.updated_at : null;
            firstLoad = false;

            // Server-authoritative reset of local lock: if server says task
            // is NOT busy (success/error/cancelled/none), clear local flag.
            if (!phpServerBusy   && phpBusyLocal)   phpBusyLocal   = false;
            if (!httpsServerBusy && httpsBusyLocal) httpsBusyLocal = false;
            if (!proxyServerBusy && proxyBusyLocal) proxyBusyLocal = false;
            // Effective busy = server OR our local guard.
            var phpBusy   = phpServerBusy   || phpBusyLocal;
            var httpsBusy = httpsServerBusy || httpsBusyLocal;
            var proxyBusy = proxyServerBusy || proxyBusyLocal;

            // --- PHP version dropdown ---
            var $sel = $('#puq-web-php-select').empty();
            var tpls = d.templates || [];
            var curVersion = '';
            var cm = (d.current || '').match(/^PHP-(\d+)_(\d+)$/);
            if (cm) curVersion = cm[1] + '.' + cm[2];

            if (!tpls.length) {
                $sel.append('<option value="">' + puqT('wf_no_php_versions') + '</option>')
                    .prop('disabled', true);
                $('#puq-web-php-save').prop('disabled', true);
            } else {
                tpls.forEach(function(t){
                    var sel = (t.version === curVersion) ? ' selected' : '';
                    $sel.append('<option value="' + puqEsc(t.template) + '"' + sel + '>PHP ' + puqEsc(t.version) + '</option>');
                });
                $sel.prop('disabled', phpBusy);
                $('#puq-web-php-save').prop('disabled', phpBusy);
            }
            renderTaskBadge($('#puq-web-php-status'), d.php_task);

            // --- HTTPS redirect checkbox ---
            var $cb = $('#puq-web-https-redirect');
            $cb.prop('checked', !!d.https_redirect);
            if (!d.ssl_active) {
                $cb.prop('disabled', true);
                $('#puq-web-https-save').prop('disabled', true);
                $('#puq-web-https-help').html('<i class="fa fa-info-circle"></i> ' + puqT('wf_https_needs_ssl'));
            } else {
                $cb.prop('disabled', httpsBusy);
                $('#puq-web-https-save').prop('disabled', httpsBusy);
                $('#puq-web-https-help').html(puqT('wf_https_help'));
            }
            renderTaskBadge($('#puq-web-https-status'), d.https_task);

            // --- Proxy extensions ---
            var $pxArea = $('#puq-web-proxy-ext');
            var px = d.proxy || {};
            // Cache the server-provided default list so the Defaults button
            // can fill the textarea instantly without re-fetching.
            if (px.default_ext) proxyDefaultExt = String(px.default_ext);
            var serverExt = String(px.extensions || '');

            if (submittedProxyExt !== null) {
                // Apply was clicked — task is in flight. Keep showing the
                // submitted value (what the customer is waiting to land on
                // the server) until proxy_task settles (transitions away
                // from queued/running). Once settled — refill from server
                // (which should now match the submitted value if Apply
                // succeeded; if not, the user sees Hestia's actual state).
                if (proxyServerBusy) {
                    $pxArea.val(submittedProxyExt);
                } else {
                    submittedProxyExt = null;
                    $pxArea.data('userEdited', false);
                    $pxArea.val(serverExt);
                }
            } else if (!$pxArea.data('userEdited') || $pxArea.val() === '') {
                // No pending submit — refill from server unless the customer
                // is mid-edit (typing in the textarea before clicking Apply).
                $pxArea.val(serverExt);
            }
            var $pxState = $('#puq-web-proxy-state');
            if (px.enabled) {
                $pxState.html('<span style="color:var(--puq-success);"><i class="fa fa-check-circle"></i> ' + puqT('wf_enabled') + '</span>');
            } else {
                $pxState.html('<span style="color:var(--puq-muted);"><i class="fa fa-pause-circle"></i> ' + puqT('wf_disabled') + '</span>');
            }
            $pxArea.prop('disabled', proxyBusy);
            $('#puq-web-proxy-save').prop('disabled', proxyBusy);
            $('#puq-web-proxy-reset').prop('disabled', proxyBusy);
            // Pick whichever proxy task is more recent for the status badge.
            var proxyBadgeTask = d.proxy_task;
            if (d.proxy_off_task && (!proxyBadgeTask || (d.proxy_off_task.updated_at || '') > (proxyBadgeTask.updated_at || ''))) {
                proxyBadgeTask = d.proxy_off_task;
            }
            renderTaskBadge($('#puq-web-proxy-status'), proxyBadgeTask);

            $state.removeClass('puq-loader').empty();
            $form.show();

            // Auto-poll every 5s while ANY task is in-flight, then stop.
            if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
            if (phpBusy || httpsBusy || proxyBusy) pollTimer = setTimeout(load, 5000);
        });
    }

    // Optimistic lock managed ENTIRELY by us — no puqBtnSpin / puqBtnRestore.
    // Those helpers restore button HTML on completion, which silently strips
    // our `disabled` attr and gives the customer a ~200 ms window to spam-
    // click between the success callback and the load()-refresh response.
    //
    // Pattern:
    //   1. On click: HARD-LOCK synchronously (save button HTML, replace
    //      with disabled spinner; disable the input).
    //   2. Reentrancy guard: if button is already disabled, bail.
    //   3. Send the enqueue AJAX.
    //   4. On success → KEEP the lock; call load() which sees the new
    //      in_progress task in DB and continues showing the lock.
    //   5. On failure → restoreXxx() puts the button back, lets user retry.

    // Module-scoped busy flags — primary guard against double-click. DOM
    // `disabled` can race with re-renders / CSS / browser quirks; a plain
    // JS variable is bulletproof. Set sync on click, cleared only when:
    //  (a) load() sees no in_progress task → released to allow next change
    //  (b) enqueue failed → released immediately
    var phpBusyLocal   = false;
    var httpsBusyLocal = false;
    var proxyBusyLocal = false;

    // Last-seen task state per control — for detecting completion
    // transitions (queued/running → success/error) to fire a toast.
    // Initialised from the first load; subsequent polls compare against it.
    // Transition trackers — state + updated_at (no task ids exposed to client).
    var phpLastState   = null, phpLastUpdatedAt   = null;
    var httpsLastState = null, httpsLastUpdatedAt = null;
    var proxyLastState = null, proxyLastUpdatedAt = null;
    var firstLoad = true;

    // Spinner lives in the BADGE, not inside the button. Button stays
    // labelled "Apply" and only its `disabled` attr toggles.
    function spinnerBadge() {
        return '<i class="fa fa-spinner fa-spin" style="color:var(--puq-primary);font-size:16px;"></i>';
    }
    function lockPhp() {
        phpBusyLocal = true;
        $('#puq-web-php-select').prop('disabled', true);
        $('#puq-web-php-save').prop('disabled', true);
        $('#puq-web-php-status').show().html(spinnerBadge());
    }
    function restorePhp() {
        phpBusyLocal = false;
        $('#puq-web-php-select').prop('disabled', false);
        $('#puq-web-php-save').prop('disabled', false);
        $('#puq-web-php-status').hide().empty();
    }
    function lockHttps() {
        httpsBusyLocal = true;
        $('#puq-web-https-redirect').prop('disabled', true);
        $('#puq-web-https-save').prop('disabled', true);
        $('#puq-web-https-status').show().html(spinnerBadge());
    }
    function restoreHttps() {
        httpsBusyLocal = false;
        $('#puq-web-https-redirect').prop('disabled', false);
        $('#puq-web-https-save').prop('disabled', false);
        $('#puq-web-https-status').hide().empty();
    }
    function lockProxy() {
        proxyBusyLocal = true;
        $('#puq-web-proxy-ext').prop('disabled', true);
        $('#puq-web-proxy-save').prop('disabled', true);
        $('#puq-web-proxy-reset').prop('disabled', true);
        $('#puq-web-proxy-status').show().html(spinnerBadge());
    }
    function restoreProxy() {
        proxyBusyLocal = false;
        $('#puq-web-proxy-ext').prop('disabled', false);
        $('#puq-web-proxy-save').prop('disabled', false);
        $('#puq-web-proxy-reset').prop('disabled', false);
        $('#puq-web-proxy-status').hide().empty();
    }

    $('#puq-web-php-save').on('click', function(e){
        e.preventDefault();
        e.stopImmediatePropagation();
        // PRIMARY guard — local flag set sync on click.
        if (phpBusyLocal) return;
        var tpl = $('#puq-web-php-select').val();
        if (!tpl) return;
        lockPhp();                                     // sets phpBusyLocal = true
        puqClientAjax('setBackendTemplate', { template: tpl }, function(r){
            if (r.status === 'success') {
                var verMatch = tpl.match(/^PHP-(\d+)_(\d+)$/);
                var ver = verMatch ? (verMatch[1] + '.' + verMatch[2]) : tpl;
                puqToast('success', puqT('wf_switching_to_php') + ' ' + ver + ' — ' + puqT('wf_queued'));
                load(); // load() will read in_progress and KEEP lock
            } else {
                puqToast('error', r.msg || puqT('wf_failed'));
                restorePhp();                          // enqueue rejected → release
            }
        }, 'POST');
    });

    $('#puq-web-https-save').on('click', function(e){
        e.preventDefault();
        e.stopImmediatePropagation();
        if (httpsBusyLocal) return;
        var enabled = $('#puq-web-https-redirect').is(':checked') ? 1 : 0;
        lockHttps();
        puqClientAjax('setHttpsRedirect', { enabled: enabled }, function(r){
            if (r.status === 'success') {
                puqToast('success', enabled ? puqT('wf_https_enabled_queued') : puqT('wf_https_disabled_queued'));
                load();
            } else {
                puqToast('error', r.msg || puqT('wf_failed'));
                restoreHttps();
            }
        }, 'POST');
    });

    // Track user edits so poll() doesn't clobber an in-progress textarea.
    $('#puq-web-proxy-ext').on('input', function(){ $(this).data('userEdited', true); });

    // Server-provided default extensions — captured from each load() call
    // so the Defaults button can fill the textarea WITHOUT an extra AJAX hop
    // (and works even if a subsequent webSettingsData call would fail).
    var proxyDefaultExt = '';

    // What the user just submitted via Apply — pinned in the textarea while
    // the task is in flight so the customer doesn't see their typed value
    // get replaced by the stale server snapshot mid-wait. Cleared by load()
    // when the task transitions out of queued/running.
    var submittedProxyExt = null;

    // Validate the textarea contents against the same rules the driver
    // applies before sending to Hestia. Pure alphanumeric per token blocks
    // every nginx-config injection vector (no '/', '.', ';', quotes, regex
    // metacharacters, no shell metacharacters) since the driver passes
    // these tokens via escapeshellarg to v-add-web-domain-proxy.
    function validateProxyExtensions(raw) {
        raw = String(raw || '').trim();
        if (raw === '') return { ok: false, msg: puqT('wf_ext_required_defaults') };
        if (raw.length > 2048) return { ok: false, msg: puqT('wf_ext_list_too_long') };
        var tokens = raw.split(/[\s,]+/).filter(Boolean);
        if (tokens.length === 0) return { ok: false, msg: puqT('wf_ext_required') };
        if (tokens.length > 200) return { ok: false, msg: puqT('wf_ext_too_many') };
        var seen = {};
        for (var i = 0; i < tokens.length; i++) {
            var t = tokens[i];
            if (t.length > 20) return { ok: false, msg: puqT('wf_ext_too_long') + ' ' + t };
            if (!/^[A-Za-z0-9]+$/.test(t)) {
                return { ok: false, msg: puqT('wf_ext_invalid') + ' ' + t + ' — ' + puqT('wf_ext_letters_digits') };
            }
            // Dedup silently (case-insensitive — nginx-side ext match is too).
            seen[t.toLowerCase()] = true;
        }
        // Return the normalised csv so caller can show it to the user if it
        // differs from input (drops dups, trims whitespace).
        return { ok: true, normalised: Object.keys(seen).join(',') };
    }

    $('#puq-web-proxy-save').on('click', function(e){
        e.preventDefault();
        e.stopImmediatePropagation();
        if (proxyBusyLocal) return;
        var raw = String($('#puq-web-proxy-ext').val() || '').trim();
        var v = validateProxyExtensions(raw);
        if (!v.ok) {
            puqToast('error', v.msg);
            return;
        }
        lockProxy();
        // Pin the submitted value in the textarea so the customer sees
        // what they're waiting on (not the stale server snapshot). load()
        // will keep this in the box as long as proxy_task is queued/running
        // and clear it once the task settles.
        submittedProxyExt = v.normalised;
        $('#puq-web-proxy-ext').val(v.normalised);
        puqClientAjax('setProxyExtensions', { extensions: v.normalised, template: 'default' }, function(r){
            if (r.status === 'success') {
                puqToast('success', puqT('wf_proxy_update_queued'));
                load();
            } else {
                // AJAX-level failure — clear the pin so server data refills.
                submittedProxyExt = null;
                $('#puq-web-proxy-ext').data('userEdited', false);
                puqToast('error', r.msg || puqT('wf_failed'));
                restoreProxy();
            }
        }, 'POST');
    });

    $('#puq-web-proxy-reset').on('click', function(e){
        e.preventDefault();
        // Defaults — UI-only convenience. Fill from the server-provided list
        // captured during the last load(). NO AJAX hop (was unreliable when
        // the response was momentarily empty). Customer still hits Apply.
        if (proxyDefaultExt === '') {
            puqToast('error', puqT('wf_default_list_not_loaded'));
            return;
        }
        $('#puq-web-proxy-ext').val(proxyDefaultExt).data('userEdited', true);
    });

    load();
})();
</script>
{/literal}
