#!/usr/bin/env node
// post-edit-validate.mjs
// PostToolUse hook — auto-formats with prettier, type-checks .ts/.tsx files.
// Fires after Edit|Write|MultiEdit succeeds.

import { existsSync, readFileSync } from 'node:fs'
import { resolve, normalize, extname, join, dirname, relative } from 'node:path'
import { execSync } from 'node:child_process'

const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()

// Extensions prettier should format
const PRETTIER_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.css', '.md'])

// Extensions that trigger type-checking
const TS_EXTENSIONS = new Set(['.ts', '.tsx'])

function findNearestTsconfig(startDir) {
  let dir = startDir
  const root = normalize(ROOT)
  while (dir.length >= root.length) {
    const candidate = join(dir, 'tsconfig.json')
    if (existsSync(candidate)) return candidate
    const parent = dirname(dir)
    if (parent === dir) break
    dir = parent
  }
  return null
}

try {
  const chunks = []
  for await (const chunk of process.stdin) chunks.push(chunk)
  const input = JSON.parse(Buffer.concat(chunks).toString())

  const filePath = input.tool_input?.file_path
  if (!filePath) process.exit(0)

  const ext = extname(filePath).toLowerCase()
  const absPath = normalize(resolve(filePath))
  if (!existsSync(absPath)) process.exit(0)

  const results = []

  // 1. Prettier (skip silently if not installed yet, e.g. before first pnpm install)
  if (PRETTIER_EXTENSIONS.has(ext)) {
    try {
      execSync('pnpm exec prettier --version', { cwd: ROOT, stdio: 'pipe', timeout: 5_000 })
      try {
        execSync('pnpm exec prettier --write "' + absPath + '"', {
          cwd: ROOT,
          stdio: ['pipe', 'pipe', 'pipe'],
          timeout: 10_000
        })
      } catch (err) {
        const stderr = err.stderr?.toString().trim() || ''
        if (stderr && !/ignored/i.test(stderr)) {
          results.push('Prettier error: ' + stderr.slice(0, 300))
        }
      }
    } catch {
      // prettier not installed yet -- skip silently
    }
  }

  // 2. Type-check for .ts/.tsx
  if (TS_EXTENSIONS.has(ext)) {
    const tsconfig = findNearestTsconfig(dirname(absPath))
    if (tsconfig) {
      try {
        execSync('pnpm exec tsc --noEmit -p "' + tsconfig + '"', {
          cwd: ROOT,
          stdio: ['pipe', 'pipe', 'pipe'],
          timeout: 30_000,
          env: { ...process.env, NODE_OPTIONS: '--max-old-space-size=4096' }
        })
      } catch (err) {
        if (err.killed) process.exit(0) // Don't block on timeout
        const stdout = err.stdout?.toString() || ''
        if (stdout.includes('error TS')) {
          const errorLines = stdout
            .split('\n')
            .filter((l) => l.includes('error TS'))
            .slice(0, 10)
          results.push('Type errors after editing ' + filePath + ':\n' + errorLines.join('\n'))
        }
      }
    }
  }

  // Output errors to Claude's context (silence = success)
  if (results.length > 0) {
    process.stderr.write(results.join('\n\n'))
    process.exit(2) // Exit 2 = send stderr as feedback to Claude
  }
} catch {}

process.exit(0)
