"use client";

import { ChatApi, chatApi } from "@/data/services/api";
import {
  ChatHistoryResponse,
  ChatMessage,
  ChatSendResponse,
  SendMessageRequest,
  UnlockHistoryResponse,
  UnlockPrivateRequest,
  UnlockPrivateResponse,
} from "@/data/dto/chat";
import { AuthStorage } from "@/data/storage/auth";
import { UserStorage } from "@/data/storage/user/user_storage";
import { Logger, Result } from "@/utils";
import type {
  CacheRemoteChatMediaInput,
  ChatMediaLookupInput,
  IChatRepository,
} from "@/data/repositories/interfaces";
import {
  LocalChatMediaStorage,
  LocalChatStorage,
  LocalMessage,
  type LocalChatMediaRow,
} from "@/data/storage/chat";
import {
  buildChatMediaCacheKey,
  fallbackChatMediaMimeType,
  getMessageMediaTargets,
  getSendResponseMediaTargets,
  isCacheableRemoteChatMediaUrl,
  uniqueMediaTargets,
} from "./chat_media_cache_helpers";
import { createLazySingleton } from "./lazy_singleton";
import { requestBrowserPersistentStorageOnce } from "@/lib/browser/persistent_storage";

const log = new Logger("DataRepositoriesChatRepository");

export class ChatRepository implements IChatRepository {
  constructor(
    private readonly api: ChatApi,
    private readonly localStorage: LocalChatStorage,
    private readonly mediaStorage: LocalChatMediaStorage,
  ) {}

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

  /** 发送一条消息。 */
  async sendMessage(
    message: string,
    options?: { image?: string; useWebSocket?: boolean },
  ): Promise<Result<ChatSendResponse>> {
    return Result.wrap(async () => {
      const request = SendMessageRequest.from({
        message,
        image: options?.image ?? "",
        useWebSocket: options?.useWebSocket ?? false,
      });
      return await this.api.sendMessage(request);
    });
  }

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

  /** 解锁私密消息。 */
  async unlockPrivateMessage(
    messageId: string,
  ): Promise<Result<UnlockPrivateResponse>> {
    return Result.wrap(() =>
      this.api.unlockPrivateMessage(UnlockPrivateRequest.from({ messageId })),
    );
  }

  /** 一键解锁历史锁定消息。 */
  async unlockHistory(): Promise<Result<UnlockHistoryResponse>> {
    return Result.wrap(() => this.api.unlockHistory());
  }

  /** 把本地缓存中的私密消息标记为已解锁。 */
  async markPrivateMessageUnlockedInLocal(
    messageId: string,
    content: string,
  ): Promise<Result<void>> {
    return Result.wrap(async () => {
      const localResult = await this.getLocalMessages();
      if (Result.isErr(localResult)) {
        throw localResult.error;
      }

      let changed = false;
      const updatedMessages = localResult.data.map((message) => {
        if (message.id !== messageId) return message;
        changed = true;
        return ChatMessage.from({
          ...message.toJson(),
          content,
          lockDetail: {
            ...message.lockDetail,
            locked: false,
            showContent: true,
            showUpgrade: false,
            hint: null,
          },
        });
      });

      if (!changed) return;

      const saveResult = await this.saveMessagesToLocal(updatedMessages);
      if (Result.isErr(saveResult)) {
        throw saveResult.error;
      }
    });
  }

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

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

  /**
   * 批量覆盖写入：先清空本地存储，再写入新列表。
   * 保留 Dart 端的「先清后写」语义（用于同步场景的整批刷新）。
   */
  async saveMessagesToLocal(
    messages: readonly ChatMessage[],
  ): Promise<Result<void>> {
    const cleared = await this.localStorage.clearAll();
    if (!cleared.success) {
      return Result.err(cleared.error);
    }
    return this._mapLocalStorageResult(
      this.localStorage.saveMessages(messages.map((m) => this._chatToLocal(m))),
    );
  }

  /** 读取所有本地消息，按 dbId 升序。 */
  async getLocalMessages(): Promise<Result<ChatMessage[]>> {
    const result = await this.localStorage.getAllMessages();
    if (!result.success) {
      return Result.err(result.error);
    }
    return Result.ok(result.data.map((lm) => this._localToChat(lm)));
  }

  /** 清空本地消息。 */
  async clearLocalMessages(): Promise<Result<void>> {
    return this._mapLocalStorageResult(this.localStorage.clearAll());
  }

  /**
   * 获取本地消息数量。
   * 替代 Dart 的同步 `int get localMessageCount`——Dexie 异步 API 决定必须 async。
   */
  async getLocalMessageCount(): Promise<Result<number>> {
    return this._mapLocalStorageResult(this.localStorage.getMessageCount());
  }

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

  async getCachedMedia(
    input: ChatMediaLookupInput,
  ): Promise<Result<LocalChatMediaRow | null>> {
    return Result.wrap(async () => {
      const ownerKeys = await this._resolveMediaOwnerKeys();
      for (const ownerKey of ownerKeys) {
        const cacheKey = this._buildMediaCacheKey(ownerKey, input);
        const result = await this.mediaStorage.getMedia(cacheKey);
        if (Result.isErr(result)) throw result.error;
        if (result.data) return result.data;
      }
      return null;
    });
  }

