/**
 * Folder-sync filesystem helpers (extension).
 * Write-only into the single granted directory handle.
 */

export function assertSafeRelPath(relPath) {
  if (typeof relPath !== 'string' || !relPath.trim()) {
    throw new Error('relativePath required');
  }
  const normalized = relPath.replace(/\\/g, '/').replace(/^\.\//, '');
  if (!normalized || normalized === '.') throw new Error('relativePath required');
  if (normalized.startsWith('/') || /^[a-zA-Z]:/.test(normalized)) {
    throw new Error('absolute paths rejected');
  }
  if (normalized.includes('\0')) throw new Error('invalid path');
  const parts = normalized.split('/');
  if (parts.some(p => !p || p === '.' || p === '..')) {
    throw new Error('path traversal rejected');
  }
  return parts;
}

const DB_NAME = 'rha-vault';
const STORE = 'handles';
const HANDLE_KEY = 'deploy';

export function openDb() {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(DB_NAME, 1);
    req.onupgradeneeded = () => {
      const db = req.result;
      if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE);
    };
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

export async function loadDeployHandle() {
  const db = await openDb();
  return new Promise((resolve, reject) => {
    const tx = db.transaction(STORE, 'readonly');
    const req = tx.objectStore(STORE).get(HANDLE_KEY);
    req.onsuccess = () => resolve(req.result || null);
    req.onerror = () => reject(req.error);
  });
}

export async function saveDeployHandle(handle) {
  const db = await openDb();
  await new Promise((resolve, reject) => {
    const tx = db.transaction(STORE, 'readwrite');
    tx.objectStore(STORE).put(handle, HANDLE_KEY);
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
  });
}

export async function clearDeployHandle() {
  const db = await openDb();
  await new Promise((resolve, reject) => {
    const tx = db.transaction(STORE, 'readwrite');
    tx.objectStore(STORE).delete(HANDLE_KEY);
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
  });
}

/** Ensure readwrite permission on the stored handle; abort if denied. */
export async function ensureWritePermission(handle) {
  if (!handle) throw new Error('no deploy folder granted — open Provider Vault options');
  let perm = await handle.queryPermission({ mode: 'readwrite' });
  if (perm !== 'granted') {
    try {
      perm = await handle.requestPermission({ mode: 'readwrite' });
    } catch {
      throw new Error(
        'deploy folder permission needs a fresh grant — open Provider Vault options and choose the folder again',
      );
    }
  }
  if (perm !== 'granted') {
    throw new Error(
      'deploy folder permission denied — open Provider Vault options and choose the folder again',
    );
  }
  return handle;
}

/** Write bytes to relativePath under rootHandle (creates intermediate dirs). */
export async function writeRelativeFile(rootHandle, relativePath, bytes) {
  const parts = assertSafeRelPath(relativePath);
  let dir = rootHandle;
  for (let i = 0; i < parts.length - 1; i++) {
    dir = await dir.getDirectoryHandle(parts[i], { create: true });
  }
  const name = parts[parts.length - 1];
  const file = await dir.getFileHandle(name, { create: true });
  const writable = await file.createWritable();
  try {
    await writable.write(bytes);
  } finally {
    await writable.close();
  }
}

export async function sha256Hex(bytes) {
  const digest = await crypto.subtle.digest('SHA-256', bytes);
  return [...new Uint8Array(digest)].map(b => b.toString(16).padStart(2, '0')).join('');
}

export function base64ToBytes(b64) {
  const bin = atob(b64);
  const out = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
  return out;
}
