/**
 * commands/edit-check.mjs — ACI edit-check parse gate (C29).
 *
 * Layer 1 (post-CLI-layering refactor, subtask 13). cmdEditCheck returns
 * { envelope, exitCode } — every fail() site became return err().
 */

import { execFileSync } from 'node:child_process'
import { existsSync, readFileSync } from 'node:fs'
import { extname } from 'node:path'
import { err, ok } from '../util/result.mjs'

export function cmdEditCheck(args) {
	const path = args[0]
	if (!path) return err('Usage: apt-tools edit-check <path>')
	if (!existsSync(path)) return err(`File not found: ${path}`)
	const ext = extname(path).toLowerCase()
	const content = readFileSync(path, 'utf-8')
	const lines = content.split('\n').length

	// JSON: strict parse
	if (ext === '.json') {
		try {
			JSON.parse(content)
			return ok({ status: 'ok', command: 'edit-check', path, check: 'json-parse', lines })
		} catch (e) {
			return err(`JSON parse failed: ${e.message}`)
		}
	}

	// JS/MJS/CJS: node --check
	if (['.js', '.mjs', '.cjs'].includes(ext)) {
		try {
			execFileSync('node', ['--check', path], { stdio: 'pipe' })
			return ok({ status: 'ok', command: 'edit-check', path, check: 'node-syntax', lines })
		} catch (e) {
			const msg = e.stderr ? String(e.stderr).slice(0, 500) : e.message
			return err(`Syntax check failed: ${msg}`)
		}
	}

	// TS/TSX/JSX: structural check (balanced braces + no obvious truncation
	// markers). Full type check is out-of-scope for edit-check — that's what
	// verify runs later.
	if (['.ts', '.tsx', '.jsx'].includes(ext)) {
		const stack = []
		const pairs = { '}': '{', ')': '(', ']': '[' }
		let inString = null
		let inLineComment = false
		let inBlockComment = false
		let prev = ''
		for (let i = 0; i < content.length; i++) {
			const c = content[i]
			if (inLineComment) {
				if (c === '\n') inLineComment = false
				prev = c
				continue
			}
			if (inBlockComment) {
				if (prev === '*' && c === '/') inBlockComment = false
				prev = c
				continue
			}
			if (inString) {
				if (c === inString && prev !== '\\') inString = null
				prev = c
				continue
			}
			if (c === '/' && prev === '/') {
				inLineComment = true
				prev = c
				continue
			}
			if (c === '*' && prev === '/') {
				inBlockComment = true
				prev = c
				continue
			}
			if (c === '"' || c === "'" || c === '`') {
				inString = c
				prev = c
				continue
			}
			if ('{(['.includes(c)) stack.push(c)
			else if (')]}'.includes(c)) {
				if (stack.pop() !== pairs[c])
					return err(`Structural check failed: unmatched '${c}' at offset ${i}`)
			}
			prev = c
		}
		if (stack.length !== 0)
			return err(`Structural check failed: ${stack.length} unclosed bracket(s)`)
		if (/\.\.\.\s*(truncated|rest of file|omitted)/i.test(content)) {
			return err('Structural check failed: truncation marker detected')
		}
		return ok({ status: 'ok', command: 'edit-check', path, check: 'ts-structural', lines })
	}

	// Other file types (md, css, etc.) — no check, just report.
	return ok({
		status: 'ok',
		command: 'edit-check',
		path,
		check: 'none',
		lines,
		note: 'no checker for this file type',
	})
}
