import { cache } from "react"

import { Lang } from "@/constants/languages"
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 { contentstackBaseProcedure, router } from "@/server/trpc"

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

import { langInput } from "./input"
import {
  type ContactConfigData,
  CurrentFooterDataRaw,
  CurrentFooterRefDataRaw,
  CurrentHeaderRefDataRaw,
  type GetCurrentHeaderData,
  headerRefsSchema,
  headerSchema,
  siteConfigRefSchema,
  siteConfigSchema,
  validateContactConfigSchema,
  validateCurrentFooterConfigSchema,
  validateCurrentHeaderConfigSchema,
  validateFooterConfigSchema,
  validateFooterRefConfigSchema,
} from "./output"
import {
  getContactConfigCounter,
  getContactConfigFailCounter,
  getContactConfigSuccessCounter,
  getCurrentFooterCounter,
  getCurrentFooterFailCounter,
  getCurrentFooterRefCounter,
  getCurrentFooterSuccessCounter,
  getCurrentHeaderCounter,
  getCurrentHeaderFailCounter,
  getCurrentHeaderRefCounter,
  getCurrentHeaderSuccessCounter,
  getFooterCounter,
  getFooterFailCounter,
  getFooterRefCounter,
  getFooterRefFailCounter,
  getFooterRefSuccessCounter,
  getFooterSuccessCounter,
  getHeaderCounter,
  getHeaderFailCounter,
  getHeaderRefsCounter,
  getHeaderRefsFailCounter,
  getHeaderRefsSuccessCounter,
  getHeaderSuccessCounter,
  getSiteConfigCounter,
  getSiteConfigFailCounter,
  getSiteConfigRefCounter,
  getSiteConfigRefFailCounter,
  getSiteConfigRefSuccessCounter,
  getSiteConfigSuccessCounter,
} from "./telemetry"
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"

