import { revalidateTag } from "next/cache"
import { headers } from "next/headers"
import { z } from "zod"

import { Lang } from "@/constants/languages"
import { env } from "@/env/server"
import { badRequest, internalServerError } from "@/server/errors/next"
import { affix as breadcrumbsAffix } from "@/server/routers/contentstack/breadcrumbs/utils"
import { languageSwitcherAffix } from "@/server/routers/contentstack/languageSwitcher/utils"
import { affix as metadataAffix } from "@/server/routers/contentstack/metadata/utils"
import { affix as pageSettingsAffix } from "@/server/routers/contentstack/pageSettings/utils"

import { getCacheClient } from "@/services/dataCache"
import {
  generateRefsResponseTag,
  generateRefTag,
  generateTag,
} from "@/utils/generateTag"

import type { NextRequest } from "next/server"

const validateJsonBody = z.object({
  data: z.object({
    content_type: z.object({
      uid: z.string(),
    }),
    entry: z.object({
      breadcrumbs: z
        .object({
          title: z.string(),
        })
        .optional(),
      locale: z.nativeEnum(Lang),
      publish_details: z.object({ locale: z.nativeEnum(Lang) }).optional(),
      uid: z.string(),
      url: z.string().optional(),
      page_settings: z
        .object({
          hide_booking_widget: z.boolean(),
        })
        .optional(),
    }),
  }),
})

export async function POST(request: NextRequest) {
  try {
    const headersList = await headers()
    const secret = headersList.get("x-revalidate-secret")

    if (secret !== env.REVALIDATE_SECRET) {
      console.error(`Invalid Secret`)
      console.error({ secret })
      return badRequest({
        now: Date.now(),
        revalidated: false,
      })
    }

    const data = await request.json()
    const validatedData = validateJsonBody.safeParse(data)
    if (!validatedData.success) {
      console.error("Bad validation for `validatedData`")
      console.error(validatedData.error)
      return internalServerError({ revalidated: false, now: Date.now() })
    }

    const {
      data: {
        data: { content_type, entry },
      },
    } = validatedData

    // The publish_details.locale is the locale that the entry is published in, regardless if it is "localized" or not
    const entryLocale = entry.publish_details?.locale ?? entry.locale

    const refsTag = generateRefsResponseTag(entryLocale, entry.uid)
    const contentEntryTag = generateRefsResponseTag(
      entryLocale,
      content_type.uid
    )
    const refTag = generateRefTag(entryLocale, content_type.uid, entry.uid)
    const tag = generateTag(entryLocale, entry.uid)
    const languageSwitcherTag = generateTag(
      entryLocale,
      entry.uid,
      languageSwitcherAffix
    )
    const metadataTag = generateTag(entryLocale, entry.uid, metadataAffix)

    const cacheClient = await getCacheClient()

    const contentTypeUidTag = generateTag(entryLocale, content_type.uid)
    console.info(`Revalidating tag by content_type_uid: ${contentTypeUidTag}`)
    revalidateTag(contentTypeUidTag)
    await cacheClient.deleteKey(contentTypeUidTag, { fuzzy: true })

    console.info(`Revalidating refsTag: ${refsTag}`)
    revalidateTag(refsTag)
    await cacheClient.deleteKey(refsTag, { fuzzy: true })

    console.info(`Revalidating refTag: ${refTag}`)
    revalidateTag(refTag)
    await cacheClient.deleteKey(refTag, { fuzzy: true })

    console.info(`Revalidating tag: ${tag}`)
    revalidateTag(tag)
    await cacheClient.deleteKey(tag, { fuzzy: true })

    console.info(`Revalidating language switcher tag: ${languageSwitcherTag}`)
    revalidateTag(languageSwitcherTag)
    await cacheClient.deleteKey(languageSwitcherTag, { fuzzy: true })

    console.info(`Revalidating metadataTag: ${metadataTag}`)
    revalidateTag(metadataTag)
    await cacheClient.deleteKey(metadataTag, { fuzzy: true })

    console.info(`Revalidating contentEntryTag: ${contentEntryTag}`)
    revalidateTag(contentEntryTag)
    await cacheClient.deleteKey(contentEntryTag, { fuzzy: true })

    if (entry.url) {
      console.info(`Revalidating url: ${entry.url}`)
      await cacheClient.deleteKey(entry.url, { fuzzy: true })
    }

    if (entry.breadcrumbs) {
      const breadcrumbsRefsTag = generateRefsResponseTag(
        entryLocale,
        entry.uid,
        breadcrumbsAffix
      )
      const breadcrumbsTag = generateTag(
        entryLocale,
        entry.uid,
        breadcrumbsAffix
      )

      console.info(`Revalidating breadcrumbsRefsTag: ${breadcrumbsRefsTag}`)
      revalidateTag(breadcrumbsRefsTag)
      await cacheClient.deleteKey(breadcrumbsRefsTag, { fuzzy: true })

      console.info(`Revalidating breadcrumbsTag: ${breadcrumbsTag}`)
      revalidateTag(breadcrumbsTag)
      await cacheClient.deleteKey(breadcrumbsTag, { fuzzy: true })
    }

    if (entry.page_settings) {
      const pageSettingsTag = generateTag(
        entryLocale,
        entry.uid,
        pageSettingsAffix
      )

      console.info(`Revalidating pageSettingsTag: ${pageSettingsTag}`)
      revalidateTag(pageSettingsTag)
      await cacheClient.deleteKey(pageSettingsTag, { fuzzy: true })
    }

    return Response.json({ revalidated: true, now: Date.now() })
  } catch (error) {
    console.error("Failed to revalidate tag(s)")
    console.error(error)
    return internalServerError({ revalidated: false, now: Date.now() })
  }
}
