{include file='modules/servers/puqWebHosting/templates/header.tpl'}
{assign var='page_icon'  value='archive'}
{assign var='page_title' value=$L.dz_backups}
{include file='modules/servers/puqWebHosting/templates/_page_header.tpl'}

<div class="puq-msg puq-msg-info" style="font-size:13px;">
    <i class="fa fa-info-circle"></i>
    {$L.dz_backups_intro}
</div>

{* Unified-mode services collapse the Web + Mail cards into one
   "Account backups" card; everything else (busy banner, table shape,
   buttons, JS) reuses the same DOM ids with role='all'. *}
{* Vanity: the mailbox lives on the provider's SHARED mail user, so there is
   no per-customer mail backup — show web backups only. Unified = one combined
   account backup; split = web + mail. *}
{if $deployment_mode eq 'vanity'}{assign var=puq_bk_roles value=['web']}{elseif $deployment_mode eq 'unified'}{assign var=puq_bk_roles value=['all']}{else}{assign var=puq_bk_roles value=['web','mail']}{/if}
{foreach from=$puq_bk_roles item=role}
<div class="puq-card puq-bk-card" id="puq-bk-card-{$role}" data-puq-lock-zone="{if $role eq 'all'}web{else}{$role}{/if}">
    <div class="puq-card-header">
        <i class="fa fa-{if $role eq 'web'}globe{elseif $role eq 'mail'}envelope{else}archive{/if}" style="color:var(--puq-{if $role eq 'web'}primary{elseif $role eq 'mail'}success{else}accent{/if});"></i>
        <strong>{if $role eq 'web'}{$L.dz_web_hosting_backups}{elseif $role eq 'mail'}{$L.dz_mail_backups}{else}{$L.dz_account_backups}{/if}</strong>
        <i class="fas fa-circle-notch fa-spin" id="puq-bk-spinner-{$role}" style="display:none;color:var(--puq-muted);margin-left:6px;font-size:12px;" title="{$L.dz_refreshing}"></i>
        <span class="puq-header-spacer"></span>
        <span class="puq-sync-toolbar" data-role="{if $role eq 'all'}web{else}{$role}{/if}" style="margin-right:8px;"></span>
        <span class="puq-badge puq-badge-muted" id="puq-bk-usage-{$role}" title="{$L.dz_used_limit}" style="margin-right:6px;">…</span>
        <button type="button" class="puq-btn puq-btn-primary puq-btn-sm puq-bk-add" data-role="{$role}" id="puq-bk-add-{$role}"><i class="fa fa-plus"></i> {$L.dz_create_backup}</button>
    </div>
    {* Per-role busy banner — shown only while a backup task is in flight. *}
    <div class="puq-bk-busy-banner" id="puq-bk-busy-{$role}" style="display:none;"></div>
    <div class="puq-card-body" style="padding:0;">
        <div class="puq-bk-table-wrap">
            <table class="table puq-bk-table" id="puq-bk-table-{$role}">
                <thead>
                    <tr>
                        <th class="col-file">{$L.dz_file}</th>
                        <th class="col-size">{$L.dz_size_created}</th>
                        <th class="col-status">{$L.dz_status}</th>
                        <th class="col-actions">{$L.dz_actions}</th>
                    </tr>
                </thead>
                <tbody>
                    <tr><td colspan="4" class="puq-loader"><i class="fas fa-circle-notch fa-spin"></i><span class="puq-loader-caption">{$L.dz_loading_backups}</span></td></tr>
                </tbody>
            </table>
        </div>
    </div>
</div>
{/foreach}

