import postcss from 'postcss'
import fs from 'fs-extra'
import parser from 'postcss-selector-parser'
import type { Node as SelectorNode } from 'postcss-selector-parser'
import { createRequire } from 'node:module'

const require = createRequire(import.meta.url)
const cssPath = require.resolve('github-markdown-css/github-markdown-light.css')

const css = fs.readFileSync(cssPath, 'utf-8')

// github-markdown-css normally applies to .markdown-body and ALL its descendants.
// But user custom components and demo component may also be rendered inside .markdown-body. We don't want github-markdown-css affects them.
// So we use this script to preprocess github-markdown-css to make it scoped to "markdown element"（.e.g "p" or "ul").

postcss([
  {
    postcssPlugin: 'postcss-github-markdown-css',
    Rule(rule, helper) {
      parser((selectors) => {
        selectors.each((selector) => {
          const first = selector.first
          const second = first.next()
          if (
            first.type === 'class' &&
            first.value === 'markdown-body' &&
            second?.type === 'combinator' &&
            second.value === ' '
          ) {
            // apply the rule to .markdown-el, as well as descendants of it
            // replace .markdown-body xxxx yyyy
            // with xxxx:is(.markdown-el, .markdown-el *) yyyy
            const groups = selector.split((selector) => {
              return selector.type === 'combinator'
            })

            // remove the ".markdown-body " part
            groups.shift()

            // now the groups[0] is the "xxxx " part

            // add the :is(.markdown-el, .markdown-el *) part
            const newNode = parser.pseudo({
              value: ':is(.markdown-el, .markdown-el *)',
            })

            const index = findPseudoInsertPosition(groups[0])
            groups[0].splice(index, 0, newNode)

            selector.nodes = groups.flat()
          }
        })
      }).processSync(rule, { updateSelector: true })
    },
  },
])
  .process(css, { from: 'github-markdown-light.css', to: 'out.css' })
  .then((result) => {
    fs.writeFileSync(
      new URL('../src/Layout/github-markdown-light.css', import.meta.url),
      `/* This file is generated by ../../scripts/process-github-markdown-css.ts. Don't Edit this file directly. */\n` +
        result.css
    )
  })

/**
 * Insert pseudo selector after type selector and universal selector, before combinator and other pseudo.
 * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors#structure_of_a_selector
 */
function findPseudoInsertPosition(nodes: SelectorNode[]) {
  let insertIndex = 0
  while (insertIndex < nodes.length) {
    if (
      nodes[insertIndex].type === 'combinator' ||
      nodes[insertIndex].type === 'pseudo'
    ) {
      break
    }
    insertIndex++
  }
  return insertIndex
}
