// Patch script for FM JS overhaul – runs with node _patch_fm.js
const fs = require('fs');
const path = require('path');

const filePath = path.join(__dirname, 'public/js/app.js');
let src = fs.readFileSync(filePath, 'utf8');

// Normalise to LF for matching, then write back with CRLF
const srcLF = src.replace(/\r\n/g, '\n');

// ─── PATCH 1: Replace renderFMFileList (old signature with items, currentPath) ─
const OLD_RENDER = `  function renderFMFileList(items, currentPath) {
    const tbody = document.getElementById('fm-tbody');
    tbody.innerHTML = items.length
      ? items.map(item => \`
          <tr>
            <td>
              \${item.isDir
                ? \`<span style="cursor:pointer;color:var(--blue)" onclick="App.fmNavigate('\${esc(item.path)}')">
                     <i class="fa-solid fa-folder" style="color:var(--yellow)"></i> \${esc(item.name)}
                   </span>\`
                : \`<i class="fa-regular fa-file" style="color:var(--text-3)"></i> \${esc(item.name)}\`}
            </td>
            <td style="font-size:12px">\${fmtSize(item.size)}</td>
            <td style="font-size:12px;white-space:nowrap">\${fmtDate(item.modified)}</td>
            <td class="actions-cell">
              \${!item.isDir ? \`<button class="btn btn-ghost btn-xs" onclick="App.fmEditFile('\${esc(item.path)}','\${esc(item.name)}')"><i class="fa-solid fa-pen"></i></button>\` : ''}
              <button class="btn btn-danger btn-xs" onclick="App.fmDelete('\${esc(item.path)}','\${esc(item.name)}')">
                <i class="fa-solid fa-trash"></i>
              </button>
            </td>
          </tr>\`).join('')
      : '<tr><td colspan="4" class="empty-row">This folder is empty.</td></tr>';
  }`;

const NEW_RENDER = `  function renderFMFileList() {
    // Reset select-all UI state
    const chkAll = document.getElementById('fm-chk-all');
    if (chkAll) { chkAll.checked = false; chkAll.indeterminate = false; }
    document.getElementById('fm-btn-delete-sel').style.display = 'none';
    document.getElementById('fm-btn-rename-sel').style.display = 'none';

    // Search filter
    const q = (document.getElementById('fm-search').value || '').toLowerCase().trim();
    let items = q ? fmItems.filter(i => i.name.toLowerCase().includes(q)) : [...fmItems];

    // Sort: dirs first, then by selected column
    const { col, dir } = fmSort;
    items.sort((a, b) => {
      if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
      let cmp = 0;
      if (col === 'name')     cmp = a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
      if (col === 'size')     cmp = (a.size || 0) - (b.size || 0);
      if (col === 'modified') cmp = new Date(a.modified) - new Date(b.modified);
      return dir === 'asc' ? cmp : -cmp;
    });

    // Update sort arrows in headers
    ['name', 'size', 'modified'].forEach(c => {
      const el = document.getElementById('si-' + c);
      if (el) el.textContent = c === col ? (dir === 'asc' ? '▲' : '▼') : '';
    });

    const tbody = document.getElementById('fm-tbody');
    tbody.innerHTML = items.length
      ? items.map(item => \`
          <tr class="fm-row" data-path="\${esc(item.path)}" data-name="\${esc(item.name)}" data-isdir="\${item.isDir}">
            <td class="fm-chk-col" onclick="event.stopPropagation()">
              <input type="checkbox" class="fm-row-chk" data-path="\${esc(item.path)}" data-name="\${esc(item.name)}" />
            </td>
            <td class="fm-name-cell">
              \${getFileIcon(item.name, item.isDir)}
              \${item.isDir
                ? \`<span class="fm-name fm-dir-link" onclick="App.fmNavigate('\${esc(item.path)}')">\${esc(item.name)}</span>\`
                : \`<span class="fm-name">\${esc(item.name)}</span>\`}
            </td>
            <td class="fm-meta-cell">\${item.isDir ? '<span style="color:var(--text-3)">—</span>' : fmtSize(item.size)}</td>
            <td class="fm-meta-cell" style="white-space:nowrap">\${fmtDate(item.modified)}</td>
            <td class="actions-cell">
              \${item.isDir
                ? \`<button class="btn btn-ghost btn-xs" onclick="App.fmNavigate('\${esc(item.path)}')" title="Open"><i class="fa-solid fa-folder-open"></i></button>\`
                : \`<button class="btn btn-ghost btn-xs" onclick="App.fmEditFile('\${esc(item.path)}','\${esc(item.name)}')" title="Edit"><i class="fa-solid fa-pen"></i></button>
                   <button class="btn btn-ghost btn-xs" onclick="App.fmDownload('\${esc(item.path)}','\${esc(item.name)}')" title="Download"><i class="fa-solid fa-download"></i></button>\`}
              <button class="btn btn-ghost btn-xs" onclick="App.fmRename('\${esc(item.path)}','\${esc(item.name)}')" title="Rename (F2)"><i class="fa-solid fa-i-cursor"></i></button>
              <button class="btn btn-danger btn-xs" onclick="App.fmDelete('\${esc(item.path)}','\${esc(item.name)}')" title="Delete (Del)"><i class="fa-solid fa-trash"></i></button>
            </td>
          </tr>\`).join('')
      : \`<tr><td colspan="5" class="empty-row">\${q ? 'No files match "' + esc(q) + '".' : 'This folder is empty.'}</td></tr>\`;

    updateFMStatusBar(items, q);
  }`;

