/**
 * IndexedDB local state persistence for the web client.
 *
 * Stores conversations, messages, and identity state in the browser.
 */

const DB_NAME = "quicproquo";
const DB_VERSION = 1;

export interface StoredConversation {
  id: string;
  name: string;
  unread: number;
  lastActivity: number;
}

export interface StoredMessage {
  id: string;
  conversationId: string;
  sender: string;
  body: string;
  timestamp: number;
  isOutgoing: boolean;
}

export interface StoredIdentity {
  username: string;
  seed: Uint8Array;
  publicKey: Uint8Array;
}

function openDB(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, DB_VERSION);

    request.onupgradeneeded = () => {
      const db = request.result;
      if (!db.objectStoreNames.contains("conversations")) {
        const convStore = db.createObjectStore("conversations", { keyPath: "id" });
        convStore.createIndex("lastActivity", "lastActivity");
      }
      if (!db.objectStoreNames.contains("messages")) {
        const msgStore = db.createObjectStore("messages", { keyPath: "id" });
        msgStore.createIndex("conversationId", "conversationId");
        msgStore.createIndex("timestamp", "timestamp");
      }
      if (!db.objectStoreNames.contains("identity")) {
        db.createObjectStore("identity", { keyPath: "username" });
      }
    };

    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
  });
}

export class LocalStore {
  private db: IDBDatabase | null = null;

  async open(): Promise<void> {
    this.db = await openDB();
  }

  close(): void {
    if (this.db) {
      this.db.close();
      this.db = null;
    }
  }

  // --- Identity ---

  async saveIdentity(identity: StoredIdentity): Promise<void> {
    const db = this.requireDB();
    const tx = db.transaction("identity", "readwrite");
    tx.objectStore("identity").put(identity);
    return txDone(tx);
  }

  async loadIdentity(username: string): Promise<StoredIdentity | null> {
    const db = this.requireDB();
    const tx = db.transaction("identity", "readonly");
    const req = tx.objectStore("identity").get(username);
    return new Promise((resolve, reject) => {
      req.onsuccess = () => resolve(req.result ?? null);
      req.onerror = () => reject(req.error);
    });
  }

  // --- Conversations ---

  async saveConversation(conv: StoredConversation): Promise<void> {
    const db = this.requireDB();
    const tx = db.transaction("conversations", "readwrite");
    tx.objectStore("conversations").put(conv);
    return txDone(tx);
  }

  async listConversations(): Promise<StoredConversation[]> {
    const db = this.requireDB();
    const tx = db.transaction("conversations", "readonly");
    const req = tx.objectStore("conversations").index("lastActivity").getAll();
    return new Promise((resolve, reject) => {
      req.onsuccess = () => resolve((req.result ?? []).reverse());
      req.onerror = () => reject(req.error);
    });
  }

  async getConversation(id: string): Promise<StoredConversation | null> {
    const db = this.requireDB();
    const tx = db.transaction("conversations", "readonly");
    const req = tx.objectStore("conversations").get(id);
    return new Promise((resolve, reject) => {
      req.onsuccess = () => resolve(req.result ?? null);
      req.onerror = () => reject(req.error);
    });
  }

  // --- Messages ---

  async saveMessage(msg: StoredMessage): Promise<void> {
    const db = this.requireDB();
    const tx = db.transaction("messages", "readwrite");
    tx.objectStore("messages").put(msg);
    return txDone(tx);
  }

  async loadMessages(conversationId: string, limit = 200): Promise<StoredMessage[]> {
    const db = this.requireDB();
    const tx = db.transaction("messages", "readonly");
    const index = tx.objectStore("messages").index("conversationId");
    const req = index.getAll(conversationId);
    return new Promise((resolve, reject) => {
      req.onsuccess = () => {
        const all: StoredMessage[] = req.result ?? [];
        all.sort((a, b) => a.timestamp - b.timestamp);
        resolve(all.slice(-limit));
      };
      req.onerror = () => reject(req.error);
    });
  }

  // --- Clear ---

  async clearAll(): Promise<void> {
    const db = this.requireDB();
    const tx = db.transaction(
      ["conversations", "messages", "identity"],
      "readwrite",
    );
    tx.objectStore("conversations").clear();
    tx.objectStore("messages").clear();
    tx.objectStore("identity").clear();
    return txDone(tx);
  }

  private requireDB(): IDBDatabase {
    if (!this.db) {
      throw new Error("LocalStore not opened. Call open() first.");
    }
    return this.db;
  }
}

function txDone(tx: IDBTransaction): Promise<void> {
  return new Promise((resolve, reject) => {
    tx.oncomplete = () => resolve();
    tx.onerror = () => reject(tx.error);
  });
}
