#!/usr/bin/env node
/**
 * 从 docs/zh-CN/component/*.md 抽取组件 API，生成 Skill references。
 */
const fs = require('fs')
const path = require('path')

const SKILL_ROOT = path.resolve(__dirname, '..')
const REPO_ROOT = path.resolve(SKILL_ROOT, '..')
const CONFIG = JSON.parse(
  fs.readFileSync(path.join(SKILL_ROOT, 'skill.config.json'), 'utf8')
)

const DOCS_DIR = path.join(REPO_ROOT, CONFIG.docsDir)
const BY_NAME_DIR = path.join(SKILL_ROOT, 'references/components/by-name')
const OUT_DIR = path.join(SKILL_ROOT, 'references/components')
const INDEX_PATH = path.join(SKILL_ROOT, 'references/component-index.md')

const SECTION_SUFFIXES = [
  'Scoped Slot',
  'Attributes',
  'Attribute',
  'Events',
  'Event',
  'Methods',
  'Method',
  'Slots',
  'Slot',
  'Props',
  'Options',
  'Exposes',
  'API',
]

const DOC_LEVEL_SECTIONS = new Set([
  'attributes',
  'attribute',
  '属性',
  'events',
  'event',
  '事件',
  'slots',
  'slot',
  '插槽',
  'exposes',
  'methods',
  'method',
  'ref 实例方法',
  'ref 方法',
  'props',
])

const CATEGORY_MAP = {
  general: [
    'avatar',
    'button',
    'link',
    'text',
    'icon',
    'iconi',
    'layout',
    'space',
    'row',
    'col',
    'container',
    'header',
    'footer',
    'main',
    'aside',
    'border',
    'typography',
    'tag',
    'title',
  ],
  navigation: [
    'affix',
    'breadcrumb',
    'dropdown',
    'menu',
    'pagination',
    'steps',
    'page-header',
    'anchor',
    'tabs',
    'tab-pane',
    'sub-menu',
    'menu-item',
    'menu-item-group',
    'breadcrumb-item',
  ],
  'date-time': [
    'time-picker-pro',
    'date-picker-pro',
    'date-picker-panel-pro',
    'date-picker-panel',
    'time-picker',
    'date-picker',
    'datetime-picker',
    'datetime-picker-pro',
    'time-select',
  ],
  'data-entry': [
    'checkbox',
    'cascader',
    'color-picker',
    'color-picker-panel',
    'form',
    'form-item',
    'input',
    'input-tag',
    'input-number',
    'radio',
    'rate',
    'select',
    'select-v2',
    'table-select',
    'tree-select',
    'switch',
    'slider',
    'transfer',
    'upload',
    'mention',
    'autocomplete',
    'segmented',
    'checkbox-group',
    'checkbox-button',
    'radio-group',
    'radio-button',
  ],
  'data-display': [
    'badge',
    'empty',
    'descriptions',
    'descriptions-item',
    'result',
    'progress',
    'status',
    'statistic',
    'countdown',
    'table',
    'table-v2',
    'table-column',
    'auto-resizer',
    'tag',
    'timeline',
    'timeline-item',
    'tree',
    'tree-v2',
    'list',
    'list-item',
    'list-item-meta',
    'tour',
    'tour-step',
    'card',
    'calendar',
    'image',
    'image-viewer',
    'skeleton',
    'skeleton-item',
  ],
  feedback: [
    'loading',
    'alert',
    'message',
    'notification',
    'drawer',
    'modal',
    'dialog',
    'popover',
    'tooltip',
    'message-box',
    'popconfirm',
    'skeleton',
    'result',
  ],
  other: [
    'backtop',
    'collapse',
    'collapse-item',
    'carousel',
    'carousel-item',
    'divider',
    'watermark',
    'scrollbar',
    'step',
    'option',
    'option-group',
    'dropdown-item',
    'dropdown-menu',
    'button-group',
    'cascader-panel',
    'check-tag',
    'collapse-transition',
    'fake-arrow',
    'popper',
    'splitter',
    'splitter-panel',
    'config-provider',
  ],
  ai: [
    'sender',
    'bubble',
    'bubble-list',
    'conversations',
    'thinking',
    'thought-chain',
    'typewriter',
    'list-table-select',
  ],
}

