/* global location */
import { marked } from 'marked'
import { UTILS } from '#amidala/main/shared/twind.js'
import idFromString from '#amidala/main/shared/id-from-string.js'

const { h, register, matter, preactHooks, dataset, version } = globalThis.ami
const { Box, Link, RichText, List, Table, Heading } = matter
const { useState, useEffect } = preactHooks

const JS_EXAMPLE_PATTERN = /```js example(.+?(?=```))```/gs
const MATTER = Object.keys(globalThis.ami.matter).map(key => `const ${key} = window.ami.matter.${key}`).join('\n')

function getData () {
  const signature = new URL(location.href)
  signature.pathname = signature.pathname.replace(/.html$/, '.model.json')
  signature.search = ''
  signature.hash = ''

  const data = dataset[signature.href]
    ?.children[0] // WebComponent
    ?.model
    ?.text

  return { data }
}

function getTitleAndText (topicData) {
  let title = ''
  const text = topicData
    // Add hash links.
    .replaceAll(/(Atoms|Molecules|Organisms|Misc)\.([A-Za-z0-9]+)/gm, (match, _p1, p2) => `[${match}](#${p2.toLowerCase()})`)
    // Remove examples.
    .replaceAll(JS_EXAMPLE_PATTERN, '')
    .replaceAll(/^# (.*)$/gm, (_match, g1) => {
      title = g1
      return ''
    })

  return [title, text]
}

function getExamples (topicData) {
  const examples = []
  topicData.replaceAll(JS_EXAMPLE_PATTERN, (_match, p1) => {
    examples.push(p1.trim())
    return ''
  })
  return examples
}

function getElementByHash () {
  return document.getElementsByTagName('ami-doc')?.[0].shadowRoot.getElementById(location.hash?.substring(1))
}

function Styles () {
  return h('div', { className: 'space-y-4 mt-6' },
    h(Heading, { _level: 2, _size: 4 }, 'Utility class reference'),
    h(Table,
      h('tr',
        h('th', { className: 'w-1/5' }, 'Name'),
        h('th', 'Style')
      ),
      Array.from(UTILS).map(([util, css]) => h('tr',
        h('td',
          h('code', { className: 'text-xs' }, util)
        ),
        h('td',
          h('pre',
            h('code', { className: 'text-xs break-all whitespace-pre-wrap' }, css.replace('  }', '}'))
          )
        )
      ))
    )
  )
}

function Markdown ({ content }) {
  const renderer = {
    heading ({ text, depth }) {
      const level = depth === 2 ? 3 : depth
      // Decrease the size of H2 for better readability.
      return `<h${depth} _level="${level}">${text}</h${depth}>`
    },
    code ({ text }) {
      return `<pre><code>${sanitizeTags(text)}</code></pre>`
    },
    codespan ({ text }) {
      return `<code data-topic-code>${sanitizeTags(text)}</code>`
    }
  }
  marked.use({ renderer })
  const markedText = marked(content)
  return h(RichText, markedText)
}

function TopicLink ({ topic }) {
  return h(Link, {
    _variant: 'underline',
    _model: { name: topic, value: `#${topic.toLowerCase()}` }
  })
}

function Preview ({ example }) {
  const code = `(function () {
      const { h, preactHooks } = window.ami
      const { useState } = preactHooks
      ${MATTER}
      return ${example}
    })()
  `

  let result
  try {
    result = (0, eval)(code) // eslint-disable-line no-eval
  } catch (error) {
    console.error(code)
    console.error(error)
    result = 'ERROR: Unable to parse!'
  }

  return result
}

function Example ({ example }) {
  return h('div', { className: 'lg:grid grid-cols-2 bg-bga text-txa border-txa/20  border-t' },
    h('div', { className: 'col-span-1 p-4 border-l border-r bg-bga text-txa border-txa/20' },
      h('div', { className: 'bg-bga text-txa border-txa/20' },
        h(Preview, { example })
      )
    ),
    h('div', { className: 'col-span-1 p-4 border-r bg-bga text-txa border-txa/20 md:border sm:border sm:break-all' },
      h('pre', { className: 'text-sm' },
        h('code', { className: 'whitespace-pre-wrap break-all' }, example)
      )
    )
  )
}

// Define Preact component.
function AmiDoc () {
  const [state, setState] = useState({})

  useEffect(() => {
    setState(getData())
    // Jump to element on hash change.
    globalThis.addEventListener('hashchange', () => {
      getElementByHash()?.scrollIntoView()
    })
  }, [])

  if (!state.data) {
    return null
  }

  // Get topics.
  const sections = []
  for (const [category, categoryData] of Object.entries(state.data)) {
    for (const [topic, topicData] of Object.entries(categoryData)) {
      const [title, text] = getTitleAndText(topicData)
      const examples = getExamples(topicData)

      sections.push(h(Box,
        // Use z-10 for sticky elements.
        h('div', { className: 'sticky top-0 bg-bga border-txa/20 border-b pb-2 z-10' },
          h(Heading, { _level: 1, id: idFromString(title) },
            h(Link, {
              _variant: 'simple',
              _model: { name: `${category} :: ${title}`, value: `#${title.toLowerCase()}` }
            })
          )
        ),
        h('div', { className: 'space-y-2' },
          text && h(Markdown, { content: text }),
          examples.length > 0 && h('div', { className: 'bg-bga text-txa border-txa/20 lg:border-b md:space-y-5 sm:space-y-5' }, examples.map(example =>
            h(Example, { key: topic, example })
          )),
          topic === 'Styles' && h(Styles)
        )
      ))
    }
  }

  return state.data && h('div', { className: 'bg-bga text-txa' },
    h('div', { className: 'lg:flex m-auto', style: 'max-width: 1500px' },
      // Navigation.
      h(Box, { className: 'grow-0 shrink-0' },
        h('p', version),
        Object.keys(state.data).map(category => [
          h('h3', { className: 'font-custom text-2xl' }, category),
          h(List, Object.keys(state.data[category]).map(topic =>
            h('li',
              h(TopicLink, { topic })
            )
          ))
        ])
      ),
      // Topic body.
      h('div', sections)
    )
  )
}

function sanitizeTags (text) {
  return text.replaceAll('<', '&lt;').replaceAll('>', '&gt;')
}

register(AmiDoc, 'ami-doc')
