/**
 * TSS Share 本地存储模块
 * 使用 IndexedDB 存储加密的 TSS shares
 */

const DB_NAME = 'tss-shares-db';
const DB_VERSION = 1;
const STORE_NAME = 'shares';

export interface ShareEntry {
  id: string;
  sessionId: string;
  walletName: string;
  partyId: string;
  partyIndex: number;
  threshold: { t: number; n: number };
  publicKey: string;
  encryptedShare: string; // AES-256-GCM 加密的 share 数据
  iv: string; // 初始化向量
  salt: string; // 密码派生盐
  createdAt: string;
  lastUsedAt?: string;
  metadata: {
    participants: Array<{ partyId: string; name: string }>;
  };
}

/**
 * 打开 IndexedDB 数据库
 */
function openDB(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, DB_VERSION);

    request.onerror = () => {
      reject(new Error('Failed to open IndexedDB'));
    };

    request.onsuccess = () => {
      resolve(request.result);
    };

    request.onupgradeneeded = (event) => {
      const db = (event.target as IDBOpenDBRequest).result;
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        const store = db.createObjectStore(STORE_NAME, { keyPath: 'id' });
        store.createIndex('sessionId', 'sessionId', { unique: false });
        store.createIndex('walletName', 'walletName', { unique: false });
        store.createIndex('publicKey', 'publicKey', { unique: false });
      }
    };
  });
}

/**
 * 从密码派生加密密钥
 */
async function deriveKey(password: string, salt: Uint8Array): Promise<CryptoKey> {
  const encoder = new TextEncoder();
  const passwordKey = await crypto.subtle.importKey(
    'raw',
    encoder.encode(password),
    'PBKDF2',
    false,
    ['deriveBits', 'deriveKey']
  );

  return crypto.subtle.deriveKey(
    {
      name: 'PBKDF2',
      salt: salt.buffer as ArrayBuffer,
      iterations: 100000,
      hash: 'SHA-256',
    },
    passwordKey,
    { name: 'AES-GCM', length: 256 },
    false,
    ['encrypt', 'decrypt']
  );
}

/**
 * 加密数据
 */
async function encryptData(data: string, password: string): Promise<{ encrypted: string; iv: string; salt: string }> {
  const encoder = new TextEncoder();
  const salt = crypto.getRandomValues(new Uint8Array(16));
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const key = await deriveKey(password, salt);

  const encrypted = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    key,
    encoder.encode(data)
  );

  return {
    encrypted: btoa(String.fromCharCode(...new Uint8Array(encrypted))),
    iv: btoa(String.fromCharCode(...iv)),
    salt: btoa(String.fromCharCode(...salt)),
  };
}

/**
 * 解密数据
 */
async function decryptData(encrypted: string, iv: string, salt: string, password: string): Promise<string> {
  const decoder = new TextDecoder();
  const encryptedBytes = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0));
  const ivBytes = Uint8Array.from(atob(iv), (c) => c.charCodeAt(0));
  const saltBytes = Uint8Array.from(atob(salt), (c) => c.charCodeAt(0));

  const key = await deriveKey(password, saltBytes);

  const decrypted = await crypto.subtle.decrypt(
    { name: 'AES-GCM', iv: ivBytes },
    key,
    encryptedBytes
  );

  return decoder.decode(decrypted);
}

/**
 * Share 存储服务
 */
