export type LoopLimitConfig =
	| {
			kind: "iterations";
			iterations: number;
	  }
	| {
			kind: "duration";
			durationMs: number;
	  };

export type LoopLimitRuntime =
	| {
			kind: "iterations";
			initial: number;
			remaining: number;
	  }
	| {
			kind: "duration";
			durationMs: number;
			deadlineMs: number;
	  };

const TIME_UNITS_MS = new Map<string, number>([
	["s", 1_000],
	["sec", 1_000],
	["secs", 1_000],
	["second", 1_000],
	["seconds", 1_000],
	["m", 60_000],
	["min", 60_000],
	["mins", 60_000],
	["minute", 60_000],
	["minutes", 60_000],
	["h", 3_600_000],
	["hr", 3_600_000],
	["hrs", 3_600_000],
	["hour", 3_600_000],
	["hours", 3_600_000],
]);

export function parseLoopLimitArgs(args: string): LoopLimitConfig | undefined | string {
	const trimmed = args.trim().toLowerCase();
	if (!trimmed) return undefined;

	const parts = trimmed.split(/\s+/);
	if (parts.length > 2) {
		return "Usage: /loop [count|duration]. Examples: /loop 10, /loop 10m, /loop 10min.";
	}

	if (parts.length === 2) {
		return parseDurationParts(parts[0], parts[1]);
	}

	const token = parts[0];
	const iterationMatch = /^(\d+)$/.exec(token);
	if (iterationMatch) {
		const iterations = Number(iterationMatch[1]);
		if (!Number.isSafeInteger(iterations) || iterations <= 0) {
			return "Loop count must be a positive integer.";
		}
		return { kind: "iterations", iterations };
	}

	const durationMatch = /^(\d+)([a-z]+)$/.exec(token);
	if (durationMatch) {
		return parseDurationParts(durationMatch[1], durationMatch[2]);
	}

	return "Usage: /loop [count|duration]. Examples: /loop 10, /loop 10m, /loop 10min.";
}

function parseDurationParts(amountText: string, unitText: string): LoopLimitConfig | string {
	if (!/^\d+$/.test(amountText)) {
		return "Loop duration must use a positive integer amount.";
	}

	const amount = Number(amountText);
	if (!Number.isSafeInteger(amount) || amount <= 0) {
		return "Loop duration must be positive.";
	}

	const unitMs = TIME_UNITS_MS.get(unitText);
	if (unitMs === undefined) {
		return "Loop duration unit must be seconds, minutes, or hours.";
	}

	return { kind: "duration", durationMs: amount * unitMs };
}

export function createLoopLimitRuntime(
	config: LoopLimitConfig | undefined,
	nowMs = Date.now(),
): LoopLimitRuntime | undefined {
	if (!config) return undefined;
	if (config.kind === "iterations") {
		return { kind: "iterations", initial: config.iterations, remaining: config.iterations };
	}
	return { kind: "duration", durationMs: config.durationMs, deadlineMs: nowMs + config.durationMs };
}

export function consumeLoopLimitIteration(limit: LoopLimitRuntime | undefined, nowMs = Date.now()): boolean {
	if (!limit) return true;
	if (limit.kind === "duration") {
		return nowMs < limit.deadlineMs;
	}
	if (limit.remaining <= 0) return false;
	limit.remaining -= 1;
	return true;
}

export function isLoopDurationExpired(limit: LoopLimitRuntime | undefined, nowMs = Date.now()): boolean {
	return limit?.kind === "duration" && nowMs >= limit.deadlineMs;
}

export function describeLoopLimit(config: LoopLimitConfig): string {
	if (config.kind === "iterations") {
		return `${config.iterations} ${config.iterations === 1 ? "iteration" : "iterations"}`;
	}
	return formatDuration(config.durationMs);
}

export function describeLoopLimitRuntime(limit: LoopLimitRuntime): string {
	if (limit.kind === "iterations") {
		return `${limit.remaining} of ${limit.initial} ${limit.initial === 1 ? "iteration" : "iterations"} remaining`;
	}
	return `${formatDuration(limit.durationMs)} limit`;
}

function formatDuration(durationMs: number): string {
	if (durationMs % 3_600_000 === 0) {
		const hours = durationMs / 3_600_000;
		return `${hours} ${hours === 1 ? "hour" : "hours"}`;
	}
	if (durationMs % 60_000 === 0) {
		const minutes = durationMs / 60_000;
		return `${minutes} ${minutes === 1 ? "minute" : "minutes"}`;
	}
	const seconds = durationMs / 1_000;
	return `${seconds} ${seconds === 1 ? "second" : "seconds"}`;
}