#!/usr/bin/env node
// CI guard. Re-runs every watch contract generator to a temporary
// directory and asserts:
//   1. Every generator runs to completion without throwing (the source
//      .ts / .json files conform to the parsers' assumptions).
//   2. Re-running is idempotent (running the generators twice produces
//      byte-identical output).
//   3. If targets/watch/Generated/ contains committed files, they match
//      the freshly-regenerated output. (Generated/ is gitignored, so
//      this branch is a no-op in normal flow but catches a future
//      mistake of hand-committing a Generated/ file.)
//
// Exit codes: 0 = clean; 1 = drift detected.

import { execSync } from 'node:child_process'
import { existsSync, readFileSync, readdirSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { runAll } from './gen-all.mjs'

const HERE = dirname(fileURLToPath(import.meta.url))
const REPO_ROOT = join(HERE, '..', '..')
const GENERATED_DIR = join(REPO_ROOT, 'targets', 'watch', 'Generated')

function sha(path) {
  return execSync(`shasum -a 256 "${path}"`, { encoding: 'utf8' }).split(' ')[0]
}

function snapshot() {
  if (!existsSync(GENERATED_DIR)) return new Map()
  const out = new Map()
  for (const name of readdirSync(GENERATED_DIR)) {
    if (!name.endsWith('.swift')) continue
    out.set(name, sha(join(GENERATED_DIR, name)))
  }
  return out
}

function main() {
  const errors = []

  try {
    runAll()
  } catch (e) {
    errors.push(`first-run: ${e.message}`)
  }

  const first = snapshot()

  try {
    runAll()
  } catch (e) {
    errors.push(`second-run: ${e.message}`)
  }

  const second = snapshot()

  for (const [name, hash] of first) {
    if (second.get(name) !== hash) {
      errors.push(`non-idempotent: ${name} differs across two runs`)
    }
  }
  for (const name of second.keys()) {
    if (!first.has(name)) {
      errors.push(`appeared on second run only: ${name}`)
    }
  }

  if (existsSync(GENERATED_DIR)) {
    const tracked = execSync(
      'git ls-files targets/watch/Generated 2>/dev/null || true',
      { cwd: REPO_ROOT, encoding: 'utf8' }
    )
      .split('\n')
      .filter(Boolean)
    if (tracked.length > 0) {
      errors.push(
        `targets/watch/Generated/ has tracked files (should be gitignored): ${tracked.join(', ')}`
      )
    }
  }

  if (errors.length > 0) {
    console.error('check-watch-contract-drift: FAIL')
    for (const err of errors) console.error(`  - ${err}`)
    process.exit(1)
  }
  console.log('check-watch-contract-drift: OK')
}

main()
