import type {
  GlobalConfigFragment,
  InitDataQuery,
  MenuLinkTreeElementFirstFragment,
  MenuLinkTreeFragment,
} from '#graphql-operations'
import type { GraphqlResponse } from '#nuxt-graphql-middleware/response'

type InitDataResponse = GraphqlResponse<InitDataQuery>

export type MappedMenuLink = {
  id: string
  label: string
  href?: string
  children?: MappedMenuLink[]
}

export interface InitData {
  mainMenuLinks: MappedMenuLink[]
  footerMenuLinks: MappedMenuLink[]
  footerSocialMediaLinks: MappedMenuLink[]
  globalConfig: {
    address?: GlobalConfigFragment['address']
  }
}

/**
 * Maps a MenuLinkTreeElementFirstFragment array to a MappedMenuLink array.
 */
export function mapMenuLinkTree(
  menuLinks?: (MenuLinkTreeFragment | MenuLinkTreeElementFirstFragment)[],
  context: { count: number } = { count: 0 },
): MappedMenuLink[] {
  if (!menuLinks) {
    return []
  }
  return menuLinks.map((menuLink) => {
    const id = `menu-item-${context.count++}`

    // Extract the basic properties
    const label = menuLink.link.label
    const href = menuLink.link.url?.path

    // Process children if they exist
    const children =
      'subtree' in menuLink && menuLink.subtree && menuLink.subtree.length > 0
        ? mapMenuLinkTree(menuLink.subtree, context)
        : undefined

    // Return the mapped menu link
    return {
      id,
      label,
      href,
      children,
    }
  })
}

function mapInitData(response: InitDataResponse): InitData {
  if (response.data) {
    return {
      mainMenuLinks: mapMenuLinkTree(response.data.mainMenu?.links),
      footerMenuLinks: mapMenuLinkTree(response.data.footerMenu?.links),
      footerSocialMediaLinks: mapMenuLinkTree(
        response.data.footerSocialMedia?.links,
      ),
      globalConfig: response.data.globalConfig || {},
    }
  }

  throw new Error('Failed to load init data.')
}

export default defineNuxtPlugin({
  name: 'starterkit:init-data',
  dependsOn: [
    'nuxt-graphql-middleware-provide-state',
    'starterkit:graphql-plugin',
    'nuxt:router',
  ],
  async setup() {
    const data = useState<InitData | null>('initData', () => null)

    if (import.meta.server) {
      const event = useRequestEvent()
      const language = useCurrentLanguage()
      if (!event) {
        throw new Error('Missing request event.')
      }

      const { value: cachedResponse, addToCache } =
        await useDataCache<InitDataResponse>(
          'initData_' + language.value,
          event,
        )

      const response = cachedResponse || (await useGraphqlQuery('initData'))

      data.value = mapInitData(response)

      if (!cachedResponse && response.extensions.cacheability?.isCacheable) {
        await addToCache(response, response.extensions.cacheability?.tagsNuxt)
      }
    }

    if (import.meta.client) {
      async function loadInitData() {
        data.value = await useGraphqlQuery('initData', null, {
          graphqlCaching: {
            client: true,
          },
        }).then(mapInitData)
      }
      if (!data.value) {
        await loadInitData()
      }

      const language = useCurrentLanguage()
      watch(language, loadInitData)
    }
  },
})
