import { isTemplate, expandTemplate } from '../utils/content-template'
import { computed } from 'vue'
import { useRoute } from '#app'

const specializations: Record<string, object> = {}

function buildSpecialization(locale: string, variant: string): object {
  const config = useAppConfig()
  const locales = config.content.matrix.locale.list.map(l => l.code)
  const variants = config.content.matrix.variant.list.map(v => v.code)
  const allTags = new Set([...locales, ...variants])

  function split(key: string): {base?: string; tags: string[]} {
    const parts = key.split('@')

    if (allTags.has(parts[0])) {
      return {base: undefined, tags: parts}
    }

    return {base: parts[0], tags: parts.slice(1)}
  }

  function relevant(tags: string[]): boolean {
    if (tags.length < 1) {
      return true
    }

    for (const tag of tags) {
      if (tag !== locale && tag !== variant) {
        return false
      }
    }

    return true
  }

  function filterObject(object: any): any {
    if (Array.isArray(object)) {
      return object.map(e => filterObject(e)).filter(e => e !== undefined)
    }

    if (typeof object !== 'object') {
      return object
    }

    const copy: Record<string, any> = {}
    let anonymous: [number, any][] = []

    for (const key in object) {
      const {base, tags} = split(key)

      if (!relevant(tags)) {
        continue
      }

      const value = filterObject(object[key])

      if (base === undefined) {
        anonymous.push([tags.length, value])
      } else {
        copy[base] = value
      }
    }

    if (Object.keys(copy).length > 0 && anonymous.length > 0) {
      console.warn(`Invalid mixing of keys in object ${object}`)
    }

    if (Object.keys(copy).length > 0) {
      return copy
    }

    if (anonymous.length < 1) {
      console.warn("Unable to parse object", object)
    }

    anonymous.sort((a, b) => b[0] - a[0])

    if (anonymous[0][0] > 2) {
      console.warn(`More than two tags per key in object ${object}`)
    }

    return anonymous[0][1]
  }

  const filtered = filterObject(config.content)

  function deepReplace(obj: any, predicate: (node: any) => boolean, replacer: (node: any) => any): void {
    if (Array.isArray(obj)) {
      for (let i = 0; i < obj.length; i++) {
        if (predicate(obj[i])) {
          obj[i] = replacer(obj[i]);
        } else if (typeof obj[i] === 'object' && obj[i] !== null) {
          deepReplace(obj[i], predicate, replacer);
        }
      }
    } else if (typeof obj === 'object' && obj !== null) {
      for (const key of Object.keys(obj)) {
        if (predicate(obj[key])) {
          obj[key] = replacer(obj[key]);
        } else if (typeof obj[key] === 'object' && obj[key] !== null) {
          deepReplace(obj[key], predicate, replacer);
        }
      }
    }
  }

  deepReplace(filtered, (node: any) => isTemplate(node), (node: any) => expandTemplate(node, filtered))
  return filtered
}

function getSpecialization(locale: string, variant: string): object {
  const key = locale + '/' + variant

  if (!(key in specializations)) {
    specializations[key] = buildSpecialization(locale, variant)
  }

  return specializations[key]
}

export function useContent() {
  const config = useAppConfig()
  const route = useRoute()
  const {getLocaleVariant, buildPrefix} = useContentPrefix()

  const locales = config.content.matrix.locale.list.map(l => l.code)
  const variants = config.content.matrix.variant.list.map(v => v.code)

  const localeVariant = computed(() => getLocaleVariant(route.path)!)
  const currentLocale = computed(() => localeVariant.value[0])
  const currentVariant = computed(() => localeVariant.value[1])

  function matrixPath(options: { page?: string; anchor?: string; locale?: string; variant?: string }): string {
    let page = options.page

    if (page && !page.startsWith('/')) {
      page = '/' + page
    }

    if (page === undefined) {
      page = route.fullPath

      const [locale, variant] = getLocaleVariant(page)!
      const length = buildPrefix(locale, variant).length

      page = page.slice(length)
    }

    const prefix = buildPrefix(options.locale ?? currentLocale.value, options.variant ?? currentVariant.value)

    let base = prefix + page

    if (base.endsWith('/')) {
      base = base.slice(base.length - 1)
    }

    return options.anchor ? `${base}#${options.anchor}` : base
  }

  function p(page: string, anchor?: string): string {
    return matrixPath({page: page, anchor: anchor})
  }

  const specialization = computed<any>(() => getSpecialization(currentLocale.value, currentVariant.value))

  return {
    locale: currentLocale,
    locales: config.content.matrix.locale.list,
    localeCodes: locales,
    variant: currentVariant,
    variants: config.content.matrix.variant.list,
    variantCodes: variants,
    matrixPath: matrixPath,
    p: p,
    c: specialization,
  }
}
