"use client";

/**
 * LocalChatStorage 完整实现（示范 2：IndexedDB via Dexie）
 *
 * 对齐 Dart 端 `LocalChatStorage`（lib/data/services/storage/chat/local_chat_storage.dart）：
 *   - init / saveMessage / saveMessages / getAllMessages / clearAll / deleteMessage / close
 *   - 额外提供 `getMessageCount`（异步）替代 Dart 的同步 `messageCount` getter
 *   - 额外提供 `getAllMessagesBySession(sessionId)`（filter 模拟，等下一轮加索引后再用 where）
 *
 * 单例挂在 class 静态字段。
 * Dexie 实例可注入，便于测试用 fake-indexeddb 跑真 Dexie。
 *
 * 注意：当前表无次级索引，所有过滤（sessionId）都是 `toArray().then(filter)`，O(n)。
 * 数据量大时考虑升级 schema 加索引。
 */

import { Result, type Result as ResultT } from "@/utils";
import { LocalChatDB } from "./local_chat_db";
import { LocalMessage } from "./local_message";

export class LocalChatStorage {
  private readonly db: LocalChatDB;
  private static _instance: LocalChatStorage | null = null;

  constructor(db?: LocalChatDB) {
    this.db = db ?? new LocalChatDB();
  }

  static getInstance(): LocalChatStorage {
    if (!LocalChatStorage._instance) {
      LocalChatStorage._instance = new LocalChatStorage();
    }
    return LocalChatStorage._instance;
  }

  /** @internal */
  static _resetForTests(): void {
    LocalChatStorage._instance = null;
  }

  /**
   * 触发 Dexie 打开数据库。
   * 幂等：Dexie.open() 多次调用无副作用。
   * 在 bootstrap 中 await 此方法可保证后续操作不会因连接未开而延迟。
   */
  async init(): Promise<ResultT<void>> {
    try {
      await this.db.open();
      return Result.ok(undefined);
    } catch (e) {
      return Result.err(e);
    }
  }

  // ---- create / append ----

  async saveMessage(message: LocalMessage): Promise<ResultT<void>> {
    try {
      await this.db.messages.add(message.toRow());
      return Result.ok(undefined);
    } catch (e) {
      return Result.err(e);
    }
  }

  async saveMessages(messages: readonly LocalMessage[]): Promise<ResultT<void>> {
    try {
      await this.db.transaction(
        "rw",
        this.db.messages,
        async () => {
          for (const m of messages) {
            await this.db.messages.add(m.toRow());
          }
        },
      );
      return Result.ok(undefined);
    } catch (e) {
      return Result.err(e);
    }
  }

  // ---- read ----

  async getAllMessages(): Promise<ResultT<LocalMessage[]>> {
    try {
      const rows = await this.db.messages.toArray();
      // 按 dbId 升序（与 Dart `box.values.toList()` 插入序语义一致）
      rows.sort((a, b) => (a.dbId ?? 0) - (b.dbId ?? 0));
      return Result.ok(rows.map((r) => LocalMessage.fromRow(r)));
    } catch (e) {
      return Result.err(e);
    }
  }

  async getMessageCount(): Promise<ResultT<number>> {
    try {
      return Result.ok(await this.db.messages.count());
    } catch (e) {
      return Result.err(e);
    }
  }

  async getAllMessagesBySession(
    sessionId: string,
  ): Promise<ResultT<LocalMessage[]>> {
    try {
      const rows = await this.db.messages
        .filter((r) => r.sessionId === sessionId)
        .toArray();
      rows.sort((a, b) => (a.dbId ?? 0) - (b.dbId ?? 0));
      return Result.ok(rows.map((r) => LocalMessage.fromRow(r)));
    } catch (e) {
      return Result.err(e);
    }
  }

  // ---- delete ----

  async clearAll(): Promise<ResultT<void>> {
    try {
      await this.db.messages.clear();
      return Result.ok(undefined);
    } catch (e) {
      return Result.err(e);
    }
  }

  /**
   * 按数组下标删除（保持 Dart 端语义：`box.values.toList()[index].delete()`）。
   * O(n) 取下标后单条 delete，与 Dart 行为一致。
   */
  async deleteMessage(index: number): Promise<ResultT<void>> {
    try {
      if (!Number.isInteger(index) || index < 0) {
        return Result.err(
          new RangeError(`deleteMessage: index ${index} out of range`),
        );
      }
      const rows = await this.db.messages.toArray();
      if (index >= rows.length) {
        return Result.err(
          new RangeError(`deleteMessage: index ${index} out of range`),
        );
      }
      const target = rows[index]!;
      await this.db.messages.delete(target.dbId!);
      return Result.ok(undefined);
    } catch (e) {
      return Result.err(e);
    }
  }

  async close(): Promise<ResultT<void>> {
    try {
      this.db.close();
      return Result.ok(undefined);
    } catch (e) {
      return Result.err(e);
    }
  }
}
