"use client";

import { useEffect, useState } from "react";

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

import {
  cacheRemoteChatMediaBlob,
  resolveCachedChatMediaBlob,
} from "./chat_media_cache_client";
import { isCacheableRemoteChatMediaUrl } from "./chat_media_url";

export interface UseCachedChatMediaUrlInput {
  messageId?: string | null;
  remoteUrl?: string | null;
  kind: ChatMediaKind;
}

export interface UseCachedChatMediaUrlOutput {
  mediaUrl: string;
  isUsingCachedMedia: boolean;
}

interface CachedMediaUrlState {
  key: string;
  objectUrl: string;
}

export function useCachedChatMediaUrl({
  messageId,
  remoteUrl,
  kind,
}: UseCachedChatMediaUrlInput): UseCachedChatMediaUrlOutput {
  const [cachedState, setCachedState] = useState<CachedMediaUrlState | null>(
    null,
  );
  const fallbackUrl = remoteUrl ?? "";
  const stateKey =
    messageId && remoteUrl ? `${messageId}:${kind}:${remoteUrl}` : "";

  useEffect(() => {
    if (
      !messageId ||
      !remoteUrl ||
      !isCacheableRemoteChatMediaUrl(remoteUrl)
    ) {
      return;
    }

    let cancelled = false;
    let objectUrl: string | null = null;

    const applyBlob = (blob: Blob) => {
      const nextObjectUrl = URL.createObjectURL(blob);
      objectUrl = nextObjectUrl;
      if (cancelled) {
        URL.revokeObjectURL(nextObjectUrl);
        return;
      }
      setCachedState((previous) => {
        if (previous) URL.revokeObjectURL(previous.objectUrl);
        return {
          key: stateKey,
          objectUrl: nextObjectUrl,
        };
      });
    };

    const resolveCachedMedia = async () => {
      const cachedResult = await resolveCachedChatMediaBlob({
        messageId,
        kind,
      });
      if (cancelled) return;

      if (Result.isOk(cachedResult) && cachedResult.data) {
        applyBlob(cachedResult.data);
        return;
      }

      const cacheResult = await cacheRemoteChatMediaBlob({
        messageId,
        kind,
        remoteUrl,
      });
      if (cancelled) return;

      if (Result.isOk(cacheResult)) {
        applyBlob(cacheResult.data);
      }
    };

    void resolveCachedMedia();

    return () => {
      cancelled = true;
      if (objectUrl) URL.revokeObjectURL(objectUrl);
    };
  }, [kind, messageId, remoteUrl, stateKey]);

  if (cachedState?.key === stateKey) {
    return {
      mediaUrl: cachedState.objectUrl,
      isUsingCachedMedia: true,
    };
  }

  return {
    mediaUrl: fallbackUrl,
    isUsingCachedMedia: false,
  };
}
