import type { Content, Parent, Root, RootContent } from 'mdast'

const escapeForRegExp = (s: string) => s.replace (/[.*+?^${}()|[\]\\]/g, '\\$&')


export default (pageNames: string[], basePath = '/wiki'): ((tree: Root) => void) => {
  const names = [...pageNames].sort ((a, b) => b.length - a.length)

  if (names.length === 0)
    {
      return () => {
	;
      }
    }

  const re = new RegExp (`(${ names.map (escapeForRegExp).join ('|') })`, 'g')

  return (tree: Root) => {
    const edits: { parent: Parent; index: number; parts: RootContent[] }[] = []

    const walk = (node: Content | Root, ancestors: Parent[]) => {
      if (!(node) || (typeof node !== 'object'))
	return

      if (!(ancestors.some (ancestor => ['link',
					 'linkReference',
					 'image',
					 'imageReference',
					 'code',
					 'inlineCode'].includes (ancestor?.type)))
	  && (node.type === 'text'))
	{
	  const value = node.value ?? ''
	  if (value)
	    {
	      re.lastIndex = 0
	      let m: RegExpExecArray | null
	      let last = 0
	      const parts: RootContent[] = []

	      while ((m = re.exec (value)) !== null)
		{
		  const start = m.index
		  const end = start + m[0].length

		  if (start > last)
		    parts.push ({ type: 'text', value: value.slice (last, start) })

		  const name = m[1]
		  parts.push ({ type:     'link',
				url:      `${ basePath }/${ encodeURIComponent (name) }`,
				title:    null,
				children: [{ type: 'text', value: name }],
				data:     { hProperties: { 'data-wiki': '1' } } })
		  last = end
		}

		if (parts.length)
		  {
		    if (last < value.length)
		      parts.push ({ type: 'text', value: value.slice (last) })
		    const parent = ancestors[ancestors.length - 1]
		    if (parent && Array.isArray (parent.children))
		      {
			const index = parent.children.indexOf (node)
			if (index >= 0)
			  edits.push ({ parent, index, parts })
		      }
		  }
	    }
	}

      const maybeChidren = 'children' in node ? node.children : undefined
      if (Array.isArray (maybeChidren))
	{
	  const parent = node as Parent

	  for (let i = 0; i < maybeChidren.length; ++i)
	    {
	      const child: Content | undefined = maybeChidren[i]
	      if (!(child))
		continue
	      walk (child, ancestors.concat (parent))
	    }
	}
    }

    walk (tree, [])

    for (let i = edits.length - 1; i >= 0; --i)
      {
	const { parent, index, parts } = edits[i]

	if (!(parent) || !(Array.isArray (parent.children)))
	  continue

	if (0 <= index && index < parent.children.length)
	  parent.children.splice (index, 1, ...parts)
      }
  }
}
