/**
 * commands/release-notes.mjs — `apt-tools release-notes <sub>` (subtask 2-3
 * of persona-aware-changelog-and-release-note_16-05-26).
 *
 * Deterministic I/O surface for the apt:release-notes skill. NEVER calls
 * an LLM. The SKILL.md body invokes the host LLM to author the note text
 * and hands the structured payload to this CLI for persistence + compile.
 *
 * Sub-verb menu:
 *
 *   apt-tools release-notes draft <project-dir> --pr <n> --note "<text>"
 *       [--persona <id>] [--output-dir <path>] [--commits <sha,sha,...>]
 *       [--date <YYYY-MM-DD>]
 *     → write `<output_dir>/_unreleased/PR-<n>.md`. Idempotent.
 *
 *   apt-tools release-notes compile <project-dir> --tag <tag>
 *       [--output-dir <path>]
 *     → read every `_unreleased/PR-*.md`, filter by persona_filter, write
 *       `<output_dir>/<tag>.md`, clear `_unreleased/` only on success.
 *
 *   apt-tools release-notes publish <project-dir> --tag <tag>
 *       [--output-dir <path>] [--dry-run]
 *     → read `<output_dir>/<tag>.md` + shell `gh release create/edit`.
 *
 * Return shape: always `{ status, command, ... }`. The wrap() helper in
 * dispatch.mjs translates `{ status: 'error' }` envelopes into exit-1.
 */

import { resolve } from 'node:path'

import { compileTag } from '../release-notes/compile.mjs'
import { writeDraft } from '../release-notes/draft.mjs'
import { publishTag } from '../release-notes/publish.mjs'
import { getFlagValue } from '../util/common-flags.mjs'
import { err, ok } from '../util/result.mjs'

/**
 * Dispatch shape (mirrors `audit`): `release-notes: (args) =>
 * wrap(cmdReleaseNotes)(args[1], args[2], args.slice(3))` — args[1] is the
 * sub-verb, args[2] is the project dir.
 *
 * @param {string | undefined} subcommand
 * @param {string | undefined} projectDirArg
 * @param {string[] | undefined} extraArgs
 */
export async function cmdReleaseNotes(subcommand, projectDirArg, extraArgs) {
	const args = extraArgs ?? []
	if (!subcommand) {
		return err(
			'Usage: apt-tools release-notes <draft|compile|publish> <project-dir> [flags]\n' +
				'  draft     write docs/releases/_unreleased/PR-<n>.md\n' +
				'  compile   read fragments → docs/releases/<tag>.md, clear _unreleased/\n' +
				'  publish   shell gh release create/edit',
		)
	}
	const projectDir = resolve(projectDirArg && projectDirArg.length > 0 ? projectDirArg : '.')

	switch (subcommand) {
		case 'draft':
			return runDraft(projectDir, args)
		case 'compile':
			return runCompile(projectDir, args)
		case 'publish':
			return runPublish(projectDir, args)
		default:
			return err(
				`apt-tools release-notes: unknown subcommand "${subcommand}". Known: draft, compile, publish.`,
			)
	}
}

/**
 * draft — write a single PR fragment.
 *
 * @param {string} projectDir
 * @param {string[]} args
 */
function runDraft(projectDir, args) {
	const prRaw = getFlagValue(args, '--pr')
	const note = getFlagValue(args, '--note')
	const persona = getFlagValue(args, '--persona')
	const outputDirFlag = getFlagValue(args, '--output-dir')
	const commitsFlag = getFlagValue(args, '--commits')
	const dateFlag = getFlagValue(args, '--date')

	if (!prRaw) {
		return err('release-notes draft: --pr <number> is required')
	}
	const pr = Number.parseInt(prRaw, 10)
	if (!Number.isFinite(pr) || pr <= 0) {
		return err(`release-notes draft: --pr must be a positive integer (got "${prRaw}")`)
	}
	if (!note || note.trim().length === 0) {
		return err('release-notes draft: --note "<text>" is required (and must be non-empty)')
	}

	const commits = commitsFlag
		? commitsFlag
				.split(',')
				.map((s) => s.trim())
				.filter((s) => s.length > 0)
		: null

	const result = writeDraft({
		projectDir,
		pr,
		note,
		persona,
		outputDir: outputDirFlag,
		commits,
		date: dateFlag,
	})

	return ok({
		status: 'ok',
		command: 'release-notes-draft',
		projectDir,
		pr,
		written: result.written,
		reason: result.reason,
		path: result.path,
	})
}

/**
 * compile — produce the per-tag narrative file from accumulated fragments.
 *
 * @param {string} projectDir
 * @param {string[]} args
 */
function runCompile(projectDir, args) {
	const tag = getFlagValue(args, '--tag')
	if (!tag) {
		return err('release-notes compile: --tag <tag> is required')
	}
	const outputDirFlag = getFlagValue(args, '--output-dir')
	const result = compileTag({ projectDir, tag, outputDir: outputDirFlag })
	return ok({
		status: 'ok',
		command: 'release-notes-compile',
		projectDir,
		tag,
		written: result.written,
		path: result.path,
		fragments_total: result.fragmentsTotal,
		fragments_in_body: result.fragmentsInBody,
		fragments_in_triage: result.fragmentsInTriage,
		fragments_filtered: result.fragmentsFiltered,
		cleared_unreleased: result.clearedUnreleased,
	})
}

/**
 * publish — shell `gh release create/edit`. Returns a graceful
 * `{status: 'skipped', reason: 'gh-not-found'}` when gh is unavailable.
 *
 * @param {string} projectDir
 * @param {string[]} args
 */
function runPublish(projectDir, args) {
	const tag = getFlagValue(args, '--tag')
	if (!tag) {
		return err('release-notes publish: --tag <tag> is required')
	}
	const outputDirFlag = getFlagValue(args, '--output-dir')
	const dryRun = args.includes('--dry-run')
	const result = publishTag({ projectDir, tag, outputDir: outputDirFlag, dryRun })
	if (result.status === 'skipped') {
		return ok({
			status: 'skipped',
			command: 'release-notes-publish',
			projectDir,
			tag,
			reason: result.reason,
			notes_path: result.notesPath ?? null,
		})
	}
	return ok({
		status: 'ok',
		command: 'release-notes-publish',
		projectDir,
		tag,
		mode: result.mode,
		dry_run: dryRun,
		notes_path: result.notesPath,
	})
}
