import type { SecretStore } from '@chat-app/shared/auth';

import { isTauriRuntime } from './globalShortcut';

// Secret store backed by Electron's safeStorage (DPAPI / Keychain /
// libsecret) via the main-process secure-store IPC. Opens a per-user
// handle on first access and caches it for the lifetime of the session.
//
// Values are Uint8Array at the SecretStore interface level; we encode
// them as base64 on the wire since the preload bridge is stringly-typed.

let handlePromise: Promise<string | null> | null = null;
let openedFor: string | null = null;

async function ensureOpen(userId: string): Promise<string | null> {
  if (openedFor === userId && handlePromise) return handlePromise;
  if (!isTauriRuntime()) {
    handlePromise = Promise.resolve(null);
    return handlePromise;
  }
  openedFor = userId;
  handlePromise = (async (): Promise<string | null> => {
    try {
      const res = await window.electronAPI.secureStoreOpen({ userId });
      if (!res.encrypted) {
        console.warn(
          'secure-store: safeStorage unavailable, using plaintext fallback',
        );
      }
      return res.handle;
    } catch (err: unknown) {
      console.warn('secure-store open failed', err);
      return null;
    }
  })();
  return handlePromise;
}

function bytesToBase64(bytes: Uint8Array): string {
  let s = '';
  for (const b of bytes) s += String.fromCharCode(b);
  return btoa(s);
}

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

export function makeStrongholdStore(userId: string): SecretStore {
  return {
    async getSecret(key: string): Promise<Uint8Array | null> {
      const handle = await ensureOpen(userId);
      if (!handle) return null;
      const raw = await window.electronAPI.secureStoreGet(handle, key);
      if (!raw) return null;
      try {
        return base64ToBytes(raw);
      } catch {
        return null;
      }
    },
    async setSecret(key: string, value: Uint8Array): Promise<void> {
      const handle = await ensureOpen(userId);
      if (!handle) return;
      await window.electronAPI.secureStoreSet(handle, key, bytesToBase64(value));
    },
    async removeSecret(key: string): Promise<void> {
      const handle = await ensureOpen(userId);
      if (!handle) return;
      await window.electronAPI.secureStoreRemove(handle, key);
    },
  };
}

// Back-compat migration helper. Legacy localStorage entries under the
// caller-supplied prefix are copied into the safeStorage-backed store on
// first run. A marker key prevents repeated work across launches.
export async function migrateLocalStorageToStronghold(
  userId: string,
  prefix: string,
): Promise<void> {
  const handle = await ensureOpen(userId);
  if (!handle) return;
  const markerKey = '__migrated_from_localstorage';
  const already = await window.electronAPI.secureStoreGet(handle, markerKey);
  if (already) return;

  for (let i = 0; i < window.localStorage.length; i++) {
    const fullKey = window.localStorage.key(i);
    if (!fullKey || !fullKey.startsWith(prefix)) continue;
    const raw = window.localStorage.getItem(fullKey);
    if (!raw) continue;
    const shortKey = fullKey.slice(prefix.length);
    // Legacy payloads are already base64 — store as-is.
    await window.electronAPI.secureStoreSet(handle, shortKey, raw);
  }
  await window.electronAPI.secureStoreSet(handle, markerKey, '1');
}