const CATEGORY_TITLES = {
  general: '通用组件',
  navigation: '导航组件',
  'date-time': '日期时间',
  'data-entry': '数据录入',
  'data-display': '数据展示',
  feedback: '反馈组件',
  other: '其他组件',
  ai: 'AI 组件',
}

function camelToKebab(str) {
  return str
    .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
    .replace(/\s+/g, '-')
    .toLowerCase()
}

function headingPrefixToComponent(prefix, docBase) {
  if (!prefix) return docBase
  const cleaned = prefix.trim()
  // 中文标题如「## 菜单 API」应归并到文档主组件（menu.md），勿生成 菜单.md
  if (/[\u4E00-\u9FFF]/.test(cleaned)) return docBase
  if (cleaned.includes('-') || cleaned.includes(' ')) {
    return cleaned.toLowerCase().replace(/\s+/g, '-')
  }
  return camelToKebab(cleaned)
}

function isValidComponentSlug(name) {
  return /^[a-z][a-z0-9-]*$/.test(name)
}

function parseHeading(heading) {
  const cleaned = heading.replace(/\s*\([^)]*\)\s*$/, '').trim()
  for (const suffix of SECTION_SUFFIXES) {
    if (cleaned === suffix) return { prefix: '', suffix }
    if (cleaned.endsWith(` ${suffix}`)) {
      return { prefix: cleaned.slice(0, -(suffix.length + 1)), suffix }
    }
  }
  return { prefix: cleaned, suffix: '' }
}

function isApiHeading(heading) {
  const { suffix } = parseHeading(
    heading.replace(/\s*\([^)]*\)\s*$/, '').trim()
  )
  return SECTION_SUFFIXES.includes(suffix)
}

function kebabToPascal(str) {
  return str
    .split('-')
    .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
    .join('')
}

function kebabToTag(str) {
  return `${CONFIG.tagPrefix}-${str}`
}

function kebabToImport(str) {
  return `${CONFIG.importPrefix}${kebabToPascal(str)}`
}

function getCategory(name) {
  for (const [cat, items] of Object.entries(CATEGORY_MAP)) {
    if (items.includes(name)) return cat
  }
  return CONFIG.tagPrefix === 'wi' ? 'ai' : 'other'
}

function cleanApiContent(text) {
  return text
    .replace(/^---[\s\S]*?---\n/m, '')
    .replace(/<[^>]+>/g, '')
    .replace(/:::[\s\S]*?:::/g, '')
    .replace(/!\[[^\]]*\]\([^)]+\)/g, '')
    .replace(/\n{3,}/g, '\n\n')
    .trim()
}

