// Auto-extracted from backup-page.js (verbatim) — augments BackupPage.prototype. Loaded after the base.
Object.assign(BackupPage.prototype, {
    renderLocations() {
        const list = document.getElementById('backup-location-list');
        const repoSelect = document.getElementById('backup-snapshot-repo');
        if (!list) return;

        const locs = this.locations?.locations || [];
        if (!locs.length) {
            list.innerHTML = `
                <div class="backup-empty-state">
                    No backup locations configured yet.<br>
                    Click <strong>Add location</strong> above to create one.
                </div>
            `;
        } else {
            list.innerHTML = locs.map(l => this.renderLocationRow(l)).join('');
        }

        if (repoSelect) {
            const cur = repoSelect.value;
            repoSelect.innerHTML = `<option value="">All locations</option>` +
                locs.filter(l => l.enabled).map(l => `<option value="${l.idx}">${this.escape(l.name)}</option>`).join('');
            if (cur) repoSelect.value = cur;
        }
    },
    renderLocationRow(l) {
        // Status pill mirrors task-status: ✅ Ready / ⏳ Initialising / ⏸ Disabled.
        const statusKind = l.enabled && l.password_exists ? 'ready'
                         : l.enabled && !l.password_exists ? 'init'
                         : 'disabled';
        const statusMeta = {
            ready:    { icon: '✅', label: 'Ready'        },
            init:     { icon: '⏳', label: 'Initialising' },
            disabled: { icon: '⏸',  label: 'Disabled'     }
        }[statusKind];
        const snapCount = this.snapshotsByLoc[l.idx]?.snapshots?.length ?? 0;
        const expanded = this.expandedLocs.has(l.idx);
        const size = this.formatBytes(parseInt(l.total_size_bytes) || 0);
        return `
            <div class="task-item backup-location-row" data-loc="${l.idx}">
                <div class="task-header backup-location-header" data-action="toggle-location" data-loc="${l.idx}" aria-expanded="${expanded ? 'true' : 'false'}">
                    <div class="task-info backup-location-row-info">
                        <span class="backup-location-row-type-icon" data-type="${this.escape(l.type)}">${this.typeIcon(l.type)}</span>
                        <span class="backup-location-row-name">${this.escape(l.name)}</span>
                        <span class="backup-repo-type-pill">${this.escape(l.type)}</span>
                        <span class="backup-engine-pill" data-engine="${this.escape(l.engine || 'restic')}">${this.escape(this.engineDisplayName(l.engine))}</span>
                        ${l.append_only ? '<span class="backup-pill-mini">append-only</span>' : ''}
                        <span class="task-status backup-loc-status status-${statusKind}">${statusMeta.icon} ${statusMeta.label}</span>
                        <span class="backup-location-row-sep">·</span>
                        <span class="backup-location-row-stat">${snapCount} backup${snapCount === 1 ? '' : 's'}</span>
                        <span class="backup-location-row-sep">·</span>
                        <span class="backup-location-row-stat">${size}</span>
                    </div>
                    <span class="backup-toggle backup-loc-enable-toggle" data-action="toggle-location-enabled" data-loc="${l.idx}" title="${l.enabled ? 'Enabled — click to disable this location' : 'Disabled — click to enable this location'}">
                        <input type="checkbox" ${l.enabled ? 'checked' : ''} aria-label="Enable this location">
                        <span class="backup-toggle-slider"></span>
                    </span>
                    <svg class="backup-location-chevron" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <polyline points="6 9 12 15 18 9"></polyline>
                    </svg>
                </div>
                <div class="task-details backup-location-details ${expanded ? 'show' : ''}" data-loc="${l.idx}">
                    ${expanded ? this.renderLocationDetailsBody(l) : ''}
                </div>
            </div>
        `;
    },
    typeIcon(type) {
        const local = `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
            <path d="M3 5v6c0 1.66 4.03 3 9 3s9-1.34 9-3V5"></path>
            <path d="M3 11v6c0 1.66 4.03 3 9 3s9-1.34 9-3v-6"></path>
        </svg>`;
        const cloud = `<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"></path>
        </svg>`;
        return type === 'local' ? local : cloud;
    },
    renderLocationDetailsBody(l) {
        const idx = l.idx;
        const groups = this.locFieldGroups(idx, l.type);
        const retentionValues = {
            last:    l.custom_retention ? (l.keep_last    || '') : '',
            daily:   l.custom_retention ? (l.keep_daily   || '') : '',
            weekly:  l.custom_retention ? (l.keep_weekly  || '') : '',
            monthly: l.custom_retention ? (l.keep_monthly || '') : '',
            yearly:  l.custom_retention ? (l.keep_yearly  || '') : ''
        };

        // Reuse the app-detail tab design (.tabs-wrapper/.tab-button/.tab-panel
        // from style.css) so the Locations editor matches the rest of the UI.
        const tab = (id, emoji, label) => `
            <button type="button" class="tab-button${id === 'connection' ? ' active' : ''}" data-action="loc-tab" data-loc="${idx}" data-tab="${id}" role="tab" aria-selected="${id === 'connection'}">
                <span class="tab-emoji">${emoji}</span>
                <span class="tab-name">${label}</span>
            </button>`;

        return `
            <div class="config-category backup-location-config" data-section="location-${idx}">
                <div class="tabs-wrapper">
                    <div class="tabs-list" role="tablist">
                        ${tab('connection', '🔗', 'Connection')}
                        ${tab('retention', '♻️', 'Retention')}
                        ${tab('advanced', '⚙️', 'Advanced')}
                    </div>
                    <div class="tabs-content">
                        <div class="tab-panel active" data-tab-panel="connection" data-loc="${idx}">
                            <div class="backup-location-connection-fields" id="backup-location-${idx}-connection">
                                ${this.renderConnectionInner(idx, l.type, l, groups.connection)}
                            </div>
                        </div>
                        <div class="tab-panel" data-tab-panel="retention" data-loc="${idx}">
                            <div id="backup-location-${idx}-retention">
                                ${this.formRetention(`CFG_BACKUP_LOC_${idx}_`, retentionValues, true)}
                            </div>
                        </div>
                        <div class="tab-panel" data-tab-panel="advanced" data-loc="${idx}">
                            <div id="backup-location-${idx}-advanced">
                                ${this.renderLocFields(idx, groups.advanced, l)}
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="backup-location-actions">
                <button class="backup-primary-btn" data-action="save-location" data-loc="${idx}">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
                        <polyline points="17 21 17 13 7 13 7 21"></polyline>
                        <polyline points="7 3 7 8 15 8"></polyline>
                    </svg>
                    Save changes
                </button>
                <button class="backup-danger-btn" data-action="delete-location" data-loc="${idx}">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <polyline points="3 6 5 6 21 6"></polyline>
                        <path d="m19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
                        <line x1="10" y1="11" x2="10" y2="17"></line>
                        <line x1="14" y1="11" x2="14" y2="17"></line>
                    </svg>
                    Delete location
                </button>
            </div>
        `;
    },
    toggleLocationExpand(idx) {
        const row = document.querySelector(`.backup-location-row[data-loc="${idx}"]`);
        if (!row) return;
        const details = row.querySelector('.task-details');
        const header = row.querySelector('.task-header');
        if (!details) return;

        const willOpen = !this.expandedLocs.has(idx);
        if (willOpen) {
            this.expandedLocs.add(idx);
            const loc = (this.locations?.locations || []).find(l => l.idx === idx);
            if (loc) {
                details.innerHTML = this.renderLocationDetailsBody(loc);
                this.tagFieldsForSave(details);
                this.filterEngineSelect(details, loc.type, loc.engine);
                this.applySshAuthVisibility(details);
                this.applyPathModeVisibility(details);
            }
            this.enhanceEngineDetailsButton();
            details.classList.add('show');
            row.classList.add('expanded');
            if (header) header.setAttribute('aria-expanded', 'true');
        } else {
            this.expandedLocs.delete(idx);
            details.classList.remove('show');
            row.classList.remove('expanded');
            if (header) header.setAttribute('aria-expanded', 'false');
        }
    },
    refreshInlineTypeFields(idx, type) {
        const loc = (this.locations?.locations || []).find(l => l.idx === idx) || {};
        const groups = this.locFieldGroups(idx, type);

        const conn = document.getElementById(`backup-location-${idx}-connection`);
        if (conn) {
            conn.innerHTML = this.renderConnectionInner(idx, type, { ...loc, type }, groups.connection);
            this.tagFieldsForSave(conn);
        }

        // The Advanced tab's fields are type-dependent too (URI override only
        // applies to some types), so rebuild it alongside the Connection tab.
        const adv = document.getElementById(`backup-location-${idx}-advanced`);
        if (adv) {
            adv.innerHTML = this.renderLocFields(idx, groups.advanced, { ...loc, type });
            this.tagFieldsForSave(adv);
        }

        // Re-apply dynamic behaviors across the whole details scope: the engine
        // select lives in the Advanced tab while SSH-auth / path-mode live in
        // Connection, so target the shared parent rather than one panel.
        const scope = (conn || adv)?.closest('.task-details');
        if (scope) {
            this.filterEngineSelect(scope, type, loc.engine);
            this.applySshAuthVisibility(scope);
            this.applyPathModeVisibility(scope);
        }
        this.enhanceEngineDetailsButton();
    },
    applySshAuthVisibility(scope) {
        const authSelect = scope.querySelector('select[name$="_SSH_AUTH"]');
        if (!authSelect) return;
        const isPassword = authSelect.value === 'password';
        const passInput = scope.querySelector('input[name$="_SSH_PASS"]');
        const passGroup = passInput?.closest('.field-group') || passInput?.closest('.password-mode-wrapper')?.parentElement;
        if (passGroup) passGroup.style.display = isPassword ? '' : 'none';
        // SSH key card is the counterpart: shown for key auth, hidden for password.
        const keyCard = scope.querySelector('.backup-ssh-key-card');
        if (keyCard) keyCard.style.display = isPassword ? 'none' : '';
    },
    applyPathModeVisibility(scope) {
        const modeSelect = scope.querySelector('select[name$="_PATH_MODE"]');
        if (!modeSelect) return;
        const pathInput = scope.querySelector('input[name$="_PATH"]:not([name$="_SSH_PATH"])');
        const pathGroup = pathInput?.closest('.field-group') || pathInput?.parentElement;
        if (!pathGroup) return;
        pathGroup.style.display = modeSelect.value === 'custom' ? '' : 'none';
    },
    filterEngineSelect(scope, type, preferred) {
        const select = scope.querySelector('select[name$="_ENGINE"]');
        if (!select) return;
        const compatible = this.enginesForType(type);
        if (!compatible.length) return;

        // Float the system-default engine (CFG_BACKUP_ENGINE) to the top and
        // tag it "(default)" so it's the obvious pick for new locations.
        const defaultId = (window.systemConfigs?.CFG_BACKUP_ENGINE || 'restic').trim();
        const rank = e => (e.id === defaultId ? 0 : 1);
        const ordered = [...compatible].sort((a, b) => rank(a) - rank(b));

        const want = ordered.find(e => e.id === preferred)?.id || ordered[0].id;
        select.innerHTML = ordered
            .map(e => {
                const label = (e.name || e.id) + (e.id === defaultId ? ' (default)' : '');
                return `<option value="${this.escape(e.id)}" ${e.id === want ? 'selected' : ''}>${this.escape(label)}</option>`;
            })
            .join('');
        select.value = want;
    },
    async saveInlineLocation(idx) {
        await this.saveSection(`location-${idx}`);
    },
    deleteInlineLocation(idx) {
        const loc = (this.locations?.locations || []).find(l => l.idx === idx);
        const name = loc?.name || `Location ${idx}`;
        const modal = document.getElementById('backup-delete-location-modal');
        const body  = document.getElementById('backup-delete-location-modal-body');
        if (!modal || !body) return;
        body.innerHTML = `
            <p>Delete location <strong>${this.escape(name)}</strong>?</p>
            <p class="backup-card-hint">Backup data already stored at this location is not deleted — only LibrePortal's reference to it. The password file on disk also stays in place.</p>
        `;
        modal.dataset.locIdx = String(idx);
        modal.classList.add('open');
    },
    async confirmDeleteLocation() {
        const modal = document.getElementById('backup-delete-location-modal');
        if (!modal) return;
        const idx = parseInt(modal.dataset.locIdx, 10);
        this.closeAllModals();
        this.expandedLocs.delete(idx);
        await this.runTask(`libreportal backup location remove ${idx}`, 'backup', null);
        setTimeout(() => this.reloadAfterSave(), 2000);
    },
    async setLocationEnabled(idx, enabled) {
        const encoded = `CFG_BACKUP_LOC_${idx}_ENABLED=${enabled ? 'true' : 'false'}`;
        try {
            if (!window.tasksManager?.router) throw new Error('Task system not available');
            await window.tasksManager.router.routeAction('config_update', {
                changes: `'${encoded.replace(/'/g, "'\\''")}'`
            });
            this.notify(`${enabled ? 'Enabling' : 'Disabling'} this location…`, 'success');
            setTimeout(() => this.reloadAfterSave(), 2500);
        } catch (err) {
            this.notify(`Save failed: ${err.message || err}`, 'error');
        }
    },
});
