import { env } from "@/env/server"
import * as api from "@/lib/api"
import { notFound } from "@/server/errors/trpc"
import {
  contentStackBaseWithProtectedProcedure,
  contentStackBaseWithServiceProcedure,
  router,
} from "@/server/trpc"

import { getAllLoyaltyLevels, getLoyaltyLevel } from "../loyaltyLevel/query"
import {
  rewardsAllInput,
  rewardsByLevelInput,
  rewardsCurrentInput,
  rewardsUpdateInput,
} from "./input"
import {
  Reward,
  SurpriseReward,
  validateApiRewardSchema,
  validateCategorizedRewardsSchema,
} from "./output"
import {
  getAllCachedApiRewards,
  getAllRewardCounter,
  getAllRewardFailCounter,
  getAllRewardSuccessCounter,
  getByLevelRewardCounter,
  getByLevelRewardFailCounter,
  getByLevelRewardSuccessCounter,
  getCachedAllTierRewards,
  getCmsRewards,
  getCurrentRewardCounter,
  getCurrentRewardFailCounter,
  getCurrentRewardSuccessCounter,
  getUniqueRewardIds,
} from "./utils"

import { Surprise } from "@/types/components/blocks/surprises"

const ONE_HOUR = 60 * 60

export const rewardQueryRouter = router({
  all: contentStackBaseWithServiceProcedure
    .input(rewardsAllInput)
    .query(async function ({ input, ctx }) {
      getAllRewardCounter.add(1)

      const allApiRewards = env.USE_NEW_REWARDS_ENDPOINT
        ? await getCachedAllTierRewards(ctx.serviceToken)
        : await getAllCachedApiRewards(ctx.serviceToken)

      if (!allApiRewards) {
        return []
      }

      const rewardIds = Object.values(allApiRewards)
        .flatMap((level) => level.map((reward) => reward?.rewardId))
        .filter((id): id is string => Boolean(id))

      const contentStackRewards = await getCmsRewards(
        ctx.lang,
        getUniqueRewardIds(rewardIds)
      )

      if (!contentStackRewards) {
        return []
      }

      const loyaltyLevelsConfig = await getAllLoyaltyLevels(ctx)
      const levelsWithRewards = Object.entries(allApiRewards).map(
        ([level, rewards]) => {
          const combinedRewards = rewards
            .filter((r) => (input.unique ? r?.rewardTierLevel === level : true))
            .map((reward) => {
              const contentStackReward = contentStackRewards.find((r) => {
                return r.reward_id === reward?.rewardId
              })

              if (contentStackReward) {
                return contentStackReward
              } else {
                console.error("No contentStackReward found", reward?.rewardId)
              }
            })
            .filter((reward): reward is Reward => Boolean(reward))

          const levelConfig = loyaltyLevelsConfig.find(
            (l) => l.level_id === level
          )

          if (!levelConfig) {
            getAllRewardFailCounter.add(1)

            console.error("contentstack.loyaltyLevels level not found")
            throw notFound()
          }
          return { ...levelConfig, rewards: combinedRewards }
        }
      )

      getAllRewardSuccessCounter.add(1)
      return levelsWithRewards
    }),
  byLevel: contentStackBaseWithServiceProcedure
    .input(rewardsByLevelInput)
    .query(async function ({ input, ctx }) {
      getByLevelRewardCounter.add(1)
      const { level_id } = input

      const allUpcomingApiRewards = env.USE_NEW_REWARDS_ENDPOINT
        ? await getCachedAllTierRewards(ctx.serviceToken)
        : await getAllCachedApiRewards(ctx.serviceToken)

      if (!allUpcomingApiRewards || !allUpcomingApiRewards[level_id]) {
        getByLevelRewardFailCounter.add(1)

        return null
      }

      let apiRewards = allUpcomingApiRewards[level_id]!

      if (input.unique) {
        apiRewards = allUpcomingApiRewards[level_id]!.filter(
          (reward) => reward?.rewardTierLevel === level_id
        )
      }

      const rewardIds = apiRewards
        .map((reward) => reward?.rewardId)
        .filter((id): id is string => Boolean(id))

      const contentStackRewards = await getCmsRewards(ctx.lang, rewardIds)
      if (!contentStackRewards) {
        return null
      }

      const loyaltyLevelsConfig = await getLoyaltyLevel(ctx, input.level_id)

      const levelsWithRewards = apiRewards
        .map((reward) => {
          const contentStackReward = contentStackRewards.find((r) => {
            return r.reward_id === reward?.rewardId
          })

          if (contentStackReward) {
            return contentStackReward
          } else {
            console.info("No contentStackReward found", reward?.rewardId)
          }
        })
        .filter((reward): reward is Reward => Boolean(reward))

      getByLevelRewardSuccessCounter.add(1)
      return { level: loyaltyLevelsConfig, rewards: levelsWithRewards }
    }),
  current: contentStackBaseWithProtectedProcedure
    .input(rewardsCurrentInput)
    .query(async function ({ input, ctx }) {
      getCurrentRewardCounter.add(1)

      const { limit, cursor } = input

      const isNewEndpoint = env.USE_NEW_REWARDS_ENDPOINT
      const endpoint = isNewEndpoint
        ? api.endpoints.v1.Profile.Reward.reward
        : api.endpoints.v1.Profile.reward

      const apiResponse = await api.get(endpoint, {
        cache: undefined, // override defaultOptions
        headers: {
          Authorization: `Bearer ${ctx.session.token.access_token}`,
        },
        next: { revalidate: ONE_HOUR },
      })

      if (!apiResponse.ok) {
        const text = await apiResponse.text()
        getCurrentRewardFailCounter.add(1, {
          error_type: "http_error",
          error: JSON.stringify({
            status: apiResponse.status,
            statusText: apiResponse.statusText,
            text,
          }),
        })
        console.error(
          "api.reward error ",
          JSON.stringify({
            error: {
              status: apiResponse.status,
              statusText: apiResponse.statusText,
              text,
            },
          })
        )
        return null
      }

      const data = await apiResponse.json()
      const validatedApiRewards = isNewEndpoint
        ? validateCategorizedRewardsSchema.safeParse(data)
        : validateApiRewardSchema.safeParse(data)

      if (!validatedApiRewards.success) {
        getCurrentRewardFailCounter.add(1, {
          locale: ctx.lang,
          error_type: "validation_error",
          error: JSON.stringify(validatedApiRewards.error),
        })
        console.error(validatedApiRewards.error)
        console.error(
          "contentstack.rewards validation error",
          JSON.stringify({
            query: { locale: ctx.lang },
            error: validatedApiRewards.error,
          })
        )
        return null
      }

      const rewardIds = validatedApiRewards.data
        .map((reward) => reward?.rewardId)
        .filter((rewardId): rewardId is string => !!rewardId)
        .sort()

      const slicedData = rewardIds.slice(cursor, limit + cursor)

      const cmsRewards = await getCmsRewards(ctx.lang, slicedData)

      if (!cmsRewards) {
        return null
      }

      const nextCursor =
        limit + cursor < rewardIds.length ? limit + cursor : undefined

      const surprisesIds = validatedApiRewards.data
        .filter(
          ({ type, rewardType }) =>
            type === "coupon" && rewardType === "Surprise"
        )
        .map(({ rewardId }) => rewardId)

      const rewards = cmsRewards.filter(
        (reward) => !surprisesIds.includes(reward.reward_id)
      )

      getCurrentRewardSuccessCounter.add(1)

      return {
        rewards,
        nextCursor,
      }
    }),
  surprises: contentStackBaseWithProtectedProcedure.query(async ({ ctx }) => {
    getCurrentRewardCounter.add(1)

    const isNewEndpoint = env.USE_NEW_REWARDS_ENDPOINT
    const endpoint = isNewEndpoint
      ? api.endpoints.v1.Profile.Reward.reward
      : api.endpoints.v1.Profile.reward

    const apiResponse = await api.get(endpoint, {
      cache: undefined,
      headers: {
        Authorization: `Bearer ${ctx.session.token.access_token}`,
      },
      next: { revalidate: ONE_HOUR },
    })

    if (!apiResponse.ok) {
      const text = await apiResponse.text()
      getCurrentRewardFailCounter.add(1, {
        error_type: "http_error",
        error: JSON.stringify({
          status: apiResponse.status,
          statusText: apiResponse.statusText,
          text,
        }),
      })
      console.error(
        "api.reward error ",
        JSON.stringify({
          error: {
            status: apiResponse.status,
            statusText: apiResponse.statusText,
            text,
          },
        })
      )
      return null
    }

    const data = await apiResponse.json()
    const validatedApiRewards = isNewEndpoint
      ? validateCategorizedRewardsSchema.safeParse(data)
      : validateApiRewardSchema.safeParse(data)

    if (!validatedApiRewards.success) {
      getCurrentRewardFailCounter.add(1, {
        locale: ctx.lang,
        error_type: "validation_error",
        error: JSON.stringify(validatedApiRewards.error),
      })
      console.error(validatedApiRewards.error)
      console.error(
        "contentstack.surprises validation error",
        JSON.stringify({
          query: { locale: ctx.lang },
          error: validatedApiRewards.error,
        })
      )
      return null
    }

    const rewardIds = validatedApiRewards.data
      .map((reward) => reward?.rewardId)
      .filter((rewardId): rewardId is string => !!rewardId)
      .sort()

    const cmsRewards = await getCmsRewards(ctx.lang, rewardIds)

    if (!cmsRewards) {
      return null
    }

    getCurrentRewardSuccessCounter.add(1)

    const surprises =
      validatedApiRewards.data
        .filter(
          (reward): reward is SurpriseReward =>
            reward?.type === "coupon" && reward?.rewardType === "Surprise"
        )
        .map((surprise) => {
          const reward = cmsRewards.find(
            ({ reward_id }) => surprise.rewardId === reward_id
          )

          if (!reward) {
            return null
          }

          return {
            ...reward,
            id: surprise.id,
            endsAt: surprise.endsAt,
          }
        })
        .filter((surprise): surprise is Surprise => !!surprise) ?? []

    return surprises
  }),
  update: contentStackBaseWithProtectedProcedure
    .input(rewardsUpdateInput)
    .mutation(async ({ input, ctx }) => {
      const response = await Promise.resolve({ ok: true })
      // const response = await api.post(api.endpoints.v1.rewards, {
      //   body: {
      //     ids: [input.id],
      //   },
      // })
      if (!response.ok) {
        return false
      }

      return true
    }),
})
