// Navigation Configuration
// This file defines the relationship between main navigation items and their related pages
import fs from 'node:fs'
import path from 'node:path'

import { NavigationCategory } from '../sidebar-config'

export interface NavigationItem {
  type: 'link' | 'group'
  label?: string
}

export interface NavigationLink extends NavigationItem {
  type: 'link'
  href: string
  label: string
  translations?: {
    [key: string]: string
  }
  description?: string
  disablePagination?: boolean
  attrs?: {
    icon?: string
    [key: string]: any
  }
  external?: boolean
  hideInSidebar?: boolean
}

export interface MainNavigationLink extends NavigationLink {
  // All links with that category will be shown in the sidebar when the link is active
  relatedGroupCategory: NavigationCategory
}

export interface NavigationGroup extends NavigationItem {
  type: 'group'
  // The category of the group, all links with that category will be shown in the sidebar when
  // the link with that category or the main link that is related to the category is active
  category: NavigationCategory
  // Used to indicate the context of the current page.
  // If the current page is the first item in the list, it is also used as the previous link in the pagination component.
  // The referenced page should be a `MainNavigationLink`. It is ignored for `NavigationCategory.MAIN` groups.
  homePageHref?: string
  autopopulateFromDir?: string
  translations?: {
    [key: string]: string
  }
  entries?: (NavigationLink | MainNavigationLink)[]
}

// HELPER FUNCTIONS

function normalizePath(path: string): string {
  return path.replace(/\/$/, '')
}

function getMainNavGroup(sidebarConfig: NavigationGroup[]): NavigationGroup {
  return sidebarConfig.find(
    group => group.category === NavigationCategory.MAIN
  ) as NavigationGroup
}

function getNavGroupsByCategory(
  sidebarConfig: NavigationGroup[],
  category: NavigationCategory
): NavigationGroup[] {
  return sidebarConfig.filter(group => group.category === category)
}

function getNavLinksByCategory(
  sidebarConfig: NavigationGroup[],
  category: NavigationCategory
): NavigationLink[] {
  const groups = getNavGroupsByCategory(sidebarConfig, category)
  return groups.flatMap(group => group.entries || []) as NavigationLink[]
}

function getNavGroupByHref(
  sidebarConfig: NavigationGroup[],
  href: string
): NavigationGroup | undefined {
  for (const group of sidebarConfig) {
    if (!group.entries) continue

    for (const entry of group.entries) {
      if (entry.type === 'link' && comparePaths(entry.href, href)) {
        return group
      }
    }
  }
  return undefined
}

function getNavLinkByHref(
  sidebarConfig: NavigationGroup[],
  href: string,
  group?: NavigationGroup
): NavigationLink | undefined {
  group = group ?? getNavGroupByHref(sidebarConfig, href)

  if (!group) return undefined

  if (!group.entries) return undefined

  return group.entries.find(
    entry => entry.type === 'link' && comparePaths(entry.href, href)
  ) as NavigationLink
}

function toCamelCase(filename: string): string {
  const nameWithoutExt = path.parse(filename).name

  return nameWithoutExt
    .split(/[-_\s]/)
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join('')
}

function populateEntriesFromDir(
  group: NavigationGroup,
  workspaceRoot: string = process.cwd()
): NavigationLink[] {
  if (!group.autopopulateFromDir) return group?.entries || []

  const dirPath = path.join(
    workspaceRoot,
    '/src/content',
    group.autopopulateFromDir
  )

  if (!fs.existsSync(dirPath)) {
    console.warn(
      `Directory ${dirPath} does not exist, cannot autopopulate navigation group`
    )
    return group?.entries || []
  }

  try {
    const files = fs.readdirSync(dirPath, { withFileTypes: true })

    const existingHrefs = new Set(
      (group.entries || [])
        .filter(entry => entry.type === 'link')
        .map(entry => (entry as NavigationLink).href)
    )

    const entries = files
      .filter(file => file.isFile())
      .filter(file => !['index.md', 'index.mdx'].includes(file.name))
      .filter(file => {
        const ext = path.extname(file.name).toLowerCase()
        return ext === '.md' || ext === '.mdx'
      })
      .map(file => {
        const fileName = path.parse(file.name).name
        const pathWithoutExt = path.join(
          group.autopopulateFromDir || '',
          fileName
        )
        const href = `${pathWithoutExt}`

        const label = toCamelCase(fileName)

        return {
          type: 'link',
          href,
          label,
        } as NavigationLink
      })
      .filter(entry => !existingHrefs.has(entry.href))

    return [...(group.entries || []), ...entries]
  } catch (error) {
    console.error(`Error reading directory ${dirPath}:`, error)
    return group?.entries || []
  }
}

