"use client";

import { Result, type Result as ResultT } from "@/utils";
import type { ChatMediaKind } from "@/data/dto/chat";

import {
  LocalChatDB,
  type LocalChatMediaRow,
} from "./local_chat_db";

export type { LocalChatMediaRow };

export interface SaveLocalChatMediaInput {
  cacheKey: string;
  ownerKey: string;
  messageId: string;
  kind: ChatMediaKind;
  remoteUrl: string;
  blob: Blob;
  mimeType: string;
}

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

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

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

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

  async getMedia(cacheKey: string): Promise<ResultT<LocalChatMediaRow | null>> {
    try {
      const row = (await this.db.media.get(cacheKey)) ?? null;
      if (row) {
        await this.db.media.update(cacheKey, {
          lastAccessedAt: Date.now(),
        });
      }
      return Result.ok(row);
    } catch (e) {
      return Result.err(e);
    }
  }

  async saveMedia(input: SaveLocalChatMediaInput): Promise<ResultT<void>> {
    try {
      const now = Date.now();
      const existing = await this.db.media.get(input.cacheKey);
      await this.db.media.put({
        cacheKey: input.cacheKey,
        ownerKey: input.ownerKey,
        messageId: input.messageId,
        kind: input.kind,
        remoteUrl: input.remoteUrl,
        blob: input.blob,
        mimeType: input.mimeType,
        byteSize: input.blob.size,
        createdAt: existing?.createdAt ?? now,
        updatedAt: now,
        lastAccessedAt: now,
      });
      return Result.ok(undefined);
    } catch (e) {
      return Result.err(e);
    }
  }

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