import type {
  CacheRemoteChatMediaInput,
  ChatMediaLookupInput,
  ChatRequestOptions,
  ChatSendOptions,
  IChatRepository,
  UnlockPrivateMessageInput,
  UnlockedPrivateMessageLocalPatch,
} from "@/data/repositories/interfaces";
import type {
  ChatHistoryResponse,
  ChatPreviewsResponse,
  ChatMessage,
  ChatSendResponse,
  OpeningMessageResponse,
  UnlockHistoryResponse,
  UnlockPrivateResponse,
} from "@/data/schemas/chat";
import { chatApi } from "@/data/services/api";
import {
  LocalChatMediaStorage,
  LocalChatStorage,
  type LocalChatMediaRow,
} from "@/data/storage/chat";
import type { Result } from "@/utils/result";

import { ChatLocalMessageStore } from "./chat_local_message_store";
import { ChatMediaCacheCoordinator } from "./chat_media_cache_coordinator";
import { ChatRemoteDataSource } from "./chat_remote_data_source";
import { createLazySingleton } from "./lazy_singleton";

export class ChatRepository implements IChatRepository {
  constructor(
    private readonly remote: ChatRemoteDataSource,
    private readonly localMessages: ChatLocalMessageStore,
    private readonly mediaCache: ChatMediaCacheCoordinator,
  ) {}

  // ============ 远程操作 ============

  /** 发送一条消息。 */
  async sendMessage(
    characterId: string,
    message: string,
    options?: ChatSendOptions,
  ): Promise<Result<ChatSendResponse>> {
    return this.remote.sendMessage(characterId, message, options);
  }

  async saveOpeningMessage(
    characterId: string,
    openingMessage: string,
    options?: ChatRequestOptions,
  ): Promise<Result<OpeningMessageResponse>> {
    return this.remote.saveOpeningMessage(
      characterId,
      openingMessage,
      options,
    );
  }

  /** 获取聊天历史，分页参数默认 limit=50, offset=0。 */
  async getHistory(
    characterId: string,
    limit = 50,
    offset = 0,
    options?: ChatRequestOptions,
  ): Promise<Result<ChatHistoryResponse>> {
    return this.remote.getHistory(characterId, limit, offset, options);
  }

  async getPreviews(
    options?: ChatRequestOptions,
  ): Promise<Result<ChatPreviewsResponse>> {
    return this.remote.getPreviews(options);
  }

  /** 解锁单条历史付费 / 私密消息。 */
  async unlockPrivateMessage(
    input: UnlockPrivateMessageInput,
    options?: ChatRequestOptions,
  ): Promise<Result<UnlockPrivateResponse>> {
    return this.remote.unlockPrivateMessage(input, options);
  }

  /** 一键解锁历史锁定消息。 */
  async unlockHistory(
    characterId: string,
    options?: ChatRequestOptions,
  ): Promise<Result<UnlockHistoryResponse>> {
    return this.remote.unlockHistory(characterId, options);
  }

  /** 把本地缓存中的单条锁定消息标记为已解锁。 */
  async markPrivateMessageUnlockedInLocal(
    messageId: string,
    patch?: UnlockedPrivateMessageLocalPatch,
    cacheIdentity?: string,
  ): Promise<Result<void>> {
    return this.localMessages.markMessageUnlocked(
      messageId,
      patch,
      cacheIdentity,
    );
  }

  // ============ 本地（Dexie）操作 ============

  /** 把一条消息写入本地存储。 */
  async saveMessageToLocal(
    message: ChatMessage,
    cacheIdentity?: string,
  ): Promise<Result<void>> {
    return this.localMessages.saveMessage(message, cacheIdentity);
  }

  /**
   * 批量覆盖写入：先清空本地存储，再写入新列表。
   * 用于网络历史同步后的整批刷新。
   */
  async saveMessagesToLocal(
    messages: readonly ChatMessage[],
    cacheIdentity?: string,
  ): Promise<Result<void>> {
    return this.localMessages.saveMessages(messages, cacheIdentity);
  }

  /** 读取所有本地消息，按 dbId 升序。 */
  async getLocalMessages(
    cacheIdentity?: string,
  ): Promise<Result<readonly ChatMessage[]>> {
    return this.localMessages.getMessages(cacheIdentity);
  }

  /** 清空本地消息。 */
  async clearLocalMessages(cacheIdentity?: string): Promise<Result<void>> {
    return this.localMessages.clearMessages(cacheIdentity);
  }

  /**
   * 获取本地消息数量。
   * Dexie 查询是异步操作，因此始终返回 Promise。
   */
  async getLocalMessageCount(cacheIdentity?: string): Promise<Result<number>> {
    return this.localMessages.getMessageCount(cacheIdentity);
  }

  // ============ 本地媒体缓存 ============

  async getCachedMedia(
    input: ChatMediaLookupInput,
  ): Promise<Result<LocalChatMediaRow | null>> {
    return this.mediaCache.getCachedMedia(input);
  }

  async cacheRemoteMedia(
    input: CacheRemoteChatMediaInput,
    cacheIdentity?: string,
  ): Promise<Result<LocalChatMediaRow>> {
    return this.mediaCache.cacheRemoteMedia(input, cacheIdentity);
  }

  async prefetchMediaForMessages(
    messages: readonly ChatMessage[],
    characterId: string,
    cacheIdentity?: string,
  ): Promise<Result<void>> {
    return this.mediaCache.prefetchMediaForMessages(
      messages,
      characterId,
      cacheIdentity,
    );
  }

  async prefetchMediaForSendResponse(
    response: ChatSendResponse,
    characterId: string,
    cacheIdentity?: string,
  ): Promise<Result<void>> {
    return this.mediaCache.prefetchMediaForSendResponse(
      response,
      characterId,
      cacheIdentity,
    );
  }
}

/** 全局懒单例。 */
export const getChatRepository = createLazySingleton<IChatRepository>(
  () =>
    new ChatRepository(
      new ChatRemoteDataSource(chatApi),
      new ChatLocalMessageStore(LocalChatStorage.getInstance()),
      new ChatMediaCacheCoordinator(LocalChatMediaStorage.getInstance()),
    ),
);
