import { metrics } from "@opentelemetry/api"
import { unstable_cache } from "next/cache"

import { Lang } from "@/constants/languages"
import * as api from "@/lib/api"
import { GetRewards } from "@/lib/graphql/Query/Rewards.graphql"
import { request } from "@/lib/graphql/request"
import { notFound } from "@/server/errors/trpc"

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

import {
  CmsRewardsResponse,
  validateApiAllTiersSchema,
  validateApiTierRewardsSchema,
  validateCmsRewardsSchema,
} from "./output"

const meter = metrics.getMeter("trpc.reward")
export const getAllRewardCounter = meter.createCounter(
  "trpc.contentstack.reward.all"
)
export const getAllRewardFailCounter = meter.createCounter(
  "trpc.contentstack.reward.all-fail"
)
export const getAllRewardSuccessCounter = meter.createCounter(
  "trpc.contentstack.reward.all-success"
)
export const getCurrentRewardCounter = meter.createCounter(
  "trpc.contentstack.reward.current"
)
export const getCurrentRewardFailCounter = meter.createCounter(
  "trpc.contentstack.reward.current-fail"
)
export const getCurrentRewardSuccessCounter = meter.createCounter(
  "trpc.contentstack.reward.current-success"
)
export const getByLevelRewardCounter = meter.createCounter(
  "trpc.contentstack.reward.byLevel"
)
export const getByLevelRewardFailCounter = meter.createCounter(
  "trpc.contentstack.reward.byLevel-fail"
)
export const getByLevelRewardSuccessCounter = meter.createCounter(
  "trpc.contentstack.reward.byLevel-success"
)

const ONE_HOUR = 60 * 60

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

/**
 * Uses the legacy profile/v1/Profile/tierRewards endpoint.
 * TODO: Delete when the new endpoint is out in production.
 */
export const getAllCachedApiRewards = unstable_cache(
  async function (token) {
    const apiResponse = await api.get(api.endpoints.v1.Profile.tierRewards, {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    })

    if (!apiResponse.ok) {
      const text = await apiResponse.text()
      getAllRewardFailCounter.add(1, {
        error_type: "http_error",
        error: JSON.stringify({
          status: apiResponse.status,
          statusText: apiResponse.statusText,
          text,
        }),
      })
      console.error(
        "api.rewards.tierRewards error ",
        JSON.stringify({
          error: {
            status: apiResponse.status,
            statusText: apiResponse.statusText,
            text,
          },
        })
      )

      throw apiResponse
    }

    const data = await apiResponse.json()
    const validatedApiTierRewards = validateApiTierRewardsSchema.safeParse(data)

    if (!validatedApiTierRewards.success) {
      getAllRewardFailCounter.add(1, {
        error_type: "validation_error",
        error: JSON.stringify(validatedApiTierRewards.error),
      })
      console.error(validatedApiTierRewards.error)
      console.error(
        "api.rewards validation error",
        JSON.stringify({
          error: validatedApiTierRewards.error,
        })
      )
      throw validatedApiTierRewards.error
    }

    return validatedApiTierRewards.data
  },
  ["getAllApiRewards"],
  { revalidate: ONE_HOUR }
)

/**
 * Cached for 1 hour.
 */
export const getCachedAllTierRewards = unstable_cache(
  async function (token) {
    const apiResponse = await api.get(
      api.endpoints.v1.Profile.Reward.allTiers,
      {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      }
    )

    if (!apiResponse.ok) {
      const text = await apiResponse.text()
      getAllRewardFailCounter.add(1, {
        error_type: "http_error",
        error: JSON.stringify({
          status: apiResponse.status,
          statusText: apiResponse.statusText,
          text,
        }),
      })
      console.error(
        "api.rewards.allTiers error ",
        JSON.stringify({
          error: {
            status: apiResponse.status,
            statusText: apiResponse.statusText,
            text,
          },
        })
      )

      throw apiResponse
    }

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

    if (!validatedApiAllTierRewards.success) {
      getAllRewardFailCounter.add(1, {
        error_type: "validation_error",
        error: JSON.stringify(validatedApiAllTierRewards.error),
      })
      console.error(validatedApiAllTierRewards.error)
      console.error(
        "api.rewards validation error",
        JSON.stringify({
          error: validatedApiAllTierRewards.error,
        })
      )
      throw validatedApiAllTierRewards.error
    }

    return validatedApiAllTierRewards.data
  },
  ["getApiAllTierRewards"],
  { revalidate: ONE_HOUR }
)

export async function getCmsRewards(locale: Lang, rewardIds: string[]) {
  const tags = rewardIds.map((id) =>
    generateLoyaltyConfigTag(locale, "reward", id)
  )
  const cmsRewardsResponse = await request<CmsRewardsResponse>(
    GetRewards,
    {
      locale: locale,
      rewardIds,
    },
    { next: { tags }, cache: "force-cache" }
  )

  if (!cmsRewardsResponse.data) {
    getAllRewardFailCounter.add(1, {
      lang: locale,
      error_type: "validation_error",
      error: JSON.stringify(cmsRewardsResponse.data),
    })
    const notFoundError = notFound(cmsRewardsResponse)
    console.error(
      "contentstack.rewards not found error",
      JSON.stringify({
        query: {
          locale,
          rewardIds,
        },
        error: { code: notFoundError.code },
      })
    )
    throw notFoundError
  }

  const validatedCmsRewards =
    validateCmsRewardsSchema.safeParse(cmsRewardsResponse)

  if (!validatedCmsRewards.success) {
    getAllRewardFailCounter.add(1, {
      locale,
      rewardIds,
      error_type: "validation_error",
      error: JSON.stringify(validatedCmsRewards.error),
    })
    console.error(validatedCmsRewards.error)
    console.error(
      "contentstack.rewards validation error",
      JSON.stringify({
        query: { locale, rewardIds },
        error: validatedCmsRewards.error,
      })
    )
    return null
  }

  return validatedCmsRewards.data
}
