import {
  LRLanguage,
  LanguageSupport,
  indentNodeProp,
  delimitedIndent,
  foldNodeProp,
  foldInside
} from '@codemirror/language'
import { styleTags, tags as t } from '@lezer/highlight'
import { parseMixed } from '@lezer/common'
import {
  javascriptLanguage,
  scopeCompletionSource,
  localCompletionSource
} from '@codemirror/lang-javascript'
import {
  xmlLanguage,
  completeFromSchema,
  autoCloseTags
} from '@codemirror/lang-xml'
import { parser as ejsParser } from './ejs.parser.js'
import {
  fcTemplateElements,
  fcTemplateAttributes,
  fcTemplateHelpers
} from './schema.js'

/**
 * FarmControl document template language: custom XML tags with EJS
 * scriptlets (`<% %>`, `<%= %>`, etc.). Uses mixed-language parsing so
 * XML and JavaScript each get their own highlighting/support.
 *
 * @see https://codemirror.net/examples/mixed-language/
 * @see https://codemirror.net/examples/autocompletion/
 */
const fcTemplateParser = ejsParser.configure({
  props: [
    indentNodeProp.add({
      JavascriptExpression: delimitedIndent({ closing: '%>', align: false })
    }),
    foldNodeProp.add({
      Scriptlet: foldInside,
      Output: foldInside,
      Comment: foldInside
    }),
    styleTags({
      'OpeningTag OutputTag CommentTag ClosingTag': t.controlKeyword,
      CommentContent: t.blockComment
    })
  ],
  wrap: parseMixed((node) => {
    // Mount JS inside EJS bodies
    if (node.name === 'JavascriptExpression') {
      return { parser: javascriptLanguage.parser }
    }
    // Overlay XML across all Text regions so tags that wrap EJS
    // (e.g. <Text><%= x %></Text>) stay one coherent XML tree.
    if (node.type.isTop) {
      return {
        parser: xmlLanguage.parser,
        overlay: (n) => n.name === 'Text'
      }
    }
    return null
  })
})

export const fcTemplateLanguage = LRLanguage.define({
  name: 'fcTemplateLang',
  parser: fcTemplateParser,
  languageData: {
    commentTokens: { block: { open: '<%#', close: '%>' } },
    closeBrackets: { brackets: ['(', '[', '{', '"', "'", '`'] }
  }
})

function buildJsScope(autoCompleteObject) {
  let data = autoCompleteObject
  if (typeof data === 'string') {
    try {
      data = JSON.parse(data)
    } catch {
      data = {}
    }
  }
  if (!data || typeof data !== 'object' || Array.isArray(data)) {
    data = {}
  }
  return {
    ...data,
    fc: fcTemplateHelpers
  }
}

/**
 * Language support for FarmControl EJS+XML document templates.
 * @param {{ autoCompleteObject?: object|string }} [options]
 */
export function fcTemplateLang(options = {}) {
  const { autoCompleteObject } = options
  const jsScope = buildJsScope(autoCompleteObject)

  return new LanguageSupport(fcTemplateLanguage, [
    xmlLanguage.data.of({
      autocomplete: completeFromSchema(
        fcTemplateElements,
        fcTemplateAttributes
      )
    }),
    autoCloseTags,
    javascriptLanguage.data.of({
      autocomplete: [localCompletionSource, scopeCompletionSource(jsScope)]
    })
  ])
}