// ─── PATCH 2: Replace initFileManager ────────────────────────────────────────
const OLD_INIT = `  function initFileManager() {
    document.getElementById('fm-site-select').addEventListener('change', async (e) => {
      fm.siteId     = e.target.value;
      fm.currentPath = '/';
      fm.sitePath   = '';
      if (!fm.siteId) {
        document.getElementById('fm-tree').innerHTML = '<div class="fm-placeholder">Select a site to browse files</div>';
        document.getElementById('fm-tbody').innerHTML = '<tr><td colspan="4" class="empty-row">—</td></tr>';
        return;
      }
      // Find deploy path
      const site = managedSites.find(s => s.id === fm.siteId);
      if (site) fm.sitePath = site.deployPath;
      enableFMButtons(true);
      await fmBrowse('/');
      await fmLoadTree('/');
    });

    document.getElementById('fm-btn-refresh').addEventListener('click', () => fmBrowse(fm.currentPath));

    document.getElementById('fm-btn-mkdir').addEventListener('click', async () => {
      const name = prompt('New folder name:');
      if (!name) return;
      const cleanName = name.replace(/[\/\\\\:*?"<>|]/g, '_');
      const newPath = (fm.currentPath === '/' ? '' : fm.currentPath) + '/' + cleanName;
      try {
        await POST(\`/files/\${fm.siteId}/mkdir\`, { path: newPath });
        toast('success', 'Folder created', cleanName);
        fmBrowse(fm.currentPath);
      } catch (err) { toast('error', 'Error', err.message); }
    });

    // Upload – delegated file list handler
    document.getElementById('fm-btn-upload').addEventListener('click', () => {
      document.getElementById('fm-upload-input').click();
    });

    document.getElementById('fm-upload-input').addEventListener('change', async (e) => {
      const files = Array.from(e.target.files);
      e.target.value = '';
      if (!files.length) return;
      await fmUploadFiles(files);
    });

    document.getElementById('fm-uq-close').addEventListener('click', () => {
      document.getElementById('fm-upload-queue').style.display = 'none';
    });

    // Select-all + delete-selected
    document.getElementById('fm-list').addEventListener('change', (e) => {
      if (e.target.id === 'fm-chk-all') {
        document.querySelectorAll('.fm-row-chk').forEach(c => { c.checked = e.target.checked; });
      }
      updateFMSelection();
    });

    document.getElementById('fm-btn-delete-sel').addEventListener('click', fmDeleteSelected);

    document.getElementById('fm-btn-save').addEventListener('click', async () => {
      if (!fm.editingFile) return;
      const content = document.getElementById('fm-editor').value;
      try {
        await PUT(\`/files/\${fm.siteId}/write\`, { path: fm.editingFile, content });
        toast('success', 'File saved', fm.editingFile);
        document.getElementById('fm-editor-card').style.display = 'none';
        fmBrowse(fm.currentPath);
      } catch (err) { toast('error', 'Save failed', err.message); }
    });

    document.getElementById('fm-btn-close-editor').addEventListener('click', () => {
      document.getElementById('fm-editor-card').style.display = 'none';
    });
  }`;

