import { type CacheTime, type DataCache } from "@/services/dataCache/Cache"

import {
  type CacheOrGetOptions,
  shouldGetFromCache,
} from "../cacheOrGetOptions"
import { cacheLogger } from "../logger"
import { generateCacheKey } from "./generateCacheKey"
import { get } from "./get"
import { set } from "./set"

export const cacheOrGet: DataCache["cacheOrGet"] = async <T>(
  key: string | string[],
  callback: (overrideTTL: (cacheTime: CacheTime) => void) => Promise<T>,
  ttl: CacheTime,
  opts?: CacheOrGetOptions
) => {
  const cacheKey = generateCacheKey(key, {
    includeGitHashInKey: opts?.includeGitHashInKey ?? true,
  })

  let cachedValue: Awaited<T> | undefined = undefined
  if (shouldGetFromCache(opts)) {
    cachedValue = await get<T>(cacheKey)
  }

  let realTTL = ttl

  const overrideTTL = function (cacheTime: CacheTime) {
    realTTL = cacheTime
  }

  if (!cachedValue) {
    const perf = performance.now()
    const data = await callback(overrideTTL)

    const size = JSON.stringify(data).length / (1024 * 1024)
    if (size >= 5) {
      cacheLogger.warn(`'${key}' is larger than 5MB!`)
    }
    cacheLogger.debug(
      `Fetching data took ${(performance.now() - perf).toFixed(2)}ms ${size.toFixed(4)}MB for '${key}'`
    )

    await set<T>(cacheKey, data, realTTL)
    return data
  }

  return cachedValue
}
