import { cache } from "react"

import { GetContactConfig } from "@/lib/graphql/Query/ContactConfig.graphql"
import {
  GetCurrentFooter,
  GetCurrentFooterRef,
} from "@/lib/graphql/Query/Current/Footer.graphql"
import {
  GetCurrentHeader,
  GetCurrentHeaderRef,
} from "@/lib/graphql/Query/Current/Header.graphql"
import { GetFooter, GetFooterRef } from "@/lib/graphql/Query/Footer.graphql"
import { GetHeader, GetHeaderRef } from "@/lib/graphql/Query/Header.graphql"
import {
  GetSiteConfig,
  GetSiteConfigRef,
} from "@/lib/graphql/Query/SiteConfig.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 { langInput } from "@/server/utils"

import {
  generateRefsResponseTag,
  generateTag,
  generateTags,
  generateTagsFromSystem,
} from "@/utils/generateTag"

import {
  type ContactConfigData,
  type CurrentFooterDataRaw,
  type CurrentFooterRefDataRaw,
  type CurrentHeaderRefDataRaw,
  type GetCurrentHeaderData,
  headerRefsSchema,
  headerSchema,
  siteConfigRefSchema,
  siteConfigSchema,
  validateContactConfigSchema,
  validateCurrentFooterConfigSchema,
  validateCurrentHeaderConfigSchema,
  validateFooterConfigSchema,
  validateFooterRefConfigSchema,
} from "./output"
import {
  getAlertPhoneContactData,
  getConnections,
  getFooterConnections,
  getSiteConfigConnections,
} from "./utils"

import type {
  FooterDataRaw,
  FooterRefDataRaw,
} from "@/types/components/footer/footer"
import type {
  GetHeader as GetHeaderData,
  GetHeaderRefs,
} from "@/types/trpc/routers/contentstack/header"
import type {
  GetSiteConfigData,
  GetSiteConfigRefData,
} from "@/types/trpc/routers/contentstack/siteConfig"
import type { Lang } from "@/constants/languages"

const getContactConfig = cache(async (lang: Lang) => {
  const getContactConfigCounter = createCounter(
    "trpc.contentstack",
    "contactConfig.get"
  )
  const metricsGetContactConfig = getContactConfigCounter.init({ lang })

  metricsGetContactConfig.start()

  const response = await request<ContactConfigData>(
    GetContactConfig,
    {
      locale: lang,
    },
    {
      key: `${lang}:contact`,
      ttl: "max",
    }
  )

  if (!response.data) {
    const notFoundError = notFound(response)
    metricsGetContactConfig.noDataError()
    throw notFoundError
  }

  const verifiedData = validateContactConfigSchema.safeParse(response.data)

  if (!verifiedData.success) {
    metricsGetContactConfig.validationError(verifiedData.error)
    return null
  }

  metricsGetContactConfig.success()

  return verifiedData.data.all_contact_config.items[0]
})

