import * as api from "@/lib/api"
import {
  GetRewards as GetRewards,
  GetRewardsRef as GetRewardsRef,
} from "@/lib/graphql/Query/RewardsWithRedeem.graphql"
import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"
import { createCounter } from "@/server/telemetry"

import { getCacheClient } from "@/services/dataCache"
import {
  generateLoyaltyConfigTag,
  generateRefsResponseTag,
} from "@/utils/generateTag"

import {
  rewardRefsSchema,
  validateApiAllTiersSchema,
  validateCmsRewardsSchema,
} from "./output"

import type {
  CMSRewardsResponse,
  GetRewardRefsSchema,
} from "@/types/trpc/routers/contentstack/reward"
import type { Lang } from "@/constants/languages"

export function getUniqueRewardIds(rewardIds: string[]) {
  const uniqueRewardIds = new Set(rewardIds)
  return Array.from(uniqueRewardIds)
}

/**
 * Cached for 1 hour.
 */
export async function getCachedAllTierRewards(token: string) {
  const cacheClient = await getCacheClient()

  return await cacheClient.cacheOrGet(
    "getAllTierRewards",
    async () => {
      const getApiRewardAllTiersCounter = createCounter(
        "trpc.api",
        "reward.allTiers"
      )
      const metricsGetApiRewardAllTiers = getApiRewardAllTiersCounter.init()

      metricsGetApiRewardAllTiers.start()

      const apiResponse = await api.get(
        api.endpoints.v1.Profile.Reward.allTiers,
        {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }
      )

      if (!apiResponse.ok) {
        metricsGetApiRewardAllTiers.httpError(apiResponse)
        throw apiResponse
      }

      const data = await apiResponse.json()
      const validatedApiAllTierRewards =
        validateApiAllTiersSchema.safeParse(data)

      if (!validatedApiAllTierRewards.success) {
        metricsGetApiRewardAllTiers.validationError(
          validatedApiAllTierRewards.error
        )
        throw validatedApiAllTierRewards.error
      }

      metricsGetApiRewardAllTiers.success()

      return validatedApiAllTierRewards.data
    },
    "1h"
  )
}

export async function getCmsRewards(lang: Lang, rewardIds: string[]) {
  if (!rewardIds.length) {
    return null
  }

  const tags = rewardIds.map((id) =>
    generateLoyaltyConfigTag(lang, "reward", id)
  )

  const getContentstackRewardAllRefsCounter = createCounter(
    "trpc.contentstack",
    "reward.all.refs"
  )
  const metricsGetContentstackRewardAllRefs =
    getContentstackRewardAllRefsCounter.init({ lang, rewardIds })

  metricsGetContentstackRewardAllRefs.start()

  const refsResponse = await request<GetRewardRefsSchema>(
    GetRewardsRef,
    {
      locale: lang,
      rewardIds,
    },
    {
      key: rewardIds.map((rewardId) => generateRefsResponseTag(lang, rewardId)),
      ttl: "max",
    }
  )

  if (!refsResponse.data) {
    const notFoundError = notFound(refsResponse)
    metricsGetContentstackRewardAllRefs.noDataError()
    throw notFoundError
  }

  const validatedRefsData = rewardRefsSchema.safeParse(refsResponse)

  if (!validatedRefsData.success) {
    metricsGetContentstackRewardAllRefs.validationError(validatedRefsData.error)
    return null
  }

  metricsGetContentstackRewardAllRefs.success()

  const getContentstackRewardAllCounter = createCounter(
    "trpc.contentstack",
    "reward.all"
  )
  const metricsGetContentstackRewardAll = getContentstackRewardAllCounter.init({
    lang,
    rewardIds,
  })

  const cmsRewardsResponse = await request<CMSRewardsResponse>(
    GetRewards,
    {
      locale: lang,
      rewardIds,
    },
    {
      key: tags,
      ttl: "max",
    }
  )

  if (!cmsRewardsResponse.data) {
    const notFoundError = notFound(cmsRewardsResponse)
    metricsGetContentstackRewardAll.noDataError()
    throw notFoundError
  }

  const validatedCmsRewards =
    validateCmsRewardsSchema.safeParse(cmsRewardsResponse)

  if (!validatedCmsRewards.success) {
    metricsGetContentstackRewardAll.validationError(validatedCmsRewards.error)
    return null
  }

  metricsGetContentstackRewardAll.success()

  return validatedCmsRewards.data
}
