/* ============================================================
   PRESET EXPLORER -- File System Access API live folder/file browser
   for the presets/ directory, with add-file ("save as"-style copy-in)
   and new-folder actions. Chromium-only (Chrome/Edge/Opera) -- Firefox
   and Safari don't support showDirectoryPicker()/showOpenFilePicker().
   Pure progressive enhancement: Screen1 still renders the existing
   static, presets.js-driven read-only list regardless of browser, this
   component only ADDS live browsing/add/create on top where supported.
   ============================================================ */

const FSA_SUPPORTED = typeof window !== "undefined" && typeof window.showDirectoryPicker === "function";

const IDB_NAME = "vktr-preset-explorer";
const IDB_STORE = "handles";
const IDB_KEY = "presetsDirHandle";

function idbOpen() {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(IDB_NAME, 1);
    req.onupgradeneeded = () => { if (!req.result.objectStoreNames.contains(IDB_STORE)) req.result.createObjectStore(IDB_STORE); };
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}
async function idbGetHandle() {
  const db = await idbOpen();
  return new Promise((resolve, reject) => {
    const tx = db.transaction(IDB_STORE, "readonly");
    const req = tx.objectStore(IDB_STORE).get(IDB_KEY);
    req.onsuccess = () => resolve(req.result || null);
    req.onerror = () => reject(req.error);
  });
}
async function idbSetHandle(handle) {
  const db = await idbOpen();
  return new Promise((resolve, reject) => {
    const tx = db.transaction(IDB_STORE, "readwrite");
    tx.objectStore(IDB_STORE).put(handle, IDB_KEY);
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
  });
}

// Silent check first (queryPermission) -- only falls through to a visible
// prompt (requestPermission) if the silent check didn't already say yes,
// so a returning user on the same browser profile sees nothing unless the
// browser actually revoked access.
async function ensurePermission(handle) {
  if (!handle) return false;
  const opts = { mode: "readwrite" };
  try {
    if ((await handle.queryPermission(opts)) === "granted") return true;
    if ((await handle.requestPermission(opts)) === "granted") return true;
  } catch (e) { /* permission API unavailable or denied -- caller treats as not granted */ }
  return false;
}

async function getPersistedHandle() {
  try {
    const handle = await idbGetHandle();
    if (handle && (await ensurePermission(handle))) return handle;
  } catch (e) { /* IndexedDB unavailable (private mode, etc.) -- fall through to manual grant */ }
  return null;
}

async function pickPresetsFolder() {
  const handle = await window.showDirectoryPicker({ id: "vktr-presets", mode: "readwrite" });
  await idbSetHandle(handle);
  return handle;
}

async function listFolders(dirHandle) {
  const folders = [];
  for await (const [name, handle] of dirHandle.entries()) {
    if (handle.kind === "directory") {
      let count = 0;
      for await (const [fname, fhandle] of handle.entries()) {
        if (fhandle.kind === "file" && fname.toLowerCase().endsWith(".json")) count++;
      }
      folders.push({ name, handle, count });
    }
  }
  folders.sort((a, b) => a.name.localeCompare(b.name));
  return folders;
}

async function listFiles(folderHandle) {
  const files = [];
  for await (const [name, handle] of folderHandle.entries()) {
    if (handle.kind === "file" && name.toLowerCase().endsWith(".json")) files.push({ name, handle });
  }
  files.sort((a, b) => a.name.localeCompare(b.name));
  return files;
}

async function readJsonFile(fileHandle) {
  const file = await fileHandle.getFile();
  return JSON.parse(await file.text());
}

