// ══ Deploy client — local & remote file browsers ══════════════════

function fileIcon(entry) {
    if (entry.type === 'dir') return '<i class="bi bi-folder-fill fi fi-dir"></i>';
    if (entry.type === 'link') return '<i class="bi bi-box-arrow-up-right fi fi-file"></i>';
    const ext = (entry.name.split('.').pop() || '').toLowerCase();
    const map = { php: 'bi-filetype-php', js: 'bi-filetype-js', css: 'bi-filetype-css', html: 'bi-filetype-html',
        json: 'bi-filetype-json', sql: 'bi-filetype-sql', png: 'bi-file-image', jpg: 'bi-file-image', jpeg: 'bi-file-image',
        svg: 'bi-filetype-svg', gif: 'bi-file-image', md: 'bi-filetype-md', txt: 'bi-filetype-txt' };
    return `<i class="bi ${map[ext] || 'bi-file-earmark'} fi fi-file"></i>`;
}

function renderCrumbs(container, rootLabel, path, onNav) {
    const el = $(container);
    const parts = path.split('/').filter(Boolean);
    let acc = '';
    let html = `<span class="crumb" data-p="">${esc(rootLabel)}</span>`;
    parts.forEach((seg) => {
        acc += '/' + seg;
        html += `<span class="sep">/</span><span class="crumb" data-p="${esc(acc)}">${esc(seg)}</span>`;
    });
    el.innerHTML = html;
    el.querySelectorAll('.crumb').forEach((c) => c.addEventListener('click', () => onNav(c.dataset.p)));
}

function renderFileList(container, entries, opts) {
    const el = $(container);
    if (!entries.length) { el.innerHTML = '<div class="dock-hint">This folder is empty.</div>'; return; }
    el.innerHTML = entries.map((e, i) => {
        const isDir = e.type === 'dir';
        const meta = e.mtime ? new Date(e.mtime * 1000).toLocaleDateString() : '';
        const size = isDir ? '' : humanBytes(e.size);
        const actions = opts.actions ? `<div class="factions">${opts.actions(e)}</div>` : '';
        return `<div class="frow ${isDir ? 'is-dir' : ''}" data-i="${i}">
            <div class="fname">${fileIcon(e)}<span class="flabel">${esc(e.name)}</span></div>
            <div class="fsize">${size}</div>
            <div class="fmeta">${meta}</div>${actions}</div>`;
    }).join('');
    el.querySelectorAll('.frow.is-dir .flabel').forEach((lbl) => {
        lbl.addEventListener('click', () => {
            const i = +lbl.closest('.frow').dataset.i;
            opts.onDir(entries[i]);
        });
    });
    if (opts.bind) opts.bind(el, entries);
}

// ── Local pane ─────────────────────────────────────────────────────
async function localGo(path) {
    $('localList').innerHTML = '<div class="list-loading"><i class="bi bi-arrow-repeat"></i> Reading…</div>';
    const r = await api('local/list', { path });
    if (!r.success) { $('localList').innerHTML = `<div class="pane-empty"><i class="bi bi-hammer"></i><p>Nothing to upload</p><span>${esc(r.message)}</span></div>`; return; }
    D.localPath = r.path || '';
    renderCrumbs('localCrumbs', 'output', D.localPath, localGo);
    renderFileList('localList', r.entries, { onDir: (e) => localGo((D.localPath ? D.localPath + '/' : '') + e.name) });
}

