/**
 * commands/commit.mjs — commit command with postcondition gate (C29).
 *
 * Layer 1 (post-CLI-layering refactor, subtask 14).
 */

import { execFileSync } from 'node:child_process'
import { maybeAutoEmitPlanReady } from '../coordination/auto-emit-artifact-ready.mjs'
import { evaluatePostcondition } from '../risk/postcondition.mjs'
import { err, ok } from '../util/result.mjs'

export function cmdCommit(args) {
	const filesIdx = args.indexOf('--files')
	const pcIdx = args.indexOf('--postcondition')
	const message = args[0]
	// Collect files after --files, stopping at the next flag. Prevents
	// --postcondition's JSON value from being swept into the file list.
	const files = []
	if (filesIdx >= 0) {
		for (let i = filesIdx + 1; i < args.length; i++) {
			if (args[i].startsWith('--')) break
			files.push(args[i])
		}
	}
	const postconditionArg = pcIdx >= 0 ? args[pcIdx + 1] : null

	if (!message) {
		return err(
			'Usage: apt-tools commit <message> --files <file1> <file2> [--postcondition \'{"cmd":"..."}\']',
		)
	}

	if (files.length === 0) {
		return err('No files specified. Use --files <file1> <file2> ...')
	}

	let sha = null
	try {
		// Stage specified files
		execFileSync('git', ['add', '--', ...files], { encoding: 'utf-8', stdio: 'pipe' })

		// Commit with co-author
		const fullMessage = `${message}\n\nCo-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>`
		execFileSync('git', ['commit', '-m', fullMessage], { encoding: 'utf-8', stdio: 'pipe' })

		// Get the commit sha
		sha = execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf-8' }).trim()
	} catch (e) {
		return err(`Commit failed: ${e.message}`)
	}

	// Conductor v2 (post live verification 2026-05-15) — auto-emit
	// `artifact.ready{kind:'plan'}` when the commit landed a fresh
	// `implementation_plan.json` under .aperant/tasks/{id}/ AND the
	// process runs under a Conductor PTY (APERANT_TERMINAL_ID set).
	// Code-enforced replacement for the markdown-discipline emit step
	// that QUICK-track tasks correctly skipped per Fast Path Guarantee.
	// Best-effort: emit failures do NOT roll back the commit.
	try {
		const emitResult = maybeAutoEmitPlanReady({ files, projectRoot: process.cwd() })
		if (emitResult.emitted) {
			// Surface via stderr so apt-tools' structured JSON stdout stays
			// machine-parseable for the caller.
			process.stderr.write(
				`[apt-tools commit] auto-emitted artifact.ready for task ${emitResult.envelope.task}\n`,
			)
		}
	} catch {
		// Non-fatal — the commit succeeded. The Conductor will fall back
		// to filesystem polling (slightly higher latency, same outcome).
	}

	// R3: Postcondition gate — atomic with the commit. If the predicate fails,
	// we roll back the commit (--soft preserves working tree so the executor
	// can fix-and-retry without losing work).
	if (postconditionArg) {
		let pc
		try {
			pc = JSON.parse(postconditionArg)
		} catch {
			// Convenience: treat a bare string as { cmd: <string> }.
			pc = { cmd: postconditionArg }
		}
		const result = evaluatePostcondition(pc, process.cwd())
		if (!result.ok) {
			try {
				execFileSync('git', ['reset', '--soft', 'HEAD~1'], { stdio: 'pipe' })
			} catch {
				/* rollback best-effort */
			}
			return err(
				`Postcondition ${result.kind} failed: ${result.reason}. Commit ${sha} rolled back (--soft).`,
			)
		}
		return ok({
			status: 'ok',
			command: 'commit',
			message,
			files,
			sha,
			postcondition: { status: 'passed', kind: result.kind },
		})
	}

	return ok({ status: 'ok', command: 'commit', message, files, sha })
}