  async cacheRemoteMedia(
    input: CacheRemoteChatMediaInput,
  ): Promise<Result<LocalChatMediaRow>> {
    return Result.wrap(async () => {
      if (!isCacheableRemoteChatMediaUrl(input.remoteUrl)) {
        throw new Error(`Unsupported media url: ${input.remoteUrl}`);
      }

      const ownerKeys = await this._resolveMediaOwnerKeys();
      const ownerKey = ownerKeys[0] ?? "anonymous";
      const cacheKey = this._buildMediaCacheKey(ownerKey, input);
      for (const lookupOwnerKey of ownerKeys) {
        const lookupCacheKey = this._buildMediaCacheKey(lookupOwnerKey, input);
        const existingResult = await this.mediaStorage.getMedia(lookupCacheKey);
        if (Result.isErr(existingResult)) throw existingResult.error;
        if (existingResult.data) return existingResult.data;
      }

      const response = await fetch(input.remoteUrl, {
        method: "GET",
        cache: "force-cache",
      });
      if (!response.ok) {
        throw new Error(
          `Media download failed: ${response.status} ${response.statusText}`,
        );
      }

      const blob = await response.blob();
      if (blob.size <= 0) {
        throw new Error("Media download returned an empty blob.");
      }

      const saveResult = await this.mediaStorage.saveMedia({
        cacheKey,
        ownerKey,
        messageId: input.messageId,
        kind: input.kind,
        remoteUrl: input.remoteUrl,
        blob,
        mimeType:
          blob.type ||
          response.headers.get("content-type") ||
          fallbackChatMediaMimeType(input.kind),
      });
      if (Result.isErr(saveResult)) throw saveResult.error;
      void requestBrowserPersistentStorageOnce();

      const savedResult = await this.mediaStorage.getMedia(cacheKey);
      if (Result.isErr(savedResult)) throw savedResult.error;
      if (!savedResult.data) {
        throw new Error("Cached media could not be read after save.");
      }
      return savedResult.data;
    });
  }

  async prefetchMediaForMessages(
    messages: readonly ChatMessage[],
  ): Promise<Result<void>> {
    return this._prefetchMediaTargets(
      messages.flatMap((message) => getMessageMediaTargets(message)),
    );
  }

  async prefetchMediaForSendResponse(
    response: ChatSendResponse,
  ): Promise<Result<void>> {
    return this._prefetchMediaTargets(getSendResponseMediaTargets(response));
  }

  // ============ 私有助手 ============

  /** ChatMessage → LocalMessage：丢弃 wire 字段，注入 storage 层的 sessionId=""。 */
  private _chatToLocal(message: ChatMessage): LocalMessage {
    return LocalMessage.from({
      id: message.id,
      role: message.role,
      type: message.type,
      content: message.content,
      createdAt: message.createdAt,
      audioUrl: message.audioUrl,
      image: message.image,
      lockDetail: message.lockDetail,
      sessionId: "",
    });
  }

  /** LocalMessage → ChatMessage：丢弃 storage 层的 sessionId。 */
  private _localToChat(local: LocalMessage): ChatMessage {
    return ChatMessage.from({
      id: local.id,
      role: local.role,
      type: local.type,
      content: local.content,
      createdAt: local.createdAt,
      audioUrl: local.audioUrl,
      image: local.image,
      lockDetail: local.lockDetail,
    });
  }

  /**
   * 把 storage 层 `Result<T>` 透传：仓库与 storage 现在共享同一全局
   * `Result<T>`（来自 `@/utils/result`），不需要任何形状转换。
   * 该方法保留仅为未来万一又出现差异化时方便快速插入适配。
   */
  private async _mapLocalStorageResult<T>(
    promise: Promise<Result<T>>,
  ): Promise<Result<T>> {
    return await promise;
  }

  private async _prefetchMediaTargets(
    targets: readonly CacheRemoteChatMediaInput[],
  ): Promise<Result<void>> {
    return Result.wrap(async () => {
      for (const target of uniqueMediaTargets(targets)) {
        const result = await this.cacheRemoteMedia(target);
        if (Result.isErr(result)) {
          log.warn("[chat-media] prefetch failed", {
            messageId: target.messageId,
            kind: target.kind,
            remoteUrl: target.remoteUrl,
            error: result.error,
          });
        }
      }
    });
  }

  private async _resolveMediaOwnerKeys(): Promise<string[]> {
    const ownerKeys: string[] = [];
    const userIdResult = await UserStorage.getInstance().getUserId();
    if (
      Result.isOk(userIdResult) &&
      userIdResult.data !== null &&
      userIdResult.data.length > 0
    ) {
      ownerKeys.push(`user:${userIdResult.data}`);
    }

    const deviceIdResult = await AuthStorage.getInstance().getDeviceId();
    if (
      Result.isOk(deviceIdResult) &&
      deviceIdResult.data !== null &&
      deviceIdResult.data.length > 0
    ) {
      ownerKeys.push(`device:${deviceIdResult.data}`);
    }

    ownerKeys.push("anonymous");
    return Array.from(new Set(ownerKeys));
  }

  private _buildMediaCacheKey(
    ownerKey: string,
    input: ChatMediaLookupInput,
  ): string {
    return buildChatMediaCacheKey({
      ownerKey,
      messageId: input.messageId,
      kind: input.kind,
    });
  }
}

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