/* ============================================================
   LOCAL VAULT — REAL file storage (IndexedDB)
   Unlike the simulated 1:1 mirror, this actually ingests the
   files the user picks: real bytes read with progress, stored in
   IndexedDB, listed with real sizes, restorable (download) and
   deletable. Persists across reloads. Backend-ready: swap the
   vaultPut/vaultDel/vaultAll calls for API uploads.
   ============================================================ */

function vaultDB() {
  return new Promise((res, rej) => {
    const name = window.PROJ_ID && window.PROJ_ID !== 'crimson' ? 'silverlines-vault-' + window.PROJ_ID : 'silverlines-vault';
    const r = indexedDB.open(name, 1);
    r.onupgradeneeded = () => r.result.createObjectStore('files', { keyPath: 'id' });
    r.onsuccess = () => res(r.result);
    r.onerror = () => rej(r.error);
  });
}
async function vaultAll() {
  const db = await vaultDB();
  return new Promise((res, rej) => {
    const q = db.transaction('files').objectStore('files').getAll();
    q.onsuccess = () => res((q.result || []).sort((a, b) => b.ts - a.ts));
    q.onerror = () => rej(q.error);
  });
}
async function vaultPut(item) {
  const db = await vaultDB();
  return new Promise((res, rej) => {
    const t = db.transaction('files', 'readwrite');
    t.objectStore('files').put(item);
    t.oncomplete = () => res(); t.onerror = () => rej(t.error);
  });
}
async function vaultGet(id) {
  const db = await vaultDB();
  return new Promise((res, rej) => {
    const q = db.transaction('files').objectStore('files').get(id);
    q.onsuccess = () => res(q.result); q.onerror = () => rej(q.error);
  });
}
async function vaultDel(id) {
  const db = await vaultDB();
  return new Promise((res, rej) => {
    const t = db.transaction('files', 'readwrite');
    t.objectStore('files').delete(id);
    t.oncomplete = () => res(); t.onerror = () => rej(t.error);
  });
}

const vSize = b => {
  if (b == null) return '—';
  if (b < 1024) return b + ' B';
  if (b < 1048576) return (b / 1024).toFixed(0) + ' KB';
  if (b < 1073741824) return (b / 1048576).toFixed(1) + ' MB';
  return (b / 1073741824).toFixed(2) + ' GB';
};
const vIcon = type => {
  if (!type) return 'File';
  if (type.startsWith('image/')) return 'Image';
  if (type.startsWith('video/')) return 'Film';
  if (type.startsWith('audio/')) return 'Music';
  if (type.includes('pdf')) return 'FileText';
  if (type.includes('zip') || type.includes('compressed')) return 'FileArchive';
  if (type.includes('sheet') || type.includes('csv') || type.includes('excel')) return 'Sheet';
  return 'File';
};

/* read a File to an ArrayBuffer with real byte-level progress */
function readWithProgress(file, onProgress) {
  return new Promise((res, rej) => {
    const r = new FileReader();
    r.onprogress = e => { if (e.lengthComputable) onProgress(e.loaded); };
    r.onload = () => { onProgress(file.size); res(r.result); };
    r.onerror = () => rej(r.error);
    r.readAsArrayBuffer(file);
  });
}

