import {Asset, MediaEntity, Message} from '@modules/chats/interfaces';
import {LOCAL_INITIAL_VERSION} from '@modules/chats/constants';
import {Storage} from '@services/mmkvFactory';
import {sentryLogs} from '@shared/utils';

class PendingAssetsClass {
  private persistor: Storage;
  private assets: Record<string, MediaEntity[]> = {};
  private isInitialized = false;

  constructor() {
    this.persistor = new Storage({id: 'CHATS_PENDING_ASSETS'});
  }

  private initialize() {
    if (this.isInitialized) {
      return;
    }
    this.isInitialized = true;
    const keys = this.persistor.getAllKeys();
    for (const key of keys) {
      const value = this.persistor.getString(key);
      if (value) {
        this.assets[key] = JSON.parse(value);
      }
    }
  }

  public add(messageId: Nullable<string>, assets: MediaEntity[]): void {
    if (!messageId) {
      sentryLogs.warn({
        message: 'Skipping pending assets add without message id',
        module: 'CHATS',
        component: 'PendingAssets.add',
      });
      return;
    }

    this.initialize();
    this.assets[messageId] = assets;
    this.persistor.set(messageId, JSON.stringify(assets));
  }

  public removeById(messageId: Nullable<string>): void {
    if (!messageId) {
      sentryLogs.warn({
        message: 'Skipping pending assets remove without message id',
        module: 'CHATS',
        component: 'PendingAssets.removeById',
      });
      return;
    }

    this.initialize();
    delete this.assets[messageId];
    this.persistor.remove(messageId);
  }

  public has(messageId: string): boolean {
    this.initialize();
    return !!this.assets[messageId]?.length;
  }

  public get(messageId: string): MediaEntity[] | undefined {
    this.initialize();
    return this.assets[messageId] || undefined;
  }

  /**
   * Adds or removes the pending assets from the message
   * @param message - Message object to add or remove the pending assets from
   */
  public addOrRemoveFromMessage(message: Message): void {
    if (!message.client_msg_id) {
      sentryLogs.warn({
        message: 'Skipping pending assets update without message id',
        module: 'CHATS',
        component: 'PendingAssets.addOrRemoveFromMessage',
        data: {
          channel: message.channel,
          ts: message.ts,
          version: message.version,
          hasAttachments: !!message.attachments?.length,
          hasPendingAssets: !!message.pendingAssets?.length,
        },
      });
      return;
    }

    const hasAttachments = message.attachments?.length;

    if (hasAttachments) {
      this.removeById(message.client_msg_id);
    } else if (
      message.version === LOCAL_INITIAL_VERSION &&
      message.pendingAssets?.length &&
      !this.has(message.client_msg_id)
    ) {
      this.add(message.client_msg_id, message.pendingAssets);
    }
  }

  /**
   * Sets the pending assets for a message if the message has pending assets
   * @param message - Message object to check if it has pending assets
   * @returns Message object with the pending assets set
   */
  public setMessage<
    T extends {
      version: number;
      attachments?: Asset[];
      client_msg_id: string;
    },
  >(message: T): T & {pendingAssets?: MediaEntity[]} {
    if (
      message.version > LOCAL_INITIAL_VERSION ||
      message.attachments?.length
    ) {
      return message;
    }

    return {
      ...message,
      pendingAssets: this.get(message.client_msg_id) || undefined,
    };
  }
}

export const PendingAssets = new PendingAssetsClass();