export const baseQueryRouter = router({
  contact: contentstackBaseProcedure.query(async ({ ctx }) => {
    return await getContactConfig(ctx.lang)
  }),
  header: contentstackBaseProcedure.query(async ({ ctx }) => {
    const { lang } = ctx

    const getHeaderRefsCounter = createCounter(
      "trpc.contentstack",
      "header.get.refs"
    )
    const metricsGetHeaderRefs = getHeaderRefsCounter.init({ lang })

    metricsGetHeaderRefs.start()

    const responseRef = await request<GetHeaderRefs>(
      GetHeaderRef,
      {
        locale: lang,
      },
      {
        key: generateRefsResponseTag(lang, "header"),
        ttl: "max",
      }
    )

    if (!responseRef.data) {
      const notFoundError = notFound(responseRef)
      metricsGetHeaderRefs.noDataError()
      throw notFoundError
    }

    const validatedHeaderRefs = headerRefsSchema.safeParse(responseRef.data)

    if (!validatedHeaderRefs.success) {
      metricsGetHeaderRefs.validationError(validatedHeaderRefs.error)
      return null
    }

    metricsGetHeaderRefs.success()

    const connections = getConnections(validatedHeaderRefs.data)

    const getHeaderCounter = createCounter("trpc.contentstack", "header.get")
    const metricsGetHeader = getHeaderCounter.init({ lang })

    metricsGetHeader.start()

    const tags = [
      generateTagsFromSystem(lang, connections),
      generateTag(lang, validatedHeaderRefs.data.header.system.uid),
    ].flat()

    const response = await request<GetHeaderData>(
      GetHeader,
      { locale: lang },
      { key: tags, ttl: "max" }
    )

    if (!response.data) {
      const notFoundError = notFound(response)
      metricsGetHeader.noDataError()
      throw notFoundError
    }

    const validatedHeaderConfig = headerSchema.safeParse(response.data)

    if (!validatedHeaderConfig.success) {
      metricsGetHeader.validationError(validatedHeaderConfig.error)
      return null
    }

    metricsGetHeader.success()

    return {
      data: validatedHeaderConfig.data.header,
    }
  }),
  currentHeader: contentstackBaseProcedure
    .input(langInput)
    .query(async ({ input }) => {
      const getCurrentHeaderRefsCounter = createCounter(
        "trpc.contentstack",
        "currentHeader.get.refs"
      )
      const metricsGetCurrentHeaderRefs = getCurrentHeaderRefsCounter.init({
        lang: input.lang,
      })

      metricsGetCurrentHeaderRefs.start()

      const responseRef = await request<CurrentHeaderRefDataRaw>(
        GetCurrentHeaderRef,
        {
          locale: input.lang,
        },
        {
          key: generateRefsResponseTag(input.lang, "current_header"),
          ttl: "max",
        }
      )

      const getCurrentHeaderCounter = createCounter(
        "trpc.contentstack",
        "currentHeader.get"
      )
      const metricsGetCurrentHeader = getCurrentHeaderCounter.init({
        lang: input.lang,
      })

      metricsGetCurrentHeader.start()

      const currentHeaderUID =
        responseRef.data.all_current_header.items[0].system.uid
      // There's currently no error handling/validation for the responseRef, should it be added?
      const response = await request<GetCurrentHeaderData>(
        GetCurrentHeader,
        { locale: input.lang },
        {
          key: generateTag(input.lang, currentHeaderUID),
          ttl: "max",
        }
      )

      if (!response.data) {
        const notFoundError = notFound(response)
        metricsGetCurrentHeader.noDataError()
        throw notFoundError
      }

      const validatedHeaderConfig = validateCurrentHeaderConfigSchema.safeParse(
        response.data
      )

      if (!validatedHeaderConfig.success) {
        metricsGetCurrentHeader.validationError(validatedHeaderConfig.error)
        return null
      }

      metricsGetCurrentHeader.success()

      return validatedHeaderConfig.data
    }),
  currentFooter: contentstackBaseProcedure
    .input(langInput)
    .query(async ({ input }) => {
      const getCurrentFooterRefsCounter = createCounter(
        "trpc.contentstack",
        "currentFooter.get.refs"
      )
      const metricsGetCurrentFooterRefs = getCurrentFooterRefsCounter.init({
        lang: input.lang,
      })

      metricsGetCurrentFooterRefs.start()

      const responseRef = await request<CurrentFooterRefDataRaw>(
        GetCurrentFooterRef,
        {
          locale: input.lang,
        },
        {
          key: generateRefsResponseTag(input.lang, "current_footer"),
          ttl: "max",
        }
      )

      const getCurrentFooterCounter = createCounter(
        "trpc.contentstack",
        "currentFooter.get"
      )
      const metricsGetCurrentFooter = getCurrentFooterCounter.init({
        lang: input.lang,
      })

      metricsGetCurrentFooter.start()

      const currentFooterUID =
        responseRef.data.all_current_footer.items[0].system.uid

      const response = await request<CurrentFooterDataRaw>(
        GetCurrentFooter,
        {
          locale: input.lang,
        },
        {
          key: generateTag(input.lang, currentFooterUID),
          ttl: "max",
        }
      )

      if (!response.data) {
        const notFoundError = notFound(response)
        metricsGetCurrentFooter.noDataError()
        throw notFoundError
      }

      const validatedCurrentFooterConfig =
        validateCurrentFooterConfigSchema.safeParse(response.data)

      if (!validatedCurrentFooterConfig.success) {
        metricsGetCurrentFooter.validationError(
          validatedCurrentFooterConfig.error
        )
        return null
      }

      metricsGetCurrentFooter.success()

      return validatedCurrentFooterConfig.data.all_current_footer.items[0]
    }),
  footer: contentstackBaseProcedure.query(async ({ ctx }) => {
    const { lang } = ctx

    const getFooterRefsCounter = createCounter(
      "trpc.contentstack",
      "footer.get.refs"
    )
    const metricsGetFooterRefs = getFooterRefsCounter.init({ lang })

    metricsGetFooterRefs.start()

    const responseRef = await request<FooterRefDataRaw>(
      GetFooterRef,
      {
        locale: lang,
      },
      {
        key: generateRefsResponseTag(lang, "footer"),
        ttl: "max",
      }
    )

    if (!responseRef.data) {
      const notFoundError = notFound(responseRef)
      metricsGetFooterRefs.noDataError()
      throw notFoundError
    }

    const validatedFooterRefs = validateFooterRefConfigSchema.safeParse(
      responseRef.data
    )

    if (!validatedFooterRefs.success) {
      metricsGetFooterRefs.validationError(validatedFooterRefs.error)
      return null
    }

    metricsGetFooterRefs.success()

    const connections = getFooterConnections(validatedFooterRefs.data)
    const footerUID = responseRef.data.all_footer.items[0].system.uid

    const getFooterCounter = createCounter("trpc.contentstack", "footer.get")
    const metricsGetFooter = getFooterCounter.init({ lang })

    metricsGetFooter.start()

    const tags = [
      generateTags(lang, connections),
      generateTag(lang, footerUID),
    ].flat()

    const response = await request<FooterDataRaw>(
      GetFooter,
      {
        locale: lang,
      },
      {
        key: tags,
        ttl: "max",
      }
    )

    if (!response.data) {
      const notFoundError = notFound(response)
      metricsGetFooter.noDataError()
      throw notFoundError
    }

    const validatedFooterConfig = validateFooterConfigSchema.safeParse(
      response.data
    )

    if (!validatedFooterConfig.success) {
      metricsGetFooter.validationError(validatedFooterConfig.error)
      return null
    }

    metricsGetFooter.success()

    return validatedFooterConfig.data
  }),
  siteConfig: contentstackBaseProcedure
    .input(langInput)
    .query(async ({ input, ctx }) => {
      const lang = input.lang ?? ctx.lang

      const getSiteConfigRefsCounter = createCounter(
        "trpc.contentstack",
        "siteConfig.get.refs"
      )
      const metricsGetSiteConfigRefs = getSiteConfigRefsCounter.init({ lang })

      metricsGetSiteConfigRefs.start()

      const responseRef = await request<GetSiteConfigRefData>(
        GetSiteConfigRef,
        {
          locale: lang,
        },
        {
          key: generateRefsResponseTag(lang, "site_config"),
          ttl: "max",
        }
      )

      if (!responseRef.data) {
        const notFoundError = notFound(responseRef)
        metricsGetSiteConfigRefs.noDataError()
        throw notFoundError
      }

      const validatedSiteConfigRef = siteConfigRefSchema.safeParse(
        responseRef.data
      )

      if (!validatedSiteConfigRef.success) {
        metricsGetSiteConfigRefs.validationError(validatedSiteConfigRef.error)
        return null
      }

      const connections = getSiteConfigConnections(validatedSiteConfigRef.data)
      const siteConfigUid = responseRef.data.all_site_config.items[0].system.uid

      const tags = [
        generateTagsFromSystem(lang, connections),
        generateTag(lang, siteConfigUid),
      ].flat()

      metricsGetSiteConfigRefs.success()

      const getSiteConfigCounter = createCounter(
        "trpc.contentstack",
        "siteConfig.get"
      )
      const metricsGetSiteConfig = getSiteConfigCounter.init({ lang })

      metricsGetSiteConfig.start()

      const [siteConfigResponse, contactConfig] = await Promise.all([
        request<GetSiteConfigData>(
          GetSiteConfig,
          {
            locale: lang,
          },
          {
            key: tags,
            ttl: "max",
          }
        ),
        getContactConfig(lang),
      ])

      if (!siteConfigResponse.data) {
        const notFoundError = notFound(siteConfigResponse)
        metricsGetSiteConfig.noDataError()
        throw notFoundError
      }

      const validatedSiteConfig = siteConfigSchema.safeParse(
        siteConfigResponse.data
      )

      if (!validatedSiteConfig.success) {
        metricsGetSiteConfig.validationError(validatedSiteConfig.error)
        return null
      }

      metricsGetSiteConfig.success()

      const { sitewideAlert } = validatedSiteConfig.data

      return {
        ...validatedSiteConfig.data,
        sitewideAlert: sitewideAlert
          ? {
              ...sitewideAlert,
              phoneContact: contactConfig
                ? getAlertPhoneContactData(sitewideAlert, contactConfig)
                : null,
            }
          : null,
      }
    }),
})
