import { readFile, writeFile } from 'fs/promises'
import type { Schema } from 'untyped'
import { resolve } from 'pathe'
import { upperFirst } from 'scule'

export async function main () {
  const rootDir = resolve(__dirname, '..')
  const configTemplate = resolve(__dirname, 'nuxt.config.md')
  const configFile = resolve(rootDir, 'content/3.docs/2.directory-structure/16.nuxt.config.md')
  await generateDocs({ configFile, configTemplate })
}

function generateMarkdown (schema: Schema, title: string, level: string, parentVersions: string[] = []) {
  const lines: string[] = []

  // Skip private
  if (schema.tags?.includes('@private')) {
    return []
  }

  // Versions
  const versions = (schema.tags || []).map(t => t.match(/@version (\d+)/)?.[1]).filter(Boolean)
  if (!versions.length) {
    // Inherit from parent if not specified
    versions.push(...parentVersions)
  }
  if (!versions.includes('3')) {
    return []
  }

  // Render heading
  lines.push(`${level} ${title}`, '')

  // Render meta info
  if (schema.type !== 'object' || !schema.properties) {
    // Type and default
    if (schema.type !== 'any') {
      lines.push(`- **Type**: \`${schema.type}\``)
    }
    const defaultValue = formatValue(schema.default)
    if (defaultValue) {
      lines.push('- **Default**', ...defaultValue)
    }

    lines.push(`- **Version**: ${versions.join(', ')}`)

    lines.push('')
  }

  // Render title
  if (schema.title) {
    lines.push('> ' + schema.title, '')
  }

  // Render description
  if (schema.description) {
    lines.push(schema.description, '')
  }

  // Render @ tags
  if (schema.tags) {
    lines.push(...schema.tags.map(renderTag).flat())
  }

  // Render properties
  if (schema.type === 'object') {
    const keys = Object.keys(schema.properties || {}).sort()
    for (const key of keys) {
      const val = schema.properties[key] as Schema
      const propLines = generateMarkdown(val, `\`${key}\``, level + '#', versions)
      if (propLines.length) {
        lines.push('', ...propLines)
      }
    }
  }

  return lines
}

const TAG_REGEX = /^@([\d\w]+)[\s\n]/i

const TagAlertType = {
  note: 'info',
  warning: 'warning',
  deprecated: 'danger'
}

const InternalTypes = new Set([
  'version',
  'deprecated'
])

function formatValue (val) {
  const stringified = JSON.stringify(val, null, 2)
  if (stringified === '{}' || stringified === '[]') { return null }
  return ['```json', stringified, '```']
}

function renderTag (tag: string) {
  const type = tag.match(TAG_REGEX)?.[1]
  if (!type) {
    return [`<!-- ${tag} -->`]
  }
  if (InternalTypes.has(type)) {
    return []
  }
  tag = tag.replace(`@${type}`, `**${upperFirst(type)}**:`)
  if (TagAlertType[type]) {
    return [`::alert{type="${TagAlertType[type]}"}`, tag, '::', '']
  }
  return tag
}

async function generateDocs ({ configFile, configTemplate }) {
  const GENERATE_KEY = '<!-- GENERATED_CONFIG_DOCS -->'
  // Prepare content directory
  const start = Date.now()
  console.log(`Updating docs on ${configFile}`)
  const template = await readFile(configTemplate, 'utf8')
  const rootSchema = require('../../packages/schema/schema/config.schema.json') as Schema
  const keys = Object.keys(rootSchema.properties).sort()
  let generatedDocs = ''

  if (!template.includes(GENERATE_KEY)) {
    throw new Error(`Could not find ${GENERATE_KEY} in ${configFile}`)
  }

  // Generate each section
  for (const key of keys) {
    const schema = rootSchema.properties[key]

    const lines = generateMarkdown(schema, key, '##')

    // Skip empty sections
    if (lines.length < 3) {
      continue
    }

    // Add lines to new file content
    generatedDocs += lines.join('\n') + '\n'
  }

  const body = template.replace(GENERATE_KEY, generatedDocs)
  await writeFile(configFile, body)

  console.log(`Generate done in ${(Date.now() - start) / 1000} seconds!`)
}

main().catch((err) => {
  console.error(err)
  process.exit(1)
})