function PresetExplorer({ lang, loadPreset, onGrantedChange }) {
  const [rootHandle, setRootHandle] = useState(null);
  const [granted, setGranted] = useState(false);
  const [checkingGrant, setCheckingGrant] = useState(true);

  // Tell the parent (Screen1) whether the live Explorer is actually showing
  // real folder content right now, so it can hide the old static fallback
  // list instead of showing both at once -- this WAS showing both, which is
  // exactly the confusing duplicate-view problem being fixed here.
  useEffect(() => { if (onGrantedChange) onGrantedChange(granted); }, [granted]);
  const [folders, setFolders] = useState([]);
  const [activeFolder, setActiveFolder] = useState(null);
  const [files, setFiles] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const refreshFolders = async (handle) => {
    setLoading(true);
    setError(null);
    try {
      setFolders(await listFolders(handle));
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    if (!FSA_SUPPORTED) { setCheckingGrant(false); return; }
    (async () => {
      const handle = await getPersistedHandle();
      if (handle) {
        setRootHandle(handle);
        setGranted(true);
        await refreshFolders(handle);
      }
      setCheckingGrant(false);
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  if (!FSA_SUPPORTED) {
    return (
      <div className="empty-window-callout">
        {tr(lang,
          "Live preset browsing and adding your own presets needs Chrome, Edge, or another Chromium-based browser. The read-only list below still works here.",
          "Penjelajahan preset langsung dan menambahkan preset sendiri membutuhkan Chrome, Edge, atau browser berbasis Chromium lainnya. Daftar baca-saja di bawah masih berfungsi di sini.")}
      </div>
    );
  }

  if (checkingGrant) return null;

  const handleGrant = async () => {
    try {
      const handle = await pickPresetsFolder();
      setRootHandle(handle);
      setGranted(true);
      await refreshFolders(handle);
    } catch (e) {
      // AbortError = user closed the picker without choosing -- not an error
    }
  };

  if (!granted) {
    return (
      <div className="empty-window-callout" style={{ textAlign: "center" }}>
        <p style={{ marginBottom: 10 }}>
          {tr(lang,
            "Grant access to this project's presets/ folder once to browse, add, and organize preset files directly from here — this device remembers the folder afterward, you won't be asked again unless access is revoked.",
            "Berikan akses ke folder presets/ proyek ini sekali untuk menjelajah, menambah, dan mengatur file preset langsung dari sini — perangkat ini akan mengingat folder tersebut, Anda tidak akan ditanya lagi kecuali akses dicabut.")}
        </p>
        <button className="btn btn-accent" onClick={handleGrant}>
          {tr(lang, "📁 Choose presets/ folder", "📁 Pilih folder presets/")}
        </button>
      </div>
    );
  }

  const openFolder = async (folder) => {
    setLoading(true);
    setError(null);
    setActiveFolder(folder);
    try {
      setFiles(await listFiles(folder.handle));
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  };

  const backToFolders = () => {
    setActiveFolder(null);
    setFiles([]);
    refreshFolders(rootHandle);
  };

  const handleLoadFile = async (file) => {
    try {
      const data = await readJsonFile(file.handle);
      const proceed = window.confirm(tr(lang,
        `Load "${file.name}"? This replaces your current profile on this device. Export first if you want to keep it.`,
        `Muat "${file.name}"? Ini akan mengganti profil Anda saat ini di perangkat ini. Ekspor dulu jika ingin menyimpannya.`));
      if (proceed && loadPreset) loadPreset(data.state || data);
    } catch (e) {
      window.alert(tr(lang, "Couldn't read that file: ", "Tidak bisa membaca file itu: ") + e.message);
    }
  };

  const handleAddFileFromDisk = async () => {
    if (!activeFolder) return;
    try {
      const [picked] = await window.showOpenFilePicker({
        types: [{ description: "JSON", accept: { "application/json": [".json"] } }],
      });
      const file = await picked.getFile();
      const text = await file.text();
      JSON.parse(text); // sanity check before copying in -- reject non-JSON early
      const destHandle = await activeFolder.handle.getFileHandle(picked.name, { create: true });
      const writable = await destHandle.createWritable();
      await writable.write(text);
      await writable.close();
      await openFolder(activeFolder);
    } catch (e) {
      if (e.name !== "AbortError") window.alert(tr(lang, "Couldn't add that file: ", "Tidak bisa menambahkan file itu: ") + e.message);
    }
  };

  const handleNewFolder = async () => {
    if (!rootHandle) return;
    const name = window.prompt(tr(lang,
      "New folder name (e.g. Industry_Company):",
      "Nama folder baru (mis. Industri_Perusahaan):"));
    if (!name) return;
    try {
      await rootHandle.getDirectoryHandle(name, { create: true });
      await refreshFolders(rootHandle);
    } catch (e) {
      window.alert(tr(lang, "Couldn't create that folder: ", "Tidak bisa membuat folder itu: ") + e.message);
    }
  };

  return (
    <div className="fs-explorer">
      <div className="fs-explorer-toolbar">
        {activeFolder ? (
          <button className="btn-link" onClick={backToFolders}>← {tr(lang, "Back to folders", "Kembali ke folder")}</button>
        ) : (
          <button className="btn-sec" onClick={handleNewFolder}>{tr(lang, "+ New folder", "+ Folder baru")}</button>
        )}
        {activeFolder && (
          <button className="btn-sec" onClick={handleAddFileFromDisk}>{tr(lang, "+ Add preset from disk", "+ Tambah preset dari disk")}</button>
        )}
      </div>

      {loading && <div className="empty-window-callout">{tr(lang, "Loading…", "Memuat…")}</div>}
      {error && <WarnHint label={tr(lang, "Error", "Error")} note={error} />}

      {!loading && !activeFolder && (
        <table className="explorer-table">
          <thead><tr><th>{tr(lang, "Name", "Nama")}</th><th>{tr(lang, "Type", "Tipe")}</th><th>{tr(lang, "Items", "Item")}</th></tr></thead>
          <tbody>
            {folders.map(f => (
              <tr key={f.name} className="explorer-row" onClick={() => openFolder(f)}>
                <td>📁 {f.name}</td>
                <td>{tr(lang, "Folder", "Folder")}</td>
                <td>{f.count} {tr(lang, "preset(s)", "preset")}</td>
              </tr>
            ))}
            {folders.length === 0 && (
              <tr><td colSpan="3" style={{ color: "var(--text-muted)" }}>{tr(lang, "No folders yet — create one above.", "Belum ada folder — buat satu di atas.")}</td></tr>
            )}
          </tbody>
        </table>
      )}

      {!loading && activeFolder && (
        <table className="explorer-table">
          <thead><tr><th>{tr(lang, "Name", "Nama")}</th><th>{tr(lang, "Type", "Tipe")}</th><th></th></tr></thead>
          <tbody>
            {files.map(f => (
              <tr key={f.name} className="explorer-row" onClick={() => handleLoadFile(f)}>
                <td>📊 {f.name}</td>
                <td>{tr(lang, "Preset", "Preset")}</td>
                <td onClick={e => e.stopPropagation()}>
                  <button className="btn-sec" onClick={() => handleLoadFile(f)}>{tr(lang, "Load", "Muat")}</button>
                </td>
              </tr>
            ))}
            {files.length === 0 && (
              <tr><td colSpan="3" style={{ color: "var(--text-muted)" }}>{tr(lang, "No presets in this folder yet — use \"Add preset from disk\" above.", "Belum ada preset di folder ini — gunakan \"Tambah preset dari disk\" di atas.")}</td></tr>
            )}
          </tbody>
        </table>
      )}
    </div>
  );
}

Object.assign(window, { PresetExplorer, FSA_SUPPORTED });