const NEW_INIT = `  function initFileManager() {
    document.getElementById('fm-site-select').addEventListener('change', async (e) => {
      fm.siteId      = e.target.value;
      fm.currentPath = '/';
      fm.sitePath    = '';
      document.getElementById('fm-search').value = '';
      fmItems = [];
      if (!fm.siteId) {
        document.getElementById('fm-tree').innerHTML = '<div class="fm-placeholder">Select a site to browse files</div>';
        document.getElementById('fm-tbody').innerHTML = '<tr><td colspan="5" class="empty-row">—</td></tr>';
        document.getElementById('fm-status-text').textContent = 'No site selected';
        enableFMButtons(false);
        return;
      }
      const site = managedSites.find(s => s.id === fm.siteId);
      if (site) fm.sitePath = site.deployPath;
      enableFMButtons(true);
      await fmBrowse('/');
      await fmLoadTree('/');
    });

    document.getElementById('fm-btn-refresh').addEventListener('click', () => fmBrowse(fm.currentPath));
    document.getElementById('fm-btn-mkdir').addEventListener('click', fmMakeDir);
    document.getElementById('fm-btn-upload').addEventListener('click', () => document.getElementById('fm-upload-input').click());
    document.getElementById('fm-upload-input').addEventListener('change', async (e) => {
      const files = Array.from(e.target.files);
      e.target.value = '';
      if (files.length) await fmUploadFiles(files);
    });
    document.getElementById('fm-uq-close').addEventListener('click', () => {
      document.getElementById('fm-upload-queue').style.display = 'none';
    });
    document.getElementById('fm-btn-delete-sel').addEventListener('click', fmDeleteSelected);
    document.getElementById('fm-btn-rename-sel').addEventListener('click', () => {
      const sel = Array.from(document.querySelectorAll('.fm-row-chk:checked'));
      if (sel.length === 1) fmRename(sel[0].dataset.path, sel[0].dataset.name);
    });
    document.getElementById('fm-search').addEventListener('input', renderFMFileList);

    // Click delegation: sort headers + row-select toggle
    document.getElementById('fm-list').addEventListener('click', (e) => {
      const th = e.target.closest('.fm-sort-col');
      if (th) { fmSetSort(th.dataset.sort); return; }
      if (!e.target.closest('button') && !e.target.closest('.fm-chk-col')) {
        const row = e.target.closest('.fm-row');
        if (row) {
          const chk = row.querySelector('.fm-row-chk');
          if (chk) { chk.checked = !chk.checked; updateFMSelection(); }
        }
      }
    });

    // Checkbox change (select-all or individual row)
    document.getElementById('fm-list').addEventListener('change', (e) => {
      if (e.target.id === 'fm-chk-all') {
        document.querySelectorAll('.fm-row-chk').forEach(c => { c.checked = e.target.checked; });
      }
      updateFMSelection();
    });

    // Right-click context menu
    document.getElementById('fm-list').addEventListener('contextmenu', (e) => {
      const row = e.target.closest('.fm-row');
      if (!row || !fm.siteId) return;
      e.preventDefault();
      fmCtxItem = { path: row.dataset.path, name: row.dataset.name, isDir: row.dataset.isdir === 'true' };
      const chk = row.querySelector('.fm-row-chk');
      if (chk && !chk.checked) { chk.checked = true; updateFMSelection(); }
      showCtxMenu(e.clientX, e.clientY, fmCtxItem.isDir);
    });
    document.addEventListener('click', () => hideCtxMenu());
    document.getElementById('ctx-open').addEventListener('click',    () => { if (fmCtxItem && fmCtxItem.isDir)  fmNavigate(fmCtxItem.path); });
    document.getElementById('ctx-edit').addEventListener('click',    () => { if (fmCtxItem && !fmCtxItem.isDir) fmEditFile(fmCtxItem.path, fmCtxItem.name); });
    document.getElementById('ctx-download').addEventListener('click',() => { if (fmCtxItem && !fmCtxItem.isDir) fmDownload(fmCtxItem.path, fmCtxItem.name); });
    document.getElementById('ctx-rename').addEventListener('click',  () => { if (fmCtxItem) fmRename(fmCtxItem.path, fmCtxItem.name); });
    document.getElementById('ctx-delete').addEventListener('click',  () => { if (fmCtxItem) fmDelete(fmCtxItem.path, fmCtxItem.name); });

    // Drag-and-drop upload onto the card
    const fmCard = document.getElementById('fm-card');
    const dropOv = document.getElementById('fm-drop-overlay');
    let dragN = 0;
    fmCard.addEventListener('dragenter', (e) => { if (!fm.siteId) return; e.preventDefault(); if (++dragN === 1) dropOv.style.display = 'flex'; });
    fmCard.addEventListener('dragleave', () => { if (--dragN <= 0) { dragN = 0; dropOv.style.display = 'none'; } });
    fmCard.addEventListener('dragover',  (e) => e.preventDefault());
    fmCard.addEventListener('drop', async (e) => {
      e.preventDefault(); dragN = 0; dropOv.style.display = 'none';
      const files = Array.from(e.dataTransfer.files);
      if (files.length && fm.siteId) await fmUploadFiles(files);
    });

    // Keyboard shortcuts (active only when on Files page)
    document.addEventListener('keydown', (e) => {
      if (currentPage !== 'files' || !fm.siteId) return;
      const act = document.activeElement;
      if (act && (act.tagName === 'INPUT' || act.tagName === 'TEXTAREA')) return;
      if (e.ctrlKey && e.key === 'a') { e.preventDefault(); fmSelectAll(); }
      if (e.key === 'Escape') fmClearSelection();
      if (e.key === 'Delete') { const s = document.querySelectorAll('.fm-row-chk:checked'); if (s.length) { e.preventDefault(); fmDeleteSelected(); } }
      if (e.key === 'F2') { const s = Array.from(document.querySelectorAll('.fm-row-chk:checked')); if (s.length === 1) { e.preventDefault(); fmRename(s[0].dataset.path, s[0].dataset.name); } }
    });

    document.getElementById('fm-btn-save').addEventListener('click', async () => {
      if (!fm.editingFile) return;
      const content = document.getElementById('fm-editor').value;
      try {
        await PUT(\`/files/\${fm.siteId}/write\`, { path: fm.editingFile, content });
        toast('success', 'File saved', fm.editingFile);
        document.getElementById('fm-editor-card').style.display = 'none';
        fmBrowse(fm.currentPath);
      } catch (err) { toast('error', 'Save failed', err.message); }
    });
    document.getElementById('fm-btn-close-editor').addEventListener('click', () => {
      document.getElementById('fm-editor-card').style.display = 'none';
    });
  }`;