function extractTitle(content) {
  const m = content.match(/^#\s+(.+)$/m)
  return m ? m[1].replace(/<[^>]+>/g, '').trim() : ''
}

function registerSection(registry, componentName, body, source) {
  if (!componentName || !body.trim()) return
  if (!registry[componentName]) {
    registry[componentName] = { sections: [], sources: new Set() }
  }
  registry[componentName].sections.push(cleanApiContent(body))
  registry[componentName].sources.add(source)
}

function scanDocApiSections(content, docBase, file, registry) {
  const lines = content.split('\n')

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]
    const m = line.match(/^(#{2,4})\s+(.+)$/)
    if (!m) continue

    const level = m[1].length
    const heading = m[2].trim()
    let componentName = docBase
    let capture = false

    if (level === 2 && isApiHeading(heading)) {
      const { prefix } = parseHeading(heading)
      componentName = headingPrefixToComponent(prefix, docBase)
      capture = true
    } else if (level >= 3 && isApiHeading(heading)) {
      const { prefix } = parseHeading(heading)
      componentName = headingPrefixToComponent(prefix, docBase)
      capture = true
    } else if (level === 2 && DOC_LEVEL_SECTIONS.has(heading.toLowerCase())) {
      componentName = docBase
      capture = true
    }

    if (!capture) continue

    const sectionLines = [line]
    let j = i + 1
    while (j < lines.length) {
      const next = lines[j]
      const nextM = next.match(/^(#{1,4})\s+/)
      if (nextM) {
        const nextLevel = nextM[1].length
        const nextHeading = next.replace(/^#+\s+/, '').trim()
        if (nextLevel <= level) break
        if (nextLevel === 2 && isApiHeading(nextHeading)) break
        if (
          nextLevel === 2 &&
          DOC_LEVEL_SECTIONS.has(nextHeading.toLowerCase())
        )
          break
      }
      sectionLines.push(next)
      j++
    }

    registerSection(
      registry,
      componentName,
      sectionLines.join('\n'),
      `${CONFIG.docsDir}/${file}`
    )
    i = j - 1
  }
}

function buildDocApiRegistry() {
  const registry = {}
  const titleRegistry = {}
  const docFiles = fs
    .readdirSync(DOCS_DIR)
    .filter(
      (f) =>
        f.endsWith('.md') &&
        !CONFIG.excludeDocs.includes(f.replace(/\.md$/, ''))
    )

  for (const file of docFiles) {
    const docBase = file.replace(/\.md$/, '')
    const content = fs.readFileSync(path.join(DOCS_DIR, file), 'utf8')
    const title = extractTitle(content)
    if (title) titleRegistry[docBase] = title
    scanDocApiSections(content, docBase, file, registry)
  }

  return { registry, titleRegistry, docFiles }
}

function findComponentSourceFile(name) {
  const compDir = path.join(REPO_ROOT, CONFIG.componentsDir, name)
  if (!fs.existsSync(compDir)) return null
  const srcDir = path.join(compDir, 'src')
  if (!fs.existsSync(srcDir)) return null
  const files = fs
    .readdirSync(srcDir)
    .filter((f) => f.endsWith('.vue') || f.endsWith('.tsx'))
  return files.length ? path.join(srcDir, files[0]) : null
}

function buildComponentDoc(name, docRegistry, titleRegistry) {
  const importName = kebabToImport(name)
  const tag = kebabToTag(name)
  const title = titleRegistry[name] || importName
  const docEntry = docRegistry[name]
  const sourceFile = findComponentSourceFile(name)

  let apiContent = ''
  const sources = []

  if (docEntry && docEntry.sections.length) {
    apiContent = docEntry.sections.join('\n\n')
    sources.push(...docEntry.sources)
  }

  const sourceLabel = sources.length
    ? sources.map((s) => `\`${s}\``).join('、')
    : sourceFile
    ? `\`packages/components/${name}\``
    : '无'

  const lines = [
    `# ${name} (${tag})`,
    '',
    `- **Import**: \`${importName}\` from \`${CONFIG.packageName}\``,
    `- **Tag**: \`<${tag}>\``,
    `- **说明**: ${title}`,
    `- **分类**: ${getCategory(name)}`,
    `- **Skill 版本**: V${CONFIG.skillVersion}`,
    `- **适配组件库**: ${CONFIG.packageName} ${CONFIG.packageVersion}`,
    `- **API 来源**: ${sourceLabel}`,
    '',
  ]

  if (apiContent) {
    lines.push('## API', '', apiContent, '')
  } else if (sourceFile) {
    lines.push('## API', '', '> 容器/插槽组件，详见源码与官网文档。', '')
  } else {
    lines.push('## API', '', '> 暂无抽取 API，使用前请查阅官网文档。', '')
  }

  return {
    content: lines.join('\n'),
    hasApi: !!apiContent || !!sourceFile,
  }
}

function writeCategoryIndexes(allNames, titleRegistry) {
  const byCategory = {}
  for (const name of allNames) {
    const cat = getCategory(name)
    if (!byCategory[cat]) byCategory[cat] = []
    byCategory[cat].push(name)
  }

  for (const [cat, names] of Object.entries(byCategory)) {
    const lines = [
      `# ${CATEGORY_TITLES[cat] || cat}`,
      '',
      `> Skill V${CONFIG.skillVersion} · ${CONFIG.packageName} ${CONFIG.packageVersion}`,
      '',
      '| 组件 | 标签 | 说明 | API |',
      '| --- | --- | --- | --- |',
      ...names.sort().map((name) => {
        const title = titleRegistry[name] || kebabToImport(name)
        return `| ${name} | \`${kebabToTag(
          name
        )}\` | ${title} | [by-name/${name}.md](by-name/${name}.md) |`
      }),
      '',
    ]
    fs.writeFileSync(path.join(OUT_DIR, `${cat}.md`), lines.join('\n'))
  }
}

function main() {
  const { registry, titleRegistry, docFiles } = buildDocApiRegistry()
  const allNames = new Set()

  for (const file of docFiles) allNames.add(file.replace(/\.md$/, ''))
  for (const name of Object.keys(registry)) {
    if (isValidComponentSlug(name)) allNames.add(name)
  }

  if (!fs.existsSync(BY_NAME_DIR))
    fs.mkdirSync(BY_NAME_DIR, { recursive: true })

  const stats = { total: 0, withApi: 0, empty: 0 }
  const indexRows = []

  for (const name of [...allNames].sort()) {
    const doc = buildComponentDoc(name, registry, titleRegistry)
    fs.writeFileSync(path.join(BY_NAME_DIR, `${name}.md`), doc.content)
    stats.total++
    if (doc.hasApi) stats.withApi++
    else stats.empty++
    indexRows.push(
      `| \`${kebabToImport(name)}\` | \`${kebabToTag(name)}\` | ${
        titleRegistry[name] || kebabToImport(name)
      } | ${getCategory(
        name
      )} | [by-name/${name}.md](components/by-name/${name}.md) |`
    )
  }

  const indexContent = [
    '# 组件索引',
    '',
    `> 共 ${stats.total} 个组件 | Skill V${CONFIG.skillVersion} · ${CONFIG.packageName} ${CONFIG.packageVersion}`,
    `> 仅使用下表列出的组件；API 以 by-name 文档为准，禁止臆造属性。`,
    '',
    '| Import 名 | 标签名 | 说明 | 分类 | API |',
    '| --- | --- | --- | --- | --- |',
    ...indexRows,
    '',
  ].join('\n')

  fs.writeFileSync(INDEX_PATH, indexContent)
  writeCategoryIndexes([...allNames], titleRegistry)

  // 清理历史误生成的非英文 slug 文件（如 菜单.md）
  if (fs.existsSync(BY_NAME_DIR)) {
    const valid = new Set([...allNames])
    for (const file of fs.readdirSync(BY_NAME_DIR)) {
      if (!file.endsWith('.md')) continue
      const slug = file.replace(/\.md$/, '')
      if (!valid.has(slug)) {
        fs.unlinkSync(path.join(BY_NAME_DIR, file))
        console.log(`[rm] stale by-name/${file}`)
      }
    }
  }

  const versionPath = path.join(SKILL_ROOT, 'version.json')
  fs.writeFileSync(
    versionPath,
    `${JSON.stringify(
      {
        skillVersion: CONFIG.skillVersion,
        packageVersion: CONFIG.packageVersion,
        packageName: CONFIG.packageName,
      },
      null,
      2
    )}\n`
  )

  console.log(
    `[ok] ${CONFIG.skillName}: ${stats.total} components (${stats.withApi} with API, ${stats.empty} empty)`
  )
}

main()