// ── Remote pane ────────────────────────────────────────────────────
async function remoteGo(path) {
    if (!D.activeId) { toast('Select and connect to a server first.', 'warning'); return; }
    $('remoteList').innerHTML = '<div class="list-loading"><i class="bi bi-arrow-repeat"></i> Listing remote directory…</div>';
    setConnected('busy', 'Listing…');
    const r = await api('remote/list', { connectionId: D.activeId, path });
    if (!r.success) {
        setConnected(false, 'Failed');
        $('remoteList').innerHTML = `<div class="pane-empty"><i class="bi bi-exclamation-octagon text-danger"></i>
            <p>Could not list directory</p><span>${esc(r.message)}</span>
            <div class="pe-actions">
                <button class="btn btn-sm btn-outline-info" id="peDiag"><i class="bi bi-clipboard-pulse me-1"></i>Run diagnostics</button>
                <button class="btn btn-sm btn-outline-secondary" id="peRetry"><i class="bi bi-arrow-clockwise me-1"></i>Retry</button>
            </div></div>`;
        const d = document.getElementById('peDiag'), rt = document.getElementById('peRetry');
        if (d) d.addEventListener('click', () => { switchTab('diag'); testConnection({ connectionId: D.activeId }, null, 'diagBody', $('btnTestConn')); });
        if (rt) rt.addEventListener('click', () => remoteGo(path));
        toast(r.message, 'danger');
        return;
    }
    D.remotePath = r.path || '/';
    setConnected(true, 'Connected · ' + D.remotePath);
    $('remoteHostLabel').textContent = (activeConn()?.host || '') + D.remotePath;
    renderCrumbs('remoteCrumbs', '/', D.remotePath, remoteGo);
    renderFileList('remoteList', r.entries, {
        onDir: (e) => remoteGo((D.remotePath === '/' ? '' : D.remotePath) + '/' + e.name),
        actions: (e) => `
            <button class="ren" title="Rename" data-n="${esc(e.name)}"><i class="bi bi-pencil"></i></button>
            <button class="del" title="Delete" data-n="${esc(e.name)}" data-d="${e.type === 'dir' ? 1 : 0}"><i class="bi bi-trash"></i></button>`,
        bind: (el, entries) => {
            el.querySelectorAll('.factions .ren').forEach((b) => b.addEventListener('click', (ev) => { ev.stopPropagation(); remoteRename(b.dataset.n); }));
            el.querySelectorAll('.factions .del').forEach((b) => b.addEventListener('click', (ev) => { ev.stopPropagation(); remoteDelete(b.dataset.n, b.dataset.d === '1'); }));
        },
    });
    renderStats();
}

function remoteChild(name) { return (D.remotePath === '/' ? '' : D.remotePath) + '/' + name; }

async function remoteMkdir() {
    if (!D.connected) return;
    const { value: name } = await Swal.fire({ title: 'New folder', input: 'text', inputPlaceholder: 'folder name',
        showCancelButton: true, confirmButtonText: 'Create' });
    if (!name) return;
    const r = await api('remote/mkdir', { connectionId: D.activeId, path: D.remotePath, name });
    toast(r.message, r.success ? 'success' : 'danger');
    if (r.success) remoteGo(D.remotePath);
}

async function remoteRename(name) {
    const { value: to } = await Swal.fire({ title: 'Rename', input: 'text', inputValue: name,
        showCancelButton: true, confirmButtonText: 'Rename' });
    if (!to || to === name) return;
    const r = await api('remote/rename', { connectionId: D.activeId, from: remoteChild(name), to });
    toast(r.message, r.success ? 'success' : 'danger');
    if (r.success) remoteGo(D.remotePath);
}

async function remoteDelete(name, isDir) {
    const c = await Swal.fire({ title: `Delete ${isDir ? 'folder' : 'file'}?`, text: name, icon: 'warning',
        showCancelButton: true, confirmButtonText: 'Delete', confirmButtonColor: '#dc2626' });
    if (!c.isConfirmed) return;
    const r = await api('remote/delete', { connectionId: D.activeId, path: remoteChild(name), isDir });
    toast(r.message, r.success ? 'success' : 'danger');
    if (r.success) remoteGo(D.remotePath);
}

async function remoteSetTarget() {
    if (!D.connected) return;
    const conn = activeConn();
    if (!conn) return;
    const c = await Swal.fire({ title: 'Set upload target', html: `Use <code>${esc(D.remotePath)}</code> as the remote path for <strong>${esc(conn.name)}</strong>?`,
        icon: 'question', showCancelButton: true, confirmButtonText: 'Set target' });
    if (!c.isConfirmed) return;
    const r = await api('connection/save', { profile: { id: conn.id, name: conn.name, protocol: conn.protocol, host: conn.host,
        port: conn.port, user: conn.user, remotePath: D.remotePath, siteUrl: conn.siteUrl, passive: conn.passive, privateKeyFile: conn.privateKeyFile } });
    if (r.success) { D.connections = r.connections; renderConnSelect(); toast('Upload target set to ' + D.remotePath, 'success'); }
    else toast(r.message, 'danger');
}

function remoteUp() {
    if (D.remotePath === '/' || !D.remotePath) return;
    const parent = D.remotePath.replace(/\/[^/]+$/, '') || '/';
    remoteGo(parent);
}