// ─── PATCH 3: Replace updateFMSelection ──────────────────────────────────────
const OLD_UPDATE_SEL_START = `  // Update selection counter + toggle delete-sel button`;
const OLD_UPDATE_SEL_END = `    const btn = document.getElementById('fm-btn-delete-sel');
    btn.style.display = checked > 0 ? 'inline-flex' : 'none';
    btn.textContent   = checked > 0 ? \`Delete (\${checked})\` : '';
  }`;

const NEW_UPDATE_SEL = `  // Update selection state + toolbar buttons
  function updateFMSelection() {
    const all     = document.querySelectorAll('.fm-row-chk');
    const checked = document.querySelectorAll('.fm-row-chk:checked').length;
    const chkAll  = document.getElementById('fm-chk-all');
    if (chkAll) {
      chkAll.checked       = all.length > 0 && checked === all.length;
      chkAll.indeterminate = checked > 0 && checked < all.length;
    }
    const delBtn = document.getElementById('fm-btn-delete-sel');
    delBtn.style.display = checked > 0 ? 'inline-flex' : 'none';
    if (checked > 0) delBtn.textContent = \`Delete (\${checked})\`;
    const renBtn = document.getElementById('fm-btn-rename-sel');
    renBtn.style.display = checked === 1 ? 'inline-flex' : 'none';
    // Update status bar selection count
    const selEl = document.getElementById('fm-status-sel');
    const selN  = document.getElementById('fm-status-sel-n');
    if (selEl) { selEl.style.display = checked > 0 ? 'inline' : 'none'; if (selN) selN.textContent = checked; }
  }`;

// ─── PATCH 4: Add new FM functions before the Activity anchor ─────────────────
const ACTIVITY_ANCHOR = `  // ─── Activity ─────────────────────────────────────────────`;

