import { base64ToBytes, bytesToBase64 } from './crypto-utils.js';

const DB_NAME = 'shine-wallet-plugin';
const DB_VERSION = 1;
const STORE_META = 'meta';
const STORE_VAULT = 'vault';
const SESSION_ENTRY_ID = 'active-session';
const VAULT_KEY_ID = 'session-wrap-key';

function openDb() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, DB_VERSION);
    request.onupgradeneeded = () => {
      const db = request.result;
      if (!db.objectStoreNames.contains(STORE_META)) {
        db.createObjectStore(STORE_META, { keyPath: 'id' });
      }
      if (!db.objectStoreNames.contains(STORE_VAULT)) {
        db.createObjectStore(STORE_VAULT, { keyPath: 'id' });
      }
    };
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error || new Error('IndexedDB недоступен'));
  });
}

async function withStore(storeName, mode, run) {
  const db = await openDb();
  try {
    return await new Promise((resolve, reject) => {
      const tx = db.transaction(storeName, mode);
      const store = tx.objectStore(storeName);
      let settled = false;
      const done = (fn) => (value) => {
        if (settled) return;
        settled = true;
        fn(value);
      };
      tx.oncomplete = () => done(resolve)(undefined);
      tx.onerror = () => done(reject)(tx.error || new Error('IndexedDB transaction failed'));
      Promise.resolve(run(store, tx, done)).catch((error) => done(reject)(error));
    });
  } finally {
    db.close();
  }
}

async function put(storeName, value) {
  return withStore(storeName, 'readwrite', (store) => {
    store.put(value);
  });
}

async function get(storeName, key) {
  const db = await openDb();
  try {
    return await new Promise((resolve, reject) => {
      const tx = db.transaction(storeName, 'readonly');
      const req = tx.objectStore(storeName).get(key);
      req.onsuccess = () => resolve(req.result || null);
      req.onerror = () => reject(req.error || new Error('Ошибка чтения из IndexedDB'));
    });
  } finally {
    db.close();
  }
}

async function deleteById(storeName, key) {
  return withStore(storeName, 'readwrite', (store) => {
    store.delete(key);
  });
}

async function getOrCreateVaultKey() {
  const current = await get(STORE_META, VAULT_KEY_ID);
  if (current?.key) return current.key;

  const key = await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 },
    false,
    ['encrypt', 'decrypt'],
  );
  await put(STORE_META, { id: VAULT_KEY_ID, key, createdAtMs: Date.now() });
  return key;
}

async function encryptJson(value) {
  const key = await getOrCreateVaultKey();
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const plainBytes = new TextEncoder().encode(JSON.stringify(value));
  const cipher = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plainBytes);
  return {
    ivB64: bytesToBase64(iv),
    cipherB64: bytesToBase64(new Uint8Array(cipher)),
  };
}

async function decryptJson(envelope) {
  const key = await getOrCreateVaultKey();
  const plain = await crypto.subtle.decrypt(
    { name: 'AES-GCM', iv: base64ToBytes(envelope.ivB64) },
    key,
    base64ToBytes(envelope.cipherB64),
  );
  return JSON.parse(new TextDecoder().decode(plain));
}

function storageApi() {
  if (globalThis.chrome?.storage?.local) return globalThis.chrome.storage.local;
  return null;
}

export async function savePluginSettings(settings) {
  const api = storageApi();
  if (api) {
    await api.set({ shineWalletSettings: settings });
    return;
  }
  localStorage.setItem('shineWalletSettings', JSON.stringify(settings));
}

export async function loadPluginSettings() {
  const api = storageApi();
  if (api) {
    const row = await api.get('shineWalletSettings');
    return row?.shineWalletSettings || {};
  }
  try {
    return JSON.parse(localStorage.getItem('shineWalletSettings') || '{}');
  } catch {
    return {};
  }
}

export async function saveSessionMaterial(sessionRecord) {
  const encrypted = await encryptJson(sessionRecord);
  await put(STORE_VAULT, {
    id: SESSION_ENTRY_ID,
    encrypted,
    updatedAtMs: Date.now(),
  });
}

export async function loadSessionMaterial() {
  const row = await get(STORE_VAULT, SESSION_ENTRY_ID);
  if (!row?.encrypted) return null;
  return decryptJson(row.encrypted);
}

export async function clearSessionMaterial() {
  await deleteById(STORE_VAULT, SESSION_ENTRY_ID);
}