{literal}
<style>
.puq-bk-card .puq-card-header { gap:8px; }
.puq-bk-busy-banner {
    margin: 0;
    padding: 10px 16px;
    background: #fff8e1;
    border-top: 1px solid #ffd966;
    border-bottom: 1px solid #ffd966;
    font-size: 12px;
    color: #8a6d3b;
    display: flex;
    align-items: center;
    gap: 10px;
}
.puq-bk-busy-banner i { color: #c79100; }
.puq-bk-busy-banner strong { color: #6b4f1d; }

/* Backup table — horizontal scroll fallback so a long filename never
   crops out of view; padding bumped so the table doesn't feel cramped. */
.puq-bk-table-wrap { overflow-x: auto; }
.puq-bk-table {
    width: 100%;
    margin: 0;
    border-collapse: collapse;
    font-size: 13px;
}
.puq-bk-table thead th {
    padding: 12px 16px;
    background: #f7f7fa;
    border-bottom: 1px solid var(--puq-border);
    text-align: left;
    font-weight: 600;
    color: #555;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .3px;
    white-space: nowrap;
}
.puq-bk-table tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid var(--puq-border);
    vertical-align: middle;
}
.puq-bk-table tbody tr:last-child td { border-bottom: none; }
.puq-bk-table tbody tr:hover { background: #fafafa; }

/* Column-specific sizing. File takes natural width (no wrap on filename);
   others get fixed widths so headings don't compete. */
.puq-bk-table .col-file    { min-width: 280px; }
.puq-bk-table .col-size    { width: 180px; white-space: nowrap; }
.puq-bk-table .col-status  { width: 110px; }
.puq-bk-table .col-actions { width: 200px; white-space: nowrap; }

/* Stacked Size / Created cell — size on top (bigger), date below (muted). */
.puq-bk-table .bk-size-created .size {
    font-weight: 600;
    color: #333;
    font-size: 13px;
    display: block;
    line-height: 1.3;
}
.puq-bk-table .bk-size-created .created {
    color: var(--puq-muted);
    font-size: 11px;
    display: block;
    line-height: 1.3;
    margin-top: 2px;
}

.puq-bk-table td .bk-filename {
    font-family: var(--puq-mono, ui-monospace, Menlo, Consolas, monospace);
    font-size: 11px;
    color: #c0392b;
    background: rgba(192, 57, 43, 0.06);
    padding: 2px 6px;
    border-radius: 3px;
    white-space: nowrap;
}

/* Locked role section — buttons greyed out + cursor:not-allowed feedback. */
.puq-bk-card-locked .puq-bk-add,
.puq-bk-card-locked .puq-bk-restore,
.puq-bk-card-locked .puq-bk-del { pointer-events: none; opacity: .55; }
.puq-bk-card-locked .puq-bk-table tbody tr { opacity: .85; }
</style>
<script>
window.puqServiceId = {/literal}{$service_id}{literal};
{/literal}{if $deployment_mode eq 'unified'}{literal}
window.puqPageRoles = ['all'];
window.puqBkLimits = {
    all: {/literal}{if isset($limits.account_backups)}{$limits.account_backups|intval}{else}0{/if}{literal}
};
{/literal}{elseif $deployment_mode eq 'vanity'}{literal}
window.puqPageRoles = ['web'];
window.puqBkLimits = {
    web:  {/literal}{if isset($limits.web_backups)}{$limits.web_backups|intval}{else}0{/if}{literal}
};
{/literal}{else}{literal}
window.puqPageRoles = ['web','mail'];
window.puqBkLimits = {
    web:  {/literal}{if isset($limits.web_backups)}{$limits.web_backups|intval}{else}0{/if}{literal},
    mail: {/literal}{if isset($limits.mail_backups)}{$limits.mail_backups|intval}{else}0{/if}{literal}
};
{/literal}{/if}{literal}
</script>
<script>
(function(){
    var TRANSIENT = ['queued','deploying','pending_delete','deleting','restoring','creating','awaiting_remote'];
    var auto = puqAutoRefresh(load);

    // Human label for what's in flight per role. Mirrors puqWebHosting.php
    // stage labels but shorter so it fits in a banner.
    var BUSY_LABELS = {
        backupCreate:       puqT('dz_creating_backup'),
        backupDelete:       puqT('dz_deleting_backup'),
        backupRestore:      puqT('dz_restoring_backup'),
        backupRestoreCheck: puqT('dz_restoring_backup'),
        backupListSync:     puqT('dz_syncing_with_server')
    };

    function updateSyncedLabel(role, data){
        var $el = $('#puq-bk-synced-'+role);
        if (data.syncing && data.syncing[role]) {
            $el.html('<i class="fas fa-circle-notch fa-spin"></i> ' + puqT('dz_syncing_with_server_lc'));
            return;
        }
        // Prefer server-computed relative time (avoids browser-vs-server tz drift).
        var s = data.synced && data.synced[role];
        if (s && s.human) {
            $el.attr('title', s.at || '').text(puqT('dz_synced') + ' ' + s.human);
        } else if (data.synced_at && data.synced_at[role]) {
            $el.attr('title', data.synced_at[role]).text(puqT('dz_synced') + ' ' + puqTimeAgo(data.synced_at[role]));
        } else {
            $el.text(puqT('dz_not_yet_synced'));
        }
    }

    function renderBusyBanner(role, busy){
        var $b = $('#puq-bk-busy-'+role);
        var $card = $('#puq-bk-card-'+role);
        if (!busy) {
            $b.hide().empty();
            $card.removeClass('puq-bk-card-locked');
            return;
        }
        // backupListSync is the cheap "fetch list" call — don't lock the
        // whole role for that (no destructive ops), just hint at the spinner
        // in the header. Only lock for create / delete / restore.
        var lockEditing = (busy.kind !== 'backupListSync');
        var label = BUSY_LABELS[busy.kind] || puqT('dz_backup_op_in_progress');
        var icon  = (busy.kind === 'backupRestore' || busy.kind === 'backupRestoreCheck') ? 'undo'
                  : (busy.kind === 'backupDelete' ? 'trash-alt'
                  : (busy.kind === 'backupCreate' ? 'archive'
                  : 'cog fa-spin'));
        var msg = '<i class="fa fa-' + icon + '"></i> <strong>' + label + '</strong>'
            + (lockEditing
                ? ' ' + puqT('dz_actions_disabled_until')
                : '…');
        $b.html(msg).show();
        if (lockEditing) $card.addClass('puq-bk-card-locked');
        else             $card.removeClass('puq-bk-card-locked');
    }

    function load(){
        var ROLES = window.puqPageRoles || ['web','mail'];
        var spinnerSel = ROLES.map(function(r){ return '#puq-bk-spinner-'+r; }).join(', ');
        ROLES.forEach(function(role){
            puqInitialTableLoader('#puq-bk-table-'+role+' tbody', 'data-filename', 5, puqT('dz_loading_backups'));
        });
        puqSetSpinner(spinnerSel, true);
        puqClientAjax('backupsList', {}, function(r){
            puqSetSpinner(spinnerSel, false);
            if (!r || r.status !== 'success') {
                if (window.puqToast) puqToast('error', (r && r.msg) || puqT('dz_failed_load_backups'));
                auto.schedule(false);
                return;
            }
            var d = r.data || {};
            var byRole = {};
            ROLES.forEach(function(role){ byRole[role] = []; });
            (d.backups || []).forEach(function(b){
                if (Object.prototype.hasOwnProperty.call(byRole, b.role)) byRole[b.role].push(b);
            });
            var anyPending = false;
            ROLES.forEach(function(role){
                var rows = byRole[role] || [];
                var $tb = $('#puq-bk-table-'+role+' tbody');
                var counts = (d.counts || {})[role] || {used:0, limit:0};
                puqUpdateQuotaBadge('#puq-bk-usage-'+role, '#puq-bk-add-'+role, counts.used, counts.limit, puqT('dz_create_backup'));
                updateSyncedLabel(role, d);

                // Per-role lock + banner — shown while any backup task is in flight.
                var busy = (d.busy || {})[role] || null;
                renderBusyBanner(role, busy);
                if (busy) anyPending = true;
                // Also pre-disable the Create button when busy (puqUpdateQuotaBadge
                // re-renders it; reapply disabled state here).
                if (busy && busy.kind !== 'backupListSync') {
                    $('#puq-bk-add-'+role).prop('disabled', true).addClass('disabled');
                } else {
                    $('#puq-bk-add-'+role).prop('disabled', false).removeClass('disabled');
                }

                var html = '';
                if (!rows.length) {
                    html = '<tr><td colspan="4" class="text-muted text-center" style="padding:32px 16px;">'
                         + '<i class="fa fa-archive" style="font-size:22px;opacity:.35;margin-bottom:8px;display:block;"></i>'
                         + puqT('dz_no_backups_yet') + '</td></tr>';
                } else {
                    rows.forEach(function(b){
                        var ed = (b.deploy_status === 'active');
                        var op = b.deploy_status === 'restoring' ? 'update' : null;
                        var actionable = (b.deploy_status === 'active' || b.deploy_status === 'error');
                        var fname = b.filename || '—';
                        // Unified restore reuses the per-role backupRestore
                        // worker via $role='web' (web_restore_lock locks the
                        // whole account — see backupRestore client endpoint
                        // and assertRoleNotLocked). v-restore-user is
                        // naturally account-wide.
                        var canRestore = ed;
                        html += '<tr data-id="' + (b.id || '') + '" data-filename="' + puqEscAttr(b.filename || '') + '" data-role="' + role + '">' +
                            '<td class="col-file"><span class="bk-filename" title="' + puqEscAttr(fname) + '">' + puqEsc(fname) + '</span></td>' +
                            '<td class="col-size bk-size-created">' +
                                '<span class="size">' + puqEsc(puqFormatBytes(b.size_bytes)) + '</span>' +
                                '<span class="created">' + puqEsc(b.created_at || '—') + '</span>' +
                            '</td>' +
                            '<td class="col-status">' + puqDeployBadge(b.deploy_status, b.deploy_error, op) + '</td>' +
                            '<td class="col-actions">' +
                                (canRestore ? '<button type="button" class="puq-btn puq-btn-warning puq-btn-sm puq-bk-restore" title="' + puqT('dz_restore_from_snapshot') + '"><i class="fa fa-undo"></i> ' + puqT('dz_restore') + '</button> ' : '') +
                                (actionable ? '<button type="button" class="puq-btn puq-btn-danger puq-btn-sm puq-bk-del" title="' + puqT('dz_delete_snapshot') + '"><i class="fa fa-trash"></i></button>' : '') +
                            '</td></tr>';
                        if (TRANSIENT.indexOf(b.deploy_status) >= 0) anyPending = true;
                    });
                }
                $tb.html(html);
            });
            if (d.syncing) {
                ROLES.forEach(function(role){
                    if (d.syncing[role]) anyPending = true;
                });
            }
            auto.schedule(anyPending);
        });
    }

    // Click handlers — also re-check busy state right before the AJAX call
    // (race: user clicked between an auto-refresh tick that came idle and
    // a task that landed just after).
    function roleIsBusy(role){
        return $('#puq-bk-card-'+role).hasClass('puq-bk-card-locked');
    }

    function roleLabel(role){
        if (role === 'web')  return puqT('dz_role_web');
        if (role === 'mail') return puqT('dz_role_mail');
        return puqT('dz_role_account');
    }
    $(document).on('click', '.puq-bk-add', function(e){
        e.preventDefault();
        var role = $(this).data('role');
        if (roleIsBusy(role)) {
            puqToast('info', puqT('dz_op_running_pre') + roleLabel(role) + puqT('dz_op_running_post'));
            return;
        }
        var lim = window.puqBkLimits[role] || 0;
        var used = $('#puq-bk-table-'+role+' tbody tr[data-filename]').length;
        if (lim > 0 && used >= lim) {
            puqConfirm({
                type: 'warning', icon: 'fa-ban',
                title: puqT('dz_backup_limit_reached'),
                text: puqT('dz_backup_limit_body_pre') + '<strong>' + lim + '</strong> ' + roleLabel(role).toLowerCase() + puqT('dz_backup_limit_body_post'),
                ok: puqT('dz_got_it'), cancel: puqT('dz_close')
            });
            return;
        }
        puqConfirm({
            type: 'info', icon: 'fa-archive',
            title: puqT('dz_create_backup_q_pre') + roleLabel(role).toLowerCase() + puqT('dz_create_backup_q_post'),
            text: puqT('dz_create_backup_body_pre') + roleLabel(role).toLowerCase() + puqT('dz_create_backup_body_post'),
            ok: puqT('dz_create'), cancel: puqT('dz_cancel'),
            onConfirm: function(){
                return new Promise(function(resolve, reject){
                    puqClientAjax('backupCreate', { role: role }, function(r){
                        if (r && r.status === 'success') { puqToast('success', puqT('dz_backup_queued')); load(); resolve(); }
                        else reject(new Error((r && r.msg) || puqT('dz_create_failed')));
                    }, 'POST');
                });
            }
        });
    });

    $(document).on('click', '.puq-bk-del', function(e){
        e.preventDefault();
        var $row = $(this).closest('tr');
        var role = $row.data('role');
        var filename = $row.data('filename');
        var backupId = $row.data('id');
        if (roleIsBusy(role)) {
            puqToast('info', puqT('dz_op_running_pre') + roleLabel(role) + puqT('dz_op_running_post'));
            return;
        }
        puqConfirm({
            type: 'danger', icon: 'fa-trash-alt',
            title: puqT('dz_delete_backup_q'),
            // A failed backup has no filename — show a generic label instead of an empty code box.
            text: puqT('dz_delete_backup_body_pre') + '<code>' + puqEsc(filename || '—') + '</code>' + puqT('dz_delete_backup_body_post'),
            ok: puqT('dz_delete'), cancel: puqT('dz_keep'),
            onConfirm: function(){
                return new Promise(function(resolve, reject){
                    // Send the id (works for failed rows with no filename) + filename for back-compat.
                    puqClientAjax('backupDelete', { backup_id: backupId, filename: filename }, function(r){
                        if (r && r.status === 'success') { puqToast('success', puqT('dz_delete_queued')); load(); resolve(); }
                        else reject(new Error((r && r.msg) || puqT('dz_delete_failed')));
                    }, 'POST');
                });
            }
        });
    });

    $(document).on('click', '.puq-bk-restore', function(e){
        e.preventDefault();
        var $row = $(this).closest('tr');
        var filename = $row.data('filename');
        var role = $row.data('role');
        if (roleIsBusy(role)) {
            puqToast('info', puqT('dz_op_running_pre') + roleLabel(role) + puqT('dz_op_running_post'));
            return;
        }
        var overwriteTarget = (role === 'all') ? puqT('dz_entire_account') : (roleLabel(role).toLowerCase() + ' ' + puqT('dz_files'));
        puqPrompt({
            type: 'danger', icon: 'fa-undo',
            title: puqT('dz_restore_backup_q'),
            text: puqT('dz_restore_body_pre') + '<code>' + puqEsc(filename) + '</code>' + puqT('dz_restore_body_mid') + overwriteTarget +
                  puqT('dz_restore_body_post'),
            placeholder: 'RESTORE', ok: puqT('dz_restore'),
            validator: function(v){ return (v || '').trim().toUpperCase() === 'RESTORE'; },
            validatorMsg: puqT('dz_type_restore_uppercase'),
            onSubmit: function(){
                return new Promise(function(resolve, reject){
                    puqClientAjax('backupRestore', { filename: filename }, function(r){
                        if (r && r.status === 'success') { puqToast('success', puqT('dz_restore_queued')); load(); resolve(); }
                        else reject(new Error((r && r.msg) || puqT('dz_restore_failed')));
                    }, 'POST');
                });
            }
        });
    });

    $(document).ready(load);
})();
</script>
{/literal}
