import { cache } from "react"

import {
  type MembershipLevel,
  MembershipLevelEnum,
} from "@/constants/membershipLevels"
import {
  GetAllLoyaltyLevels,
  GetLoyaltyLevel,
} from "@/lib/graphql/Query/LoyaltyLevels.graphql"
import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"
import { createCounter } from "@/server/telemetry"
import { contentstackBaseProcedure, router } from "@/server/trpc"

import { generateLoyaltyConfigTag } from "@/utils/generateTag"

import { loyaltyLevelInput } from "./input"
import {
  type LoyaltyLevel,
  type LoyaltyLevelsResponse,
  validateLoyaltyLevelsSchema,
} from "./output"

import type { Context } from "@/server/context"

export const getAllLoyaltyLevels = cache(async (ctx: Context) => {
  const getLoyaltyLevelAllCounter = createCounter(
    "trpc.contentstack",
    "loyaltyLevel.all"
  )
  const metricsGetLoyaltyLevelAll = getLoyaltyLevelAllCounter.init()

  metricsGetLoyaltyLevelAll.start()

  // Ideally we should fetch all available tiers from API, but since they
  // are static, we can just use the enum values. We want to know which
  // levels we are fetching so that we can use tags to cache them
  const allLevelIds = Object.values(MembershipLevelEnum)

  const tags = allLevelIds.map((levelId) =>
    generateLoyaltyConfigTag(ctx.lang, "loyalty_level", levelId)
  )

  const loyaltyLevelsConfigResponse = await request<LoyaltyLevelsResponse>(
    GetAllLoyaltyLevels,
    { lang: ctx.lang, level_ids: allLevelIds },
    { key: tags, ttl: "max" }
  )

  if (!loyaltyLevelsConfigResponse.data) {
    const notFoundError = notFound(loyaltyLevelsConfigResponse)
    metricsGetLoyaltyLevelAll.noDataError()
    throw notFoundError
  }

  const validatedLoyaltyLevels = validateLoyaltyLevelsSchema.safeParse(
    loyaltyLevelsConfigResponse.data
  )
  if (!validatedLoyaltyLevels.success) {
    metricsGetLoyaltyLevelAll.validationError(validatedLoyaltyLevels.error)
    return []
  }

  metricsGetLoyaltyLevelAll.success()

  return validatedLoyaltyLevels.data
})

export const getLoyaltyLevel = cache(
  async (ctx: Context, level_id: MembershipLevel) => {
    const getLoyaltyLevelCounter = createCounter(
      "trpc.contentstack",
      "loyaltyLevel.get"
    )
    const metricsGetLoyaltyLevel = getLoyaltyLevelCounter.init({
      lang: ctx.lang,
      level_id,
    })

    metricsGetLoyaltyLevel.start()

    const loyaltyLevelsConfigResponse = await request<LoyaltyLevelsResponse>(
      GetLoyaltyLevel,
      { lang: ctx.lang, level_id },
      {
        key: generateLoyaltyConfigTag(ctx.lang, "loyalty_level", level_id),
        ttl: "max",
      }
    )
    if (
      !loyaltyLevelsConfigResponse.data ||
      !loyaltyLevelsConfigResponse.data.all_loyalty_level.items.length
    ) {
      const notFoundError = notFound(loyaltyLevelsConfigResponse)
      metricsGetLoyaltyLevel.noDataError()
      throw notFoundError
    }

    const validatedLoyaltyLevels = validateLoyaltyLevelsSchema.safeParse(
      loyaltyLevelsConfigResponse.data
    )
    if (!validatedLoyaltyLevels.success) {
      metricsGetLoyaltyLevel.validationError(validatedLoyaltyLevels.error)
      return null
    }

    metricsGetLoyaltyLevel.success()

    const result: LoyaltyLevel = validatedLoyaltyLevels.data[0]
    return result
  }
)

export const loyaltyLevelQueryRouter = router({
  byLevel: contentstackBaseProcedure
    .input(loyaltyLevelInput)
    .query(async function ({ ctx, input }) {
      return getLoyaltyLevel(ctx, input.level)
    }),
  all: contentstackBaseProcedure.query(async function ({ ctx }) {
    return getAllLoyaltyLevels(ctx)
  }),
})
