// Renders .superpowers/findings-ledger.json to FINDINGS.md, and validates it.
// Run:  node .superpowers/render-ledger.mjs           # write FINDINGS.md
//       node .superpowers/render-ledger.mjs --check   # validate only
//       node .superpowers/render-ledger.mjs --selftest # run built-in tests
import assert from 'node:assert/strict'

const VALID_STATUS = ['open', 'fixed', 'declined', 'refuted', 'duplicate', 'blocked']

export function validate(ledger) {
  const problems = []
  const ids = new Set()
  for (const r of ledger.findings) {
    if (ids.has(r.id)) problems.push(`duplicate id ${r.id}`)
    ids.add(r.id)
    if (!/^OC-\d{4}$/.test(r.id)) problems.push(`${r.id}: malformed id`)
    if (!VALID_STATUS.includes(r.status)) problems.push(`${r.id}: bad status ${r.status}`)
    if (r.status === 'fixed' && (!r.fix || !r.fix.commit)) problems.push(`${r.id}: fixed without a commit`)
    if (r.status === 'declined' && !r.rationale) problems.push(`${r.id}: declined without a rationale`)
    if (r.status === 'duplicate' && !r.duplicateOf) problems.push(`${r.id}: duplicate without duplicateOf`)
  }
  return problems
}

function selftest() {
  assert.deepEqual(validate({ findings: [] }), [])
  assert.deepEqual(
    validate({ findings: [{ id: 'OC-0001', status: 'fixed', fix: null }] }),
    ['OC-0001: fixed without a commit'],
  )
  assert.deepEqual(validate({ findings: [{ id: 'bad', status: 'open' }] }), ['bad: malformed id'])
  assert.deepEqual(
    validate({ findings: [{ id: 'OC-0001', status: 'open' }, { id: 'OC-0001', status: 'open' }] }),
    ['duplicate id OC-0001'],
  )
  assert.deepEqual(validate({ findings: [{ id: 'OC-0002', status: 'declined' }] }), ['OC-0002: declined without a rationale'])
  console.log('selftest: all assertions pass')
}

const SEV_RANK = { critical: 0, high: 1, medium: 2, low: 3 }

export function render(ledger) {
  const by = (s) => ledger.findings.filter((f) => f.status === s)
  const open = by('open').sort((a, b) => SEV_RANK[a.severity] - SEV_RANK[b.severity])
  const blocked = by('blocked')
  const fixed = by('fixed')
  const declined = by('declined')
  const refuted = by('refuted')
  const dup = by('duplicate')

  const lines = []
  lines.push('# OwnCord Findings Ledger', '')
  lines.push('Generated by `render-ledger.mjs`. Do not hand-edit — edit `findings-ledger.json`.', '')
  lines.push(
    `**${open.length} open** · ${blocked.length} blocked · ${fixed.length} fixed · ` +
      `${declined.length} declined · ${refuted.length} refuted · ${dup.length} duplicate`,
    '',
  )

  const section = (title, rows, extra) => {
    if (!rows.length) return
    lines.push(`## ${title}`, '')
    for (const r of rows) {
      lines.push(`### ${r.id} — ${r.severity} — ${r.title}`, '')
      lines.push(`\`${r.file}:${r.line}\` · found ${r.found} · hunt \`${r.hunt}\` · lens \`${r.lens}\``, '')
      if (r.why) lines.push(r.why, '')
      if (r.repro) lines.push(`**Repro:** ${r.repro}`, '')
      if (r.evidence) lines.push(`**Evidence:** ${r.evidence}`, '')
      if (r.suggestedFix) lines.push(`**Suggested fix:** ${r.suggestedFix}`, '')
      const e = extra && extra(r)
      if (e) lines.push(e, '')
    }
  }

  section('Open', open)
  section('Blocked — fix attempted, revert-proof failed', blocked)
  section('Fixed', fixed, (r) => `**Fixed:** \`${r.fix.commit}\` · test \`${r.fix.test}\` · revert-proof ${r.fix.revertProof}`)
  section('Declined', declined, (r) => `**Declined:** ${r.rationale}`)
  section('Refuted', refuted)
  section('Duplicate', dup, (r) => `**Duplicate of** ${r.duplicateOf}`)
  return lines.join('\n')
}

async function main() {
  const { readFileSync, writeFileSync } = await import('node:fs')
  const { dirname, join } = await import('node:path')
  const { fileURLToPath } = await import('node:url')
  const here = dirname(fileURLToPath(import.meta.url))
  const ledger = JSON.parse(readFileSync(join(here, 'findings-ledger.json'), 'utf8'))
  const problems = validate(ledger)
  if (problems.length) {
    for (const p of problems) console.error(`INVALID ${p}`)
    process.exit(1)
  }
  if (process.argv.includes('--check')) {
    console.log(`ledger valid: ${ledger.findings.length} finding(s)`)
    return
  }
  writeFileSync(join(here, 'FINDINGS.md'), render(ledger) + '\n')
  console.log(`wrote FINDINGS.md (${ledger.findings.length} finding(s))`)
}

if (process.argv.includes('--selftest')) selftest()
else await main()
