import fs from 'node:fs'
import path from 'node:path'

const rootDir = process.cwd()
const aiPath = path.join(rootDir, 'docs/ai-summary.json')
const docsDir = path.join(rootDir, 'docs/components')

if (!fs.existsSync(aiPath) || !fs.existsSync(docsDir)) {
  console.log('Skip enrich: required docs/ai-summary.json or docs/components missing.')
  process.exit(0)
}

const ai = JSON.parse(fs.readFileSync(aiPath, 'utf8'))
const componentMap = new Map(ai.components.map((c) => [c.path, c]))

function toPosix(p) {
  return p.split(path.sep).join('/')
}

function guessSourcePathFromDoc(docFile) {
  const rel = toPosix(path.relative(docsDir, docFile))
  const noExt = rel.replace(/\.md$/, '')
  return `src/runtime/components/${noExt}.vue`
}

function mdEscape(str) {
  return String(str ?? '').replace(/\|/g, '\\|')
}

const propHints = {
  modelName: { type: 'string', description: 'GraphQL model name used to resolve query/mutation operations.' },
  modelBy: { type: 'Record<string, any>', description: 'Base filter object applied to read/search/create flows.' },
  modelKey: { type: 'string', description: 'Unique key field used to identify and update existing rows/items.' },
  fields: { type: 'Array<string | object>', description: 'Requested GraphQL fields used when loading records.' },
  headers: { type: 'any[]', description: 'Column definitions used by table/iterator rendering.' },
  itemTitle: { type: 'string', description: 'Item field used as display label.' },
  itemValue: { type: 'string', description: 'Item field used as stored value.' },
  filterKeys: { type: 'string | string[]', description: 'Keys used for client-side filtering/searching.' },
  multiple: { type: 'boolean', description: 'Enables multi-select mode.' },
  placeholder: { type: 'string', description: 'Placeholder text shown when input has no value.' },
  noDataText: { type: 'string', description: 'Text shown when no matching data is available.' },
}

function inferTypeFromDocDefault(defaultCell) {
  const v = (defaultCell || '').trim().replace(/^`|`$/g, '')
  if (!v || v === '-' || v === 'undefined') return undefined
  if (v === 'true' || v === 'false') return 'boolean'
  if (/^-?\d+(\.\d+)?$/.test(v)) return 'number'
  if ((v.startsWith("'") && v.endsWith("'")) || (v.startsWith('"') && v.endsWith('"'))) return 'string'
  if (v.includes('=> []') || v === '[]') return 'any[]'
  if (v.includes('=> ({') || v === '{}' || v === 'null') return 'Record<string, any>'
  return undefined
}

function inferEventDescription(eventName, payload) {
  if (eventName === 'update:modelValue') return 'Emitted when the bound model value changes.'
  if (eventName === 'open:dialog') return 'Emitted when the component opens its dialog/form editor.'
  if (eventName === 'close:dialog') return 'Emitted when the component closes its dialog/form editor.'
  if (eventName === 'create') return 'Emitted after a create action completes.'
  if (eventName === 'update') return 'Emitted after an update action completes.'
  if (eventName === 'delete') return 'Emitted after a delete action completes.'
  if (eventName === 'import') return 'Emitted after import data is parsed and ready.'
  if (eventName === 'error') return 'Emitted when an operation fails.'
  if (payload) return `Emits payload: \`${payload}\`.`
  return 'Custom event emitted by this component.'
}

function splitRow(line) {
  const cols = line.split('|').slice(1, -1).map((c) => c.trim())
  return cols
}

function joinRow(cols) {
  return `| ${cols.join(' | ')} |`
}

function updatePropsTable(lines, sectionStartIdx, component) {
  // table starts after "## Props", header and separator rows
  let i = sectionStartIdx + 1
  while (i < lines.length && !lines[i].startsWith('|')) i++
  if (i + 1 >= lines.length) return 0

  const propsByName = new Map((component.props || []).map((p) => [p.name, p]))
  let changed = 0

  // skip header + separator
  for (let r = i + 2; r < lines.length; r++) {
    const line = lines[r]
    if (!line.startsWith('|')) break
    const cols = splitRow(line)
    if (cols.length < 5) continue

    const propName = cols[0].replace(/`/g, '').trim()
    const meta = propsByName.get(propName)
    const hint = propHints[propName]

    const desc = cols[1]
    const type = cols[2]
    const def = cols[4]

    if ((!desc || desc === '-') && meta?.description) cols[1] = mdEscape(meta.description)
    else if ((!desc || desc === '-') && hint?.description) cols[1] = mdEscape(hint.description)

    if ((type === '-' || !type) && meta?.type) cols[2] = `\`${mdEscape(meta.type)}\``
    else if ((type === '-' || !type) && hint?.type) cols[2] = `\`${mdEscape(hint.type)}\``
    else if ((type === '-' || !type)) {
      const inferred = inferTypeFromDocDefault(def)
      if (inferred) cols[2] = `\`${mdEscape(inferred)}\``
    }

    if ((!def || def === '-') && meta?.default !== undefined) cols[4] = `\`${mdEscape(meta.default)}\``

    const next = joinRow(cols)
    if (next !== line) {
      lines[r] = next
      changed++
    }
  }

  return changed
}

function updateEventsTable(lines, sectionStartIdx, component) {
  let i = sectionStartIdx + 1
  while (i < lines.length && !lines[i].startsWith('|')) i++
  if (i + 1 >= lines.length) return 0

  const emitsByName = new Map((component.emits || []).map((e) => [e.event, e]))
  let changed = 0

  // skip header + separator
  for (let r = i + 2; r < lines.length; r++) {
    const line = lines[r]
    if (!line.startsWith('|')) break
    const cols = splitRow(line)
    while (cols.length < 3) cols.push('')

    const eventName = cols[0].replace(/`/g, '').trim()
    const meta = emitsByName.get(eventName)
    if (!meta) continue

    const propsCol = cols[1]
    const descCol = cols[2]

    if ((!propsCol || propsCol === '-') && meta.payload) cols[1] = `\`${mdEscape(meta.payload)}\``
    if (!descCol || descCol === '-') cols[2] = mdEscape(inferEventDescription(eventName, meta.payload))

    const next = joinRow(cols)
    if (next !== line) {
      lines[r] = next
      changed++
    }
  }

  return changed
}

function processDocFile(docFile) {
  const sourcePath = guessSourcePathFromDoc(docFile)
  const component = componentMap.get(sourcePath)
  if (!component) return 0

  const content = fs.readFileSync(docFile, 'utf8')
  const lines = content.split('\n')
  let changed = 0

  for (let i = 0; i < lines.length; i++) {
    if (lines[i].trim() === '## Props') changed += updatePropsTable(lines, i, component)
    if (lines[i].trim() === '## Events') changed += updateEventsTable(lines, i, component)
  }

  if (changed > 0) fs.writeFileSync(docFile, lines.join('\n'))
  return changed
}

function walkMd(dir) {
  const out = []
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name)
    if (entry.isDirectory()) out.push(...walkMd(full))
    else if (entry.name.endsWith('.md')) out.push(full)
  }
  return out
}

const mdFiles = walkMd(docsDir)
let changedFiles = 0
let changedRows = 0
for (const file of mdFiles) {
  const changed = processDocFile(file)
  if (changed > 0) {
    changedFiles++
    changedRows += changed
  }
}

console.log(`Enriched ${changedRows} table rows across ${changedFiles} files.`)
