/**
 * EncryptID Wallet Sync
 *
 * Bridges WalletStore (encrypted localStorage) ↔ VaultManager (encrypted server blob)
 * so wallet associations survive across devices.
 *
 * On login:
 *   1. Load vault from server
 *   2. Merge vault wallets into local WalletStore (new entries only)
 *   3. Merge local wallets into vault (in case this device has wallets the vault doesn't)
 *   4. Save vault if anything changed
 *
 * On wallet add/remove:
 *   syncWalletsToVault() pushes current WalletStore state into the vault.
 */

import { getWalletStore, type SafeWalletEntry } from './wallet-store';
import { getVaultManager, type AccountVault } from './vault';
import { getKeyManager } from './key-derivation';
import type { DocCrypto } from '../../shared/local-first/crypto';

/**
 * After login + vault load, merge wallets bidirectionally between
 * the local WalletStore and the remote AccountVault.
 *
 * Requires PRF-derived encryption key (for WalletStore) and DocCrypto (for VaultManager).
 */
export async function syncWalletsOnLogin(docCrypto: DocCrypto): Promise<void> {
  try {
    const km = getKeyManager();
    if (!km.isInitialized()) return;

    const keys = await km.getKeys();
    if (!keys.encryptionKey) return;

    const walletStore = getWalletStore(keys.encryptionKey);
    const vaultManager = getVaultManager(docCrypto);

    const vault = await vaultManager.get();
    const localWallets = await walletStore.list();
    const vaultWallets = vault.wallets || [];

    let changed = false;

    // 1. Restore vault wallets → local WalletStore (if not already present)
    for (const vw of vaultWallets) {
      const exists = localWallets.find(
        lw => lw.safeAddress.toLowerCase() === vw.safeAddress.toLowerCase()
          && lw.chainId === vw.chainId
      );
      if (!exists) {
        await walletStore.add({
          safeAddress: vw.safeAddress,
          chainId: vw.chainId,
          eoaAddress: vw.eoaAddress,
          label: vw.label,
        });
      }
    }

    // 2. Push local wallets → vault (if not already present)
    const updatedLocal = await walletStore.list();
    const updatedVaultWallets = [...vaultWallets];

    for (const lw of updatedLocal) {
      const exists = updatedVaultWallets.find(
        vw => vw.safeAddress.toLowerCase() === lw.safeAddress.toLowerCase()
          && vw.chainId === lw.chainId
      );
      if (!exists) {
        updatedVaultWallets.push({
          id: lw.id,
          safeAddress: lw.safeAddress,
          chainId: lw.chainId,
          eoaAddress: lw.eoaAddress,
          label: lw.label,
          addedAt: lw.addedAt,
        });
        changed = true;
      }
    }

    // 3. Also ensure the server-side wallet_address profile field is set
    //    This is the fallback for mobile (no PRF) — the JWT carries it.
    if (keys.eoaAddress) {
      await setServerWalletAddress(keys.eoaAddress).catch(() => {});
    }

    // 4. Save vault if anything changed
    if (changed) {
      await vaultManager.update({ wallets: updatedVaultWallets });
    }
  } catch (err) {
    console.warn('Wallet sync failed:', err);
  }
}

/**
 * Push current WalletStore state into the vault.
 * Call this after any wallet add/update/remove operation.
 */
export async function syncWalletsToVault(docCrypto: DocCrypto): Promise<void> {
  try {
    const km = getKeyManager();
    if (!km.isInitialized()) return;

    const keys = await km.getKeys();
    if (!keys.encryptionKey) return;

    const walletStore = getWalletStore(keys.encryptionKey);
    const vaultManager = getVaultManager(docCrypto);

    const localWallets = await walletStore.list();
    const vaultWallets = localWallets.map(lw => ({
      id: lw.id,
      safeAddress: lw.safeAddress,
      chainId: lw.chainId,
      eoaAddress: lw.eoaAddress,
      label: lw.label,
      addedAt: lw.addedAt,
    }));

    await vaultManager.update({ wallets: vaultWallets });
  } catch (err) {
    console.warn('Wallet vault sync failed:', err);
  }
}

/**
 * Set the wallet address on the server user profile.
 * This ensures the JWT carries the wallet address for non-PRF devices (mobile).
 */
async function setServerWalletAddress(walletAddress: string): Promise<void> {
  const token = localStorage.getItem('encryptid_token');
  if (!token) return;

  await fetch('/encryptid/api/wallet-capability', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ walletAddress }),
  });
}
