import { env } from "@/env/server"
import { createCounter } from "@/server/telemetry"

import { removeTrailingSlash } from "@/utils/url"

import type { SyncItem } from "@/types/sitemap"
import type { Lang } from "@/constants/languages"

export function mergeEntries(
  currentEntries: SyncItem[],
  newEntries: SyncItem[]
) {
  const entriesMergeCounter = createCounter("sitemap", "entries.merge")
  const metricsEntriesMerge = entriesMergeCounter.init({
    currentEntriesCount: currentEntries.length,
    newEntriesCount: newEntries.length,
  })

  metricsEntriesMerge.start()

  const entries = [...currentEntries]
  newEntries.forEach((entry) => {
    if (!entry?.data?.uid || !entry?.data?.locale) {
      metricsEntriesMerge.dataError(
        `Invalid entry data, missing uid or locale`,
        { entry }
      )
      return
    }

    const index = entries.findIndex(({ data }) => {
      if (!data) {
        metricsEntriesMerge.dataError(`Data is null or undefined,`)
        return false
      }

      return data.uid === entry.data.uid && data.locale === entry.data.locale
    })
    if (index > -1) {
      entries[index] = entry
    } else {
      entries.push(entry)
    }
  })

  metricsEntriesMerge.success({
    entriesCount: entries.length,
  })

  return entries
}

export function mapEntriesToSitemapData(entries: SyncItem[]) {
  const entriesTransformCounter = createCounter("sitemap", "entries.transform")
  const metricsEntriesTransform = entriesTransformCounter.init({
    entriesCount: entries.length,
  })

  metricsEntriesTransform.start()

  const filteredEntries = filterEntriesToSitemapEntries(entries)

  const entriesByUid = groupEntriesByUid(filteredEntries)
  const sitemapEntries = Object.entries(entriesByUid)
    .map(([_, entries]) => mapEntriesToSitemapEntries(entries))
    .flat()

  metricsEntriesTransform.success({
    entriesCount: entries.length,
    sitemapEntriesCount: sitemapEntries.length,
  })

  return sitemapEntries
}

function filterEntriesToSitemapEntries(entries: SyncItem[]) {
  return entries.filter((entry: SyncItem) => {
    const shouldIndex = !entry.data.web?.seo_metadata?.noindex
    return !!(entry.type === "entry_published" && entry.data.url && shouldIndex)
  })
}

// We group the entries by UID because Contentstack has the same `uid` for an
// entry regardless of locale. We want to display each locale as an alternate
// in the sitemap, therefore we group them here by `uid`.
function groupEntriesByUid(entries: SyncItem[]) {
  return entries.reduce<Record<string, SyncItem[]>>((acc, entry) => {
    const uid = entry.data.uid
    if (!acc[uid]) {
      acc[uid] = []
    }
    acc[uid].push(entry)
    return acc
  }, {})
}

function mapEntriesToSitemapEntries(entries: SyncItem[]) {
  const alternates = mapToAlternates(entries)
  return entries.map((currentEntry) => ({
    url: removeTrailingSlash(
      `${env.PUBLIC_URL}/${currentEntry.data.locale}${currentEntry.data.url}`
    ),
    lastModified: currentEntry.data.updated_at,
    changeFrequency:
      currentEntry.data.web?.seo_metadata?.sitemap?.change_frequency ?? "daily",
    priority: currentEntry.data.web?.seo_metadata?.sitemap?.priority ?? 0.5,
    alternates,
  }))
}

function mapToAlternates(entries: SyncItem[]) {
  return entries.reduce<Partial<Record<Lang, string>>>((acc, entry) => {
    acc[entry.data.locale] = removeTrailingSlash(
      `${env.PUBLIC_URL}/${entry.data.locale}${entry.data.url}`
    )
    return acc
  }, {})
}