const NEW_FM_FUNCTIONS = `  function fmSetSort(col) {
    if (fmSort.col === col) fmSort.dir = fmSort.dir === 'asc' ? 'desc' : 'asc';
    else { fmSort.col = col; fmSort.dir = 'asc'; }
    renderFMFileList();
  }

  function fmSelectAll() {
    document.querySelectorAll('.fm-row-chk').forEach(c => { c.checked = true; });
    updateFMSelection();
  }

  function fmClearSelection() {
    document.querySelectorAll('.fm-row-chk').forEach(c => { c.checked = false; });
    const chkAll = document.getElementById('fm-chk-all');
    if (chkAll) { chkAll.checked = false; chkAll.indeterminate = false; }
    updateFMSelection();
  }

  function updateFMStatusBar(items, q) {
    const statusEl = document.getElementById('fm-status-text');
    if (!statusEl) return;
    const dirs  = items.filter(i => i.isDir).length;
    const files = items.length - dirs;
    let msg = '';
    if (q) msg = \`\${items.length} result\${items.length !== 1 ? 's' : ''} for "\${q}"\`;
    else msg = \`\${dirs} folder\${dirs !== 1 ? 's' : ''}, \${files} file\${files !== 1 ? 's' : ''}\`;
    statusEl.textContent = msg;
  }

  async function fmRename(filePath, oldName) {
    const newName = prompt('Rename to:', oldName);
    if (!newName || newName === oldName) return;
    const dirPart = filePath.substring(0, filePath.lastIndexOf('/') + 1);
    const newPath = dirPart + newName;
    try {
      await PUT(\`/files/\${fm.siteId}/rename\`, { from: filePath, to: newPath });
      toast('success', 'Renamed', \`\${oldName} → \${newName}\`);
      fmBrowse(fm.currentPath);
    } catch (err) { toast('error', 'Rename failed', err.message); }
  }

  function fmDownload(filePath, name) {
    const a = document.createElement('a');
    a.href     = \`/api/files/\${fm.siteId}/download?path=\${encodeURIComponent(filePath)}\`;
    a.download = name;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  }

  async function fmMakeDir() {
    const name = prompt('New folder name:');
    if (!name) return;
    const cleanName = name.replace(/[/\\\\:*?"<>|]/g, '_');
    const newPath = (fm.currentPath === '/' ? '' : fm.currentPath) + '/' + cleanName;
    try {
      await POST(\`/files/\${fm.siteId}/mkdir\`, { path: newPath });
      toast('success', 'Folder created', cleanName);
      fmBrowse(fm.currentPath);
    } catch (err) { toast('error', 'Error', err.message); }
  }

  function showCtxMenu(x, y, isDir) {
    const menu = document.getElementById('fm-ctx-menu');
    document.getElementById('ctx-open').style.display     = isDir  ? 'flex' : 'none';
    document.getElementById('ctx-edit').style.display     = !isDir ? 'flex' : 'none';
    document.getElementById('ctx-download').style.display = !isDir ? 'flex' : 'none';
    menu.style.left    = x + 'px';
    menu.style.top     = y + 'px';
    menu.style.display = 'block';
    // Keep menu within viewport
    const rect = menu.getBoundingClientRect();
    if (rect.right  > window.innerWidth)  menu.style.left = (x - rect.width)  + 'px';
    if (rect.bottom > window.innerHeight) menu.style.top  = (y - rect.height) + 'px';
  }

  function hideCtxMenu() {
    const menu = document.getElementById('fm-ctx-menu');
    if (menu) menu.style.display = 'none';
  }

  // ─── Activity ─────────────────────────────────────────────`;

// ─── PATCH 5: Update return object ───────────────────────────────────────────
const OLD_RETURN_FM = `    // File manager
    fmNavigate,
    fmEditFile,
    fmDelete,
    fmDeleteSelected,`;

const NEW_RETURN_FM = `    // File manager
    fmNavigate,
    fmEditFile,
    fmDelete,
    fmDeleteSelected,
    fmRename,
    fmDownload,`;

// ─── Apply patches ────────────────────────────────────────────────────────────
function patch(src, oldStr, newStr, label) {
  if (!src.includes(oldStr)) {
    console.error(`PATCH FAILED [${label}]: old string not found`);
    // Print first 80 chars of old string to help debug
    console.error('Looking for:', JSON.stringify(oldStr.slice(0, 120)));
    process.exitCode = 1;
    return src;
  }
  console.log(`PATCH OK    [${label}]`);
  return src.replace(oldStr, newStr);
}

let out = srcLF;
out = patch(out, OLD_RENDER,     NEW_RENDER,     'renderFMFileList');
out = patch(out, OLD_INIT,       NEW_INIT,        'initFileManager');

// updateFMSelection: find just the comment+function by looking for unique markers
const OLD_UPD_SEL_FULL = (() => {
  const start = out.indexOf('  // Update selection counter + toggle delete-sel button');
  const end   = out.indexOf('\n  }', out.indexOf('    btn.textContent   = checked > 0', start));
  return out.slice(start, end + '\n  }'.length);
})();
out = patch(out, OLD_UPD_SEL_FULL, NEW_UPDATE_SEL, 'updateFMSelection');

// Insert new FM functions + replace Activity anchor
out = patch(out, ACTIVITY_ANCHOR, NEW_FM_FUNCTIONS, 'newFunctions+Activity');
out = patch(out, OLD_RETURN_FM,   NEW_RETURN_FM,    'returnObject');

// Restore CRLF and write
const result = out.replace(/\n/g, '\r\n');
fs.writeFileSync(filePath, result, 'utf8');
console.log('\nDone. File written:', filePath);