const getContactConfig = cache(async (lang: Lang) => {
  getContactConfigCounter.add(1, { lang })
  console.info(
    "contentstack.contactConfig start",
    JSON.stringify({ query: { lang } })
  )
  const response = await request<ContactConfigData>(
    GetContactConfig,
    {
      locale: lang,
    },
    {
      cache: "force-cache",
      next: {
        tags: [`${lang}:contact`],
      },
    }
  )

  if (!response.data) {
    const notFoundError = notFound(response)

    getContactConfigFailCounter.add(1, {
      lang,
      error_type: "not_found",
      error: JSON.stringify({ code: notFoundError.code }),
    })

    console.error(
      "contentstack.config not found error",
      JSON.stringify({ query: { lang }, error: { code: notFoundError.code } })
    )

    throw notFoundError
  }

  const validatedContactConfigConfig = validateContactConfigSchema.safeParse(
    response.data
  )

  if (!validatedContactConfigConfig.success) {
    getContactConfigFailCounter.add(1, {
      lang,
      error_type: "validation_error",
      error: JSON.stringify(validatedContactConfigConfig.error),
    })
    console.error(
      "contentstack.contactConfig validation error",
      JSON.stringify({
        query: { lang },
        error: validatedContactConfigConfig.error,
      })
    )
    return null
  }
  getContactConfigSuccessCounter.add(1, { lang })
  console.info(
    "contentstack.contactConfig success",
    JSON.stringify({ query: { lang } })
  )
  return validatedContactConfigConfig.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
    getHeaderRefsCounter.add(1, { lang })
    console.info(
      "contentstack.header.refs start",
      JSON.stringify({ query: { lang } })
    )

    const responseRef = await request<GetHeaderRefs>(
      GetHeaderRef,
      {
        locale: lang,
      },
      {
        cache: "force-cache",
        next: {
          tags: [generateRefsResponseTag(lang, "header")],
        },
      }
    )

    if (!responseRef.data) {
      const notFoundError = notFound(responseRef)
      getHeaderRefsFailCounter.add(1, {
        lang,
        error_type: "not_found",
        error: JSON.stringify({ code: notFoundError.code }),
      })
      console.error(
        "contentstack.header.refs not found error",
        JSON.stringify({
          query: {
            lang,
          },
          error: { code: notFoundError.code },
        })
      )
      throw notFoundError
    }

    const validatedHeaderRefs = headerRefsSchema.safeParse(responseRef.data)

    if (!validatedHeaderRefs.success) {
      getHeaderRefsFailCounter.add(1, {
        lang,
        error_type: "validation_error",
        error: JSON.stringify(validatedHeaderRefs.error),
      })
      console.error(
        "contentstack.header.refs validation error",
        JSON.stringify({
          query: {
            lang,
          },
          error: validatedHeaderRefs.error,
        })
      )
      return null
    }

    getHeaderRefsSuccessCounter.add(1, { lang })
    console.info(
      "contentstack.header.refs success",
      JSON.stringify({ query: { lang } })
    )

    const connections = getConnections(validatedHeaderRefs.data)

    getHeaderCounter.add(1, { lang })
    console.info(
      "contentstack.header start",
      JSON.stringify({ query: { lang } })
    )

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

    const response = await request<GetHeaderData>(
      GetHeader,
      { locale: lang },
      { cache: "force-cache", next: { tags } }
    )

    if (!response.data) {
      const notFoundError = notFound(response)
      getHeaderFailCounter.add(1, {
        lang,
        error_type: "not_found",
        error: JSON.stringify({ code: notFoundError.code }),
      })
      console.error(
        "contentstack.header not found error",
        JSON.stringify({
          query: { lang },
          error: { code: notFoundError.code },
        })
      )
      throw notFoundError
    }

    const validatedHeaderConfig = headerSchema.safeParse(response.data)

    if (!validatedHeaderConfig.success) {
      getHeaderFailCounter.add(1, {
        lang,
        error_type: "validation_error",
        error: JSON.stringify(validatedHeaderConfig.error),
      })
      console.error(
        "contentstack.header validation error",
        JSON.stringify({
          query: { lang },
          error: validatedHeaderConfig.error,
        })
      )
      return null
    }
    getHeaderSuccessCounter.add(1, { lang })
    console.info(
      "contentstack.header success",
      JSON.stringify({ query: { lang } })
    )

    return {
      data: validatedHeaderConfig.data.header,
    }
  }),
  currentHeader: contentstackBaseProcedure
    .input(langInput)
    .query(async ({ input }) => {
      getCurrentHeaderRefCounter.add(1, { lang: input.lang })
      console.info(
        "contentstack.currentHeader.ref start",
        JSON.stringify({ query: { lang: input.lang } })
      )
      const responseRef = await request<CurrentHeaderRefDataRaw>(
        GetCurrentHeaderRef,
        {
          locale: input.lang,
        },
        {
          cache: "force-cache",
          next: {
            tags: [generateRefsResponseTag(input.lang, "current_header")],
          },
        }
      )
      getCurrentHeaderCounter.add(1, { lang: input.lang })
      console.info(
        "contentstack.currentHeader start",
        JSON.stringify({
          query: { lang: input.lang },
        })
      )

      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 },
        {
          cache: "force-cache",
          next: {
            tags: [generateTag(input.lang, currentHeaderUID)],
          },
        }
      )

      if (!response.data) {
        const notFoundError = notFound(response)
        getCurrentHeaderFailCounter.add(1, {
          lang: input.lang,
          error_type: "not_found",
          error: JSON.stringify({ code: notFoundError.code }),
        })
        console.error(
          "contentstack.currentHeader not found error",
          JSON.stringify({
            query: {
              lang: input.lang,
            },
            error: { code: notFoundError.code },
          })
        )
        throw notFoundError
      }

      const validatedHeaderConfig = validateCurrentHeaderConfigSchema.safeParse(
        response.data
      )

      if (!validatedHeaderConfig.success) {
        getCurrentHeaderFailCounter.add(1, {
          lang: input.lang,
          error_type: "validation_error",
          error: JSON.stringify(validatedHeaderConfig.error),
        })
        console.error(
          "contentstack.currentHeader validation error",
          JSON.stringify({
            query: {
              lang: input.lang,
            },
            error: validatedHeaderConfig.error,
          })
        )
        return null
      }
      getCurrentHeaderSuccessCounter.add(1, { lang: input.lang })
      console.info(
        "contentstack.currentHeader success",
        JSON.stringify({
          query: { lang: input.lang },
        })
      )

      return validatedHeaderConfig.data
    }),
  currentFooter: contentstackBaseProcedure
    .input(langInput)
    .query(async ({ input }) => {
      getCurrentFooterRefCounter.add(1, { lang: input.lang })
      console.info(
        "contentstack.currentFooter.ref start",
        JSON.stringify({ query: { lang: input.lang } })
      )
      const responseRef = await request<CurrentFooterRefDataRaw>(
        GetCurrentFooterRef,
        {
          locale: input.lang,
        },
        {
          cache: "force-cache",
          next: {
            tags: [generateRefsResponseTag(input.lang, "current_footer")],
          },
        }
      )
      // There's currently no error handling/validation for the responseRef, should it be added?
      getCurrentFooterCounter.add(1, { lang: input.lang })
      console.info(
        "contentstack.currentFooter start",
        JSON.stringify({
          query: {
            lang: input.lang,
          },
        })
      )
      const currentFooterUID =
        responseRef.data.all_current_footer.items[0].system.uid

      const response = await request<CurrentFooterDataRaw>(
        GetCurrentFooter,
        {
          locale: input.lang,
        },
        {
          next: {
            tags: [generateTag(input.lang, currentFooterUID)],
          },
        }
      )

      if (!response.data) {
        const notFoundError = notFound(response)
        getCurrentFooterFailCounter.add(1, {
          lang: input.lang,
          error_type: "not_found",
          error: JSON.stringify({ code: notFoundError.code }),
        })
        console.error(
          "contentstack.currentFooter not found error",
          JSON.stringify({
            query: {
              lang: input.lang,
            },
            error: { code: notFoundError.code },
          })
        )
        throw notFoundError
      }

      const validatedCurrentFooterConfig =
        validateCurrentFooterConfigSchema.safeParse(response.data)

      if (!validatedCurrentFooterConfig.success) {
        getFooterFailCounter.add(1, {
          lang: input.lang,
          error_type: "validation_error",
          error: JSON.stringify(validatedCurrentFooterConfig.error),
        })
        console.error(
          "contentstack.currentFooter validation error",
          JSON.stringify({
            query: { lang: input.lang },
            error: validatedCurrentFooterConfig.error,
          })
        )
        return null
      }
      getCurrentFooterSuccessCounter.add(1, { lang: input.lang })
      console.info(
        "contentstack.currentFooter success",
        JSON.stringify({ query: { lang: input.lang } })
      )
      return validatedCurrentFooterConfig.data.all_current_footer.items[0]
    }),
  footer: contentstackBaseProcedure.query(async ({ ctx }) => {
    const { lang } = ctx
    getFooterRefCounter.add(1, { lang })
    console.info(
      "contentstack.footer.ref start",
      JSON.stringify({ query: { lang } })
    )
    const responseRef = await request<FooterRefDataRaw>(
      GetFooterRef,
      {
        locale: lang,
      },
      {
        cache: "force-cache",
        next: {
          tags: [generateRefsResponseTag(lang, "footer")],
        },
      }
    )

    if (!responseRef.data) {
      const notFoundError = notFound(responseRef)
      getFooterRefFailCounter.add(1, {
        lang,
        error_type: "not_found",
        error: JSON.stringify({ code: notFoundError.code }),
      })
      console.error(
        "contentstack.footer.refs not found error",
        JSON.stringify({
          query: {
            lang,
          },
          error: { code: notFoundError.code },
        })
      )
      throw notFoundError
    }

    const validatedFooterRefs = validateFooterRefConfigSchema.safeParse(
      responseRef.data
    )

    if (!validatedFooterRefs.success) {
      getFooterRefFailCounter.add(1, {
        lang,
        error_type: "validation_error",
        error: JSON.stringify(validatedFooterRefs.error),
      })
      console.error(
        "contentstack.footer.refs validation error",
        JSON.stringify({
          query: {
            lang,
          },
          error: validatedFooterRefs.error,
        })
      )
      return null
    }

    getFooterRefSuccessCounter.add(1, { lang })
    console.info(
      "contentstack.footer.refs success",
      JSON.stringify({ query: { lang } })
    )

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

    getFooterCounter.add(1, { lang: lang })
    console.info(
      "contentstack.footer start",
      JSON.stringify({
        query: {
          lang,
        },
      })
    )
    const tags = [
      generateTags(lang, connections),
      generateTag(lang, footerUID),
    ].flat()

    const response = await request<FooterDataRaw>(
      GetFooter,
      {
        locale: lang,
      },
      {
        cache: "force-cache",
        next: {
          tags,
        },
      }
    )

    if (!response.data) {
      const notFoundError = notFound(response)
      getFooterFailCounter.add(1, {
        lang,
        error_type: "not_found",
        error: JSON.stringify({ code: notFoundError.code }),
      })
      console.error(
        "contentstack.footer not found error",
        JSON.stringify({
          query: {
            lang,
          },
          error: { code: notFoundError.code },
        })
      )
      throw notFoundError
    }

    const validatedFooterConfig = validateFooterConfigSchema.safeParse(
      response.data
    )

    if (!validatedFooterConfig.success) {
      getFooterFailCounter.add(1, {
        lang,
        error_type: "validation_error",
        error: JSON.stringify(validatedFooterConfig.error),
      })
      console.error(
        "contentstack.footer validation error",
        JSON.stringify({
          query: { lang: lang },
          error: validatedFooterConfig.error,
        })
      )
      return null
    }
    getFooterSuccessCounter.add(1, { lang })
    console.info(
      "contentstack.footer success",
      JSON.stringify({ query: { lang } })
    )

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

    getSiteConfigRefCounter.add(1, { lang })
    console.info(
      "contentstack.siteConfig.ref start",
      JSON.stringify({ query: { lang } })
    )
    const responseRef = await request<GetSiteConfigRefData>(
      GetSiteConfigRef,
      {
        locale: lang,
      },
      {
        cache: "force-cache",
        next: {
          tags: [generateRefsResponseTag(lang, "site_config")],
        },
      }
    )

    if (!responseRef.data) {
      const notFoundError = notFound(responseRef)
      getSiteConfigRefFailCounter.add(1, {
        lang,
        error_type: "not_found",
        error: JSON.stringify({ code: notFoundError.code }),
      })
      console.error(
        "contentstack.siteConfig.refs not found error",
        JSON.stringify({
          query: {
            lang,
          },
          error: { code: notFoundError.code },
        })
      )
      throw notFoundError
    }

    const validatedSiteConfigRef = siteConfigRefSchema.safeParse(
      responseRef.data
    )

    if (!validatedSiteConfigRef.success) {
      getSiteConfigRefFailCounter.add(1, {
        lang,
        error_type: "validation_error",
        error: JSON.stringify(validatedSiteConfigRef.error),
      })
      console.error(
        "contentstack.siteConfig.refs validation error",
        JSON.stringify({
          query: {
            lang,
          },
          error: 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()

    getSiteConfigRefSuccessCounter.add(1, { lang })
    console.info(
      "contentstack.siteConfig.refs success",
      JSON.stringify({ query: { lang } })
    )

    getSiteConfigCounter.add(1, { lang })
    console.info(
      "contentstack.siteConfig start",
      JSON.stringify({ query: { lang } })
    )
    const [siteConfigResponse, contactConfig] = await Promise.all([
      request<GetSiteConfigData>(
        GetSiteConfig,
        {
          locale: lang,
        },
        {
          cache: "force-cache",
          next: { tags },
        }
      ),
      getContactConfig(lang),
    ])

    if (!siteConfigResponse.data) {
      const notFoundError = notFound(siteConfigResponse)

      getSiteConfigFailCounter.add(1, {
        lang,
        error_type: "not_found",
        error: JSON.stringify({ code: notFoundError.code }),
      })

      console.error(
        "contentstack.siteConfig not found error",
        JSON.stringify({ query: { lang }, error: { code: notFoundError.code } })
      )

      throw notFoundError
    }

    const validatedSiteConfig = siteConfigSchema.safeParse(
      siteConfigResponse.data
    )

    if (!validatedSiteConfig.success) {
      getSiteConfigFailCounter.add(1, {
        lang,
        error_type: "validation_error",
        error: JSON.stringify(validatedSiteConfig.error),
      })
      console.error(
        "contentstack.siteConfig validation error",
        JSON.stringify({
          query: { lang },
          error: validatedSiteConfig.error,
        })
      )
      return null
    }

    getSiteConfigSuccessCounter.add(1, { lang })
    console.info(
      "contentstack.siteConfig success",
      JSON.stringify({ query: { lang } })
    )

    const { sitewideAlert } = validatedSiteConfig.data

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