/**
 * commands/fan-out.mjs — FRAMEWORK-BUG-033 capability gap (ID-04 / ID-05).
 *
 * Single resolver helper for the `/apt:fan-out` SKILL body. Three input
 * forms accepted (`--tasks`, `--phases`, `--unblocked`); all resolve to
 * the canonical `{task_ids, source, batch_id}` shape. The actual
 * orchestration loop (worktree create, worker spawn, polling) lives in
 * the SKILL body — this CLI just materializes the batch.
 *
 * Subcommands: `resolve`.
 */

import { existsSync, readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { parseFlags } from '../util/args.mjs'
import { err, ok } from '../util/result.mjs'

const ID_PATTERN = /^[a-z0-9_-]+$/i

function generateBatchId() {
	const d = new Date()
	const yyyy = d.getUTCFullYear()
	const mm = String(d.getUTCMonth() + 1).padStart(2, '0')
	const dd = String(d.getUTCDate()).padStart(2, '0')
	const hex = Math.random().toString(16).slice(2, 8)
	return `fanout-${yyyy}${mm}${dd}-${hex}`
}

function parseIdList(raw) {
	if (typeof raw !== 'string') return []
	return raw
		.split(',')
		.map((s) => s.trim())
		.filter(Boolean)
}

function readState(targetDir) {
	const statePath = join(targetDir, '.aperant', 'state.json')
	if (!existsSync(statePath)) return null
	try {
		return JSON.parse(readFileSync(statePath, 'utf-8'))
	} catch {
		return null
	}
}

function resolveFromPhases(state, phases) {
	const wanted = new Set(phases)
	const ids = []
	for (const [id, task] of Object.entries(state?.active_tasks || {})) {
		if (!task) continue
		const key = `${task.milestone_id ?? ''}/${task.phase_id ?? ''}`
		if (wanted.has(key) && task.lifecycle_phase !== 'closed') {
			ids.push(id)
		}
	}
	return ids
}

function resolveUnblocked(state) {
	const ids = []
	for (const [id, task] of Object.entries(state?.active_tasks || {})) {
		if (task?.lifecycle_phase === 'ready-for-agent') ids.push(id)
	}
	return ids
}

export function cmdFanOut(subcommand, projectDir, extraArgs) {
	if (!subcommand) {
		return err(
			'Usage: apt-tools fan-out <resolve> <project-dir> [--tasks <ids> | --phases <m/p,m/p> | --unblocked]',
		)
	}
	switch (subcommand) {
		case 'resolve': {
			if (!projectDir) {
				return err(
					'Usage: apt-tools fan-out resolve <project-dir> [--tasks <ids> | --phases <m/p,m/p> | --unblocked]',
				)
			}
			const targetDir = resolve(projectDir)
			const flags = parseFlags(extraArgs)

			const hasTasks = flags.has('tasks')
			const hasPhases = flags.has('phases')
			const hasUnblocked = flags.has('unblocked')

			const inputForms = [hasTasks, hasPhases, hasUnblocked].filter(Boolean).length
			if (inputForms === 0) {
				return err('One of --tasks, --phases, or --unblocked is required')
			}
			if (inputForms > 1) {
				return err('Only one of --tasks, --phases, or --unblocked may be passed at a time')
			}

			let taskIds = []
			let source = ''
			if (hasTasks) {
				taskIds = parseIdList(flags.get('tasks'))
				source = 'tasks'
				for (const id of taskIds) {
					if (!ID_PATTERN.test(id)) {
						return err(`Invalid task id in --tasks: "${id}" (must match /^[a-z0-9_-]+$/i)`)
					}
				}
			} else if (hasPhases) {
				const state = readState(targetDir)
				if (!state) return err('No state.json found — cannot resolve --phases')
				taskIds = resolveFromPhases(state, parseIdList(flags.get('phases')))
				source = 'phases'
			} else {
				const state = readState(targetDir)
				if (!state) return err('No state.json found — cannot resolve --unblocked')
				taskIds = resolveUnblocked(state)
				source = 'unblocked'
			}

			return ok({
				status: 'ok',
				command: 'fan-out-resolve',
				source,
				task_ids: taskIds,
				batch_id: generateBatchId(),
			})
		}
		default:
			return err(`Unknown fan-out subcommand: ${subcommand}. Available: resolve`)
	}
}