function processAutopopulateGroups(sidebarConfig: NavigationGroup[]) {
  sidebarConfig.forEach(group => {
    if (group.autopopulateFromDir) {
      group.entries = populateEntriesFromDir(group)
      group.autopopulateFromDir = undefined
    }
  })
}

export function getPagination(
  sidebarConfig: NavigationGroup[],
  currentPath: string
): {
  prev?: { href: string; label: string }
  next?: { href: string; label: string }
} {
  processAutopopulateGroups(sidebarConfig)

  currentPath = currentPath.replace(/\/$/, '')
  const result: {
    prev?: { href: string; label: string }
    next?: { href: string; label: string }
  } = {}

  const link = getNavLinkByHref(sidebarConfig, currentPath)
  if (!link || link.disablePagination) return result

  const group = getNavGroupByHref(sidebarConfig, currentPath)

  if (!group) return result

  if (group.category === NavigationCategory.MAIN) {
    const links = getNavLinksByCategory(
      sidebarConfig,
      (link as MainNavigationLink).relatedGroupCategory
    )
    if (links && links.length > 1) {
      result.next = { href: links[1].href, label: links[1].label }
    }
  } else {
    const links = getNavLinksByCategory(sidebarConfig, group.category)

    const index = links.findIndex(link => comparePaths(link.href, currentPath))

    if (index === 0) {
      if (group.homePageHref) {
        const homePageLink = getNavLinkByHref(sidebarConfig, group.homePageHref)
        if (homePageLink) {
          result.prev = { href: homePageLink.href, label: homePageLink.label }
        }
      }
    } else {
      result.prev = {
        href: links[index - 1].href,
        label: links[index - 1].label,
      }
    }

    if (index != links.length - 1) {
      result.next = {
        href: links[index + 1].href,
        label: links[index + 1].label,
      }
    }
  }
  return result
}

export function getSidebar(
  sidebarConfig: NavigationGroup[],
  currentPath: string
): NavigationGroup[] {
  processAutopopulateGroups(sidebarConfig)

  currentPath = currentPath.replace(/\/$/, '')
  const mainGroup = getMainNavGroup(sidebarConfig)
  const currentGroup = getNavGroupByHref(sidebarConfig, currentPath)

  // If no current group found, return GENERAL category groups as default
  if (!currentGroup) {
    const generalGroups = getNavGroupsByCategory(
      sidebarConfig,
      NavigationCategory.GENERAL
    )
    return generalGroups.map(group => ({
      ...group,
      entries: group.entries?.filter(
        entry =>
          entry.type !== 'link' || !(entry as NavigationLink).hideInSidebar
      ),
    }))
  }

  let relatedGroups: NavigationGroup[] = []

  if (currentGroup.category === NavigationCategory.MAIN && mainGroup) {
    const currentLink = getNavLinkByHref(
      sidebarConfig,
      currentPath,
      currentGroup
    ) as MainNavigationLink

    if (!currentLink) {
      const generalGroups = getNavGroupsByCategory(
        sidebarConfig,
        NavigationCategory.GENERAL
      )
      return generalGroups
    }

    relatedGroups = getNavGroupsByCategory(
      sidebarConfig,
      currentLink.relatedGroupCategory
    )
  } else {
    relatedGroups = getNavGroupsByCategory(sidebarConfig, currentGroup.category)
  }

  // Filter out links with hideInSidebar from related groups
  const filteredRelatedGroups = relatedGroups.map(group => ({
    ...group,
    entries: group.entries?.filter(
      entry => entry.type !== 'link' || !(entry as NavigationLink).hideInSidebar
    ),
  }))

  return filteredRelatedGroups
}