function RealVaultPanel({ mobile }) {
  const [files, setFiles] = useState([]);
  const [loaded, setLoaded] = useState(false);
  const [drag, setDrag] = useState(false);
  const [job, setJob] = useState(null); // { name, idx, total, done, bytes, totalBytes }
  const [quota, setQuota] = useState(null);
  const inputRef = useRef(null);

  const refresh = async () => {
    setFiles(await vaultAll());
    setLoaded(true);
    if (navigator.storage && navigator.storage.estimate) {
      try { const q = await navigator.storage.estimate(); setQuota(q); } catch (e) {}
    }
  };
  useEffect(() => { refresh(); }, []);

  const ingest = async list => {
    const arr = Array.from(list || []);
    if (!arr.length) return;
    const totalBytes = arr.reduce((a, f) => a + f.size, 0);
    let doneBytes = 0, stored = 0;
    try {
      for (let i = 0; i < arr.length; i++) {
        const f = arr[i];
        setJob({ name: f.name, idx: i + 1, total: arr.length, bytes: doneBytes, totalBytes });
        const buf = await readWithProgress(f, loaded => {
          setJob(j => j && { ...j, bytes: doneBytes + loaded });
        });
        await vaultPut({
          id: 'v' + Date.now() + '-' + i, name: f.name, type: f.type || '',
          size: f.size, ts: Date.now(), blob: new Blob([buf], { type: f.type || 'application/octet-stream' }),
        });
        doneBytes += f.size;
        stored++;
      }
      window.toast && window.toast(`${stored} file${stored > 1 ? 's' : ''} stored in the vault`, 'ShieldCheck');
      window.notify && window.notify(`${stored} file${stored > 1 ? 's' : ''} added to the Local Vault`, 'HardDrive', 'storage');
    } catch (e) {
      /* a read or IndexedDB (quota) failure used to leave `job` stuck, disabling
         "Add files" forever — finally clears it; files stored before the error stay. */
      const isQuota = e && (e.name === 'QuotaExceededError' || e.code === 22);
      window.toast && window.toast(
        isQuota
          ? `Storage is full — saved ${stored} of ${arr.length}. Free up space and add the rest.`
          : `Couldn't finish the import — saved ${stored} of ${arr.length}.`,
        'AlertTriangle');
    } finally {
      setJob(null);
      await refresh();
    }
  };

  const restore = async it => {
    const rec = await vaultGet(it.id);
    if (!rec || !rec.blob) return;
    const url = URL.createObjectURL(rec.blob);
    const a = document.createElement('a');
    a.href = url; a.download = it.name; document.body.appendChild(a); a.click();
    a.remove(); setTimeout(() => URL.revokeObjectURL(url), 4000);
    window.toast && window.toast(`Restoring ${it.name}`, 'Download');
  };
  const remove = async it => {
    await vaultDel(it.id); await refresh();
    window.toast && window.toast(`${it.name} removed from the vault`, 'Trash2');
  };

  const totalBytes = files.reduce((a, f) => a + f.size, 0);
  const onDrop = e => { e.preventDefault(); setDrag(false); ingest(e.dataTransfer.files); };
  const jobPct = job && job.totalBytes ? Math.min(100, (job.bytes / job.totalBytes) * 100) : 0;

  return (
    <div className="fade-up grid gap-5 grid-cols-1">
      {/* summary strip */}
      <Panel>
        <div className="flex flex-wrap items-center justify-between gap-4">
          <div className="flex items-center gap-3">
            <span className="flex h-11 w-11 items-center justify-center rounded-xl bg-electric/12 text-electric-soft"><Icon name="HardDrive" size={20} /></span>
            <div>
              <div className="font-display text-[15px] font-600 text-head">Local Vault</div>
              <div className="text-[12px] text-zinc-500">Private to this browser on this device · {files.length} file{files.length === 1 ? '' : 's'} · {vSize(totalBytes)}</div>
            </div>
          </div>
          <div className="flex items-center gap-2">
            <span className="hidden items-center gap-1.5 rounded-full border border-mint/30 bg-mint/10 px-2.5 py-1 text-[10.5px] font-700 text-mint sm:flex"><Icon name="ShieldCheck" size={12} /> Persists across reloads</span>
            <button onClick={() => inputRef.current && inputRef.current.click()} disabled={!!job}
              className={cx('flex items-center gap-2 rounded-xl px-3.5 py-2.5 text-[12.5px] font-700 transition-colors', job ? 'cursor-not-allowed bg-ink-700 text-zinc-500' : 'bg-cta text-cta-ink hover:bg-cta-hover')}>
              <Icon name="Upload" size={15} /> Add files
            </button>
          </div>
        </div>
        {quota && quota.quota ? (
          <div className="mt-4">
            <div className="flex items-center justify-between text-[11px] text-zinc-500">
              <span>Browser storage used</span>
              <span className="font-mono">{vSize(quota.usage)} / {vSize(quota.quota)}</span>
            </div>
            <div className="mt-1.5 h-1.5 overflow-hidden rounded-full bg-ink-700">
              <div className="h-full rounded-full bg-electric" style={{ width: Math.min(100, (quota.usage / quota.quota) * 100) + '%' }} />
            </div>
          </div>
        ) : null}
      </Panel>

      {/* drop zone + active ingest */}
      <input ref={inputRef} type="file" multiple className="hidden" onChange={e => { ingest(e.target.files); e.target.value = ''; }} />
      <div
        onDragOver={e => { e.preventDefault(); setDrag(true); }}
        onDragLeave={() => setDrag(false)}
        onDrop={onDrop}
        onClick={() => !job && inputRef.current && inputRef.current.click()}
        className={cx('cursor-pointer rounded-2xl border-2 border-dashed p-6 text-center transition-colors',
          drag ? 'border-electric bg-electric/8' : 'border-ink-600 bg-ink-900 hover:border-ink-600 hover:bg-ink-800')}>
        {job ? (
          <div className="fade-up">
            <div className="flex items-center justify-center gap-2 text-[13px] font-700 text-electric-soft">
              <Icon name="HardDriveDownload" size={16} className="animate-pulse" /> Storing {job.idx} of {job.total} — {job.name}
            </div>
            <div className="mx-auto mt-3 h-2 max-w-md overflow-hidden rounded-full bg-ink-700">
              <div className="h-full rounded-full bg-electric transition-[width] duration-150" style={{ width: jobPct + '%' }} />
            </div>
            <div className="mt-2 font-mono text-[11px] text-zinc-500">{vSize(job.bytes)} / {vSize(job.totalBytes)} · {Math.round(jobPct)}%</div>
          </div>
        ) : (
          <div className="flex flex-col items-center gap-2">
            <span className="flex h-12 w-12 items-center justify-center rounded-xl bg-ink-700 text-zinc-400"><Icon name="UploadCloud" size={22} /></span>
            <div className="text-[13.5px] font-600 text-zinc-200">Drop files here, or click to browse</div>
            <div className="text-[11.5px] text-zinc-500">Footage, stills, documents — stored locally with real byte-verified writes</div>
          </div>
        )}
      </div>

      {/* stored files */}
      <Panel pad={false} className="overflow-hidden">
        <div className="flex items-center justify-between border-b border-line p-3.5">
          <h3 className="font-display text-[15px] font-600 text-head">Stored Files</h3>
          {files.length > 0 && <span className="font-mono text-[11px] text-zinc-500">{files.length} item{files.length > 1 ? 's' : ''} · {vSize(totalBytes)}</span>}
        </div>
        {!loaded ? (
          <div className="px-3.5 py-10 text-center text-[12px] text-zinc-500">Opening vault…</div>
        ) : files.length === 0 ? (
          <div className="flex flex-col items-center gap-2 px-3.5 py-12 text-center">
            <Icon name="Archive" size={24} className="text-zinc-600" />
            <div className="text-[12.5px] text-zinc-500">Vault is empty — add files above and they'll persist here.</div>
          </div>
        ) : (
          <div>
            {files.map(it => (
              <div key={it.id} className="group/vf flex items-center gap-3 border-t border-line/70 px-3.5 py-2.5 first:border-t-0 hover:bg-ink-800">
                <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-ink-700 text-zinc-300"><Icon name={vIcon(it.type)} size={16} /></span>
                <div className="min-w-0 flex-1">
                  <div className="truncate text-[13px] font-600 text-zinc-100">{it.name}</div>
                  <div className="whitespace-nowrap font-mono text-[10.5px] text-zinc-500">{vSize(it.size)} · {new Date(it.ts).toLocaleDateString('en-US', { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' })}</div>
                </div>
                <button onClick={() => restore(it)} title="Restore (download)"
                  className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-line bg-ink-900 text-zinc-400 transition-colors hover:text-electric-soft"><Icon name="Download" size={14} /></button>
                <button onClick={() => remove(it)} title="Delete from vault"
                  className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-line bg-ink-900 text-zinc-500 transition-colors hover:text-[#FF4D6D]"><Icon name="Trash2" size={14} /></button>
              </div>
            ))}
          </div>
        )}
      </Panel>
    </div>
  );
}

window.RealVaultPanel = RealVaultPanel;