export const shareStorage = {
  /**
   * 保存 share
   */
  async saveShare(
    share: Omit<ShareEntry, 'encryptedShare' | 'iv' | 'salt'> & { rawShare: string },
    password: string
  ): Promise<void> {
    const db = await openDB();
    const { encrypted, iv, salt } = await encryptData(share.rawShare, password);

    const entry: ShareEntry = {
      id: share.id,
      sessionId: share.sessionId,
      walletName: share.walletName,
      partyId: share.partyId,
      partyIndex: share.partyIndex,
      threshold: share.threshold,
      publicKey: share.publicKey,
      encryptedShare: encrypted,
      iv,
      salt,
      createdAt: share.createdAt,
      lastUsedAt: share.lastUsedAt,
      metadata: share.metadata,
    };

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readwrite');
      const store = transaction.objectStore(STORE_NAME);
      const request = store.put(entry);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(new Error('Failed to save share'));
    });
  },

  /**
   * 获取所有 shares（不包含加密数据）
   */
  async listShares(): Promise<Omit<ShareEntry, 'encryptedShare' | 'iv' | 'salt'>[]> {
    const db = await openDB();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readonly');
      const store = transaction.objectStore(STORE_NAME);
      const request = store.getAll();

      request.onsuccess = () => {
        const shares = request.result.map((entry: ShareEntry) => ({
          id: entry.id,
          sessionId: entry.sessionId,
          walletName: entry.walletName,
          partyId: entry.partyId,
          partyIndex: entry.partyIndex,
          threshold: entry.threshold,
          publicKey: entry.publicKey,
          createdAt: entry.createdAt,
          lastUsedAt: entry.lastUsedAt,
          metadata: entry.metadata,
        }));
        resolve(shares);
      };
      request.onerror = () => reject(new Error('Failed to list shares'));
    });
  },

  /**
   * 获取 share（包含解密后的原始数据）
   */
  async getShare(id: string, password: string): Promise<{ share: ShareEntry; rawShare: string }> {
    const db = await openDB();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readonly');
      const store = transaction.objectStore(STORE_NAME);
      const request = store.get(id);

      request.onsuccess = async () => {
        if (!request.result) {
          reject(new Error('Share not found'));
          return;
        }

        try {
          const entry = request.result as ShareEntry;
          const rawShare = await decryptData(entry.encryptedShare, entry.iv, entry.salt, password);
          resolve({ share: entry, rawShare });
        } catch {
          reject(new Error('Invalid password'));
        }
      };
      request.onerror = () => reject(new Error('Failed to get share'));
    });
  },

  /**
   * 更新最后使用时间
   */
  async updateLastUsed(id: string): Promise<void> {
    const db = await openDB();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readwrite');
      const store = transaction.objectStore(STORE_NAME);
      const getRequest = store.get(id);

      getRequest.onsuccess = () => {
        if (!getRequest.result) {
          reject(new Error('Share not found'));
          return;
        }

        const entry = getRequest.result as ShareEntry;
        entry.lastUsedAt = new Date().toISOString();
        const putRequest = store.put(entry);

        putRequest.onsuccess = () => resolve();
        putRequest.onerror = () => reject(new Error('Failed to update share'));
      };
      getRequest.onerror = () => reject(new Error('Failed to get share'));
    });
  },

  /**
   * 删除 share
   */
  async deleteShare(id: string): Promise<void> {
    const db = await openDB();

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readwrite');
      const store = transaction.objectStore(STORE_NAME);
      const request = store.delete(id);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(new Error('Failed to delete share'));
    });
  },

  /**
   * 导出 share（加密格式）
   */
  async exportShare(id: string, password: string): Promise<string> {
    const { share, rawShare } = await this.getShare(id, password);

    // 使用新密码重新加密用于导出
    const { encrypted, iv, salt } = await encryptData(rawShare, password);

    const exportData = {
      version: 1,
      id: share.id,
      sessionId: share.sessionId,
      walletName: share.walletName,
      partyId: share.partyId,
      partyIndex: share.partyIndex,
      threshold: share.threshold,
      publicKey: share.publicKey,
      encryptedShare: encrypted,
      iv,
      salt,
      createdAt: share.createdAt,
      metadata: share.metadata,
    };

    return btoa(JSON.stringify(exportData));
  },

  /**
   * 导入 share
   */
  async importShare(data: string, password: string): Promise<void> {
    try {
      const exportData = JSON.parse(atob(data));

      // 验证密码并解密
      const rawShare = await decryptData(
        exportData.encryptedShare,
        exportData.iv,
        exportData.salt,
        password
      );

      // 保存到本地存储
      await this.saveShare(
        {
          id: exportData.id,
          sessionId: exportData.sessionId,
          walletName: exportData.walletName,
          partyId: exportData.partyId,
          partyIndex: exportData.partyIndex,
          threshold: exportData.threshold,
          publicKey: exportData.publicKey,
          createdAt: exportData.createdAt,
          metadata: exportData.metadata,
          rawShare,
        },
        password
      );
    } catch {
      throw new Error('导入失败：数据格式错误或密码不正确');
    }
  },
};
