/**
 * Schedule Milestone Integration — process ScheduleSpec items at milestone
 * creation and completion time.
 *
 * Purpose: convert ScheduleSpec objects (from milestone planning) into
 * ScheduleEntry records appended to the store.
 *
 * Consumer: plan-milestone.js and complete-milestone.js handlers.
 */
import { createScheduleStore } from "./schedule-store.js";
import { isValidKind } from "./schedule-types.js";
import { generateULID } from "./schedule-ulid.js";

/**
 * Convert a ScheduleSpec into a ScheduleEntry and append it to the store.
 *
 * Purpose: create a concrete schedule entry from a milestone-declared spec.
 *
 * Consumer: milestone creation and completion handlers.
 *
 * @param {string} basePath
 * @param {import("./schedule-types.js").ScheduleSpec} spec
 * @param {string} [milestoneId]
 * @returns {void}
 */
export function appendScheduleSpec(basePath, spec, milestoneId) {
	if (!spec || typeof spec !== "object") return;
	if (!isValidKind(spec.kind)) {
		logWarning(
			"schedule",
			`Skipping schedule spec with unknown kind: ${spec.kind}`,
		);
		return;
	}

	const store = createScheduleStore(basePath);
	const dueAt = _resolveDueAt(spec);
	if (!dueAt) return;

	const entry = {
		id: generateULID(),
		kind: spec.kind,
		status: "pending",
		due_at: dueAt,
		created_at: new Date().toISOString(),
		payload: { message: spec.title, ...(spec.payload ?? {}) },
		created_by: "milestone",
	};
	if (milestoneId) {
		entry.payload.milestoneId = milestoneId;
	}
	store.appendEntry("project", entry);
}

/**
 * Process an array of ScheduleSpec items, appending each to the store.
 *
 * @param {string} basePath
 * @param {Array<import("./schedule-types.js").ScheduleSpec>} specs
 * @param {string} [milestoneId]
 * @returns {void}
 */
export function appendScheduleSpecs(basePath, specs, milestoneId) {
	if (!Array.isArray(specs)) return;
	for (const spec of specs) {
		try {
			appendScheduleSpec(basePath, spec, milestoneId);
		} catch {
			// Non-fatal — one bad spec must not block others.
		}
	}
}

// ─── Internal ───────────────────────────────────────────────────────────────

/**
 * Resolve the due_at ISO string from a ScheduleSpec.
 * Supports `spec.in` (relative duration) and `spec.on_complete.in`.
 *
 * @param {import("./schedule-types.js").ScheduleSpec} spec
 * @returns {string|null}
 */
function _resolveDueAt(spec) {
	let dur = "";
	if (spec.in) {
		dur = spec.in;
	} else if (spec.on_complete?.in) {
		dur = spec.on_complete.in;
	}
	if (!dur) return null;

	const ms = _parseDuration(dur);
	if (ms === null) return null;
	return new Date(Date.now() + ms).toISOString();
}

/**
 * Parse a duration string into milliseconds.
 *
 * @param {string} str
 * @returns {number|null}
 */
function _parseDuration(str) {
	const match = String(str)
		.trim()
		.match(/^(\d+)([wdhm])$/i);
	if (!match) return null;
	const value = parseInt(match[1], 10);
	const unit = match[2].toLowerCase();
	switch (unit) {
		case "w":
			return value * 7 * 24 * 60 * 60 * 1000;
		case "d":
			return value * 24 * 60 * 60 * 1000;
		case "h":
			return value * 60 * 60 * 1000;
		case "m":
			return value * 60 * 1000;
		default:
			return null;
	}
}

/**
 * Best-effort warning logger.
 *
 * @param {string} _scope
 * @param {string} message
 */
function logWarning(_scope, message) {
	try {
		process.stderr.write(`[sf:schedule-milestone] ${message}\n`);
	} catch {
		// ignore
	}
}