export function getExploreMoreData(
  sidebarConfig: NavigationGroup[],
  currentPath: string
) {
  processAutopopulateGroups(sidebarConfig)

  currentPath = currentPath.replace(/\/$/, '')

  // Check if this is the main docs index page
  const isMainIndex =
    currentPath.endsWith('/docs') ||
    currentPath.match(/\/docs\/[a-z]{2}$/) !== null

  let relatedGroups: NavigationGroup[]

  if (isMainIndex) {
    // For main index, show GENERAL category groups
    relatedGroups = getNavGroupsByCategory(
      sidebarConfig,
      NavigationCategory.GENERAL
    )
  } else {
    const link = getNavLinkByHref(
      sidebarConfig,
      currentPath
    ) as MainNavigationLink
    if (!link || !link.relatedGroupCategory) {
      return []
    }
    relatedGroups = getNavGroupsByCategory(
      sidebarConfig,
      link.relatedGroupCategory
    )
  }

  return relatedGroups.map(group => {
    const items = (group.entries || [])
      .filter(entry => !entry.hideInSidebar)
      .map(navLink => {
        return {
          title: navLink.label,
          subtitle: navLink.description || '',
          href: navLink.href,
        }
      })

    return {
      title: group.label || '',
      items,
    }
  })
}

export function comparePaths(path1: string, path2: string): boolean {
  return normalizePath(path1) === normalizePath(path2)
}

export interface HeaderActiveState {
  isTypescriptSdkActive: boolean
  isPythonSdkActive: boolean
  isRubySdkActive: boolean
  isGoSdkActive: boolean
  isJavaSdkActive: boolean
  isApiActive: boolean
  isCliActive: boolean
  isReferencesActive: boolean
  isGuidesActive: boolean
}

export function getHeaderActiveState(
  baseUrl: string,
  currentPath: string
): HeaderActiveState {
  const referencePaths = {
    typescriptSdk: `${baseUrl}/en/typescript-sdk`,
    pythonSdk: `${baseUrl}/en/python-sdk`,
    rubySdk: `${baseUrl}/en/ruby-sdk`,
    goSdk: `${baseUrl}/en/go-sdk`,
    javaSdk: `${baseUrl}/en/java-sdk`,
    api: `${baseUrl}/en/tools/api`,
    cli: `${baseUrl}/en/tools/cli`,
  }

  const isTypescriptSdkActive = isActiveOrParentPath(
    referencePaths.typescriptSdk,
    currentPath
  )
  const isPythonSdkActive = isActiveOrParentPath(
    referencePaths.pythonSdk,
    currentPath
  )
  const isRubySdkActive = isActiveOrParentPath(
    referencePaths.rubySdk,
    currentPath
  )
  const isGoSdkActive = isActiveOrParentPath(referencePaths.goSdk, currentPath)
  const isJavaSdkActive = isActiveOrParentPath(
    referencePaths.javaSdk,
    currentPath
  )
  const isApiActive = isActiveOrParentPath(referencePaths.api, currentPath)
  const isCliActive = isActiveOrParentPath(referencePaths.cli, currentPath)

  return {
    isTypescriptSdkActive,
    isPythonSdkActive,
    isRubySdkActive,
    isGoSdkActive,
    isJavaSdkActive,
    isApiActive,
    isCliActive,
    isReferencesActive:
      isTypescriptSdkActive ||
      isPythonSdkActive ||
      isRubySdkActive ||
      isGoSdkActive ||
      isJavaSdkActive ||
      isApiActive ||
      isCliActive,
    isGuidesActive: isActiveOrParentPath(`${baseUrl}/en/guides`, currentPath),
  }
}

export function isActiveOrParentPath(
  entryPath: string,
  currentPath: string
): boolean {
  const normalizedEntry = normalizePath(entryPath)
  const normalizedCurrent = normalizePath(currentPath)

  if (normalizedEntry === normalizedCurrent) {
    return true
  }

  const segments = normalizedEntry.split('/').filter(Boolean)
  if (segments.length < 3) {
    return false
  }

  return normalizedCurrent.startsWith(normalizedEntry + '/')
}
