import fs from "node:fs";
import path from "node:path";
import { withLock } from "./lock";
import type { InboxMessage } from "./models";
import { inboxPath } from "./paths";
import { readConfig } from "./teams";

export function nowIso(): string {
	return new Date().toISOString();
}

export async function appendMessage(teamName: string, agentName: string, message: InboxMessage) {
	const p = inboxPath(teamName, agentName);
	const dir = path.dirname(p);
	if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });

	await withLock(p, async () => {
		let msgs: InboxMessage[] = [];
		if (fs.existsSync(p)) {
			msgs = JSON.parse(fs.readFileSync(p, "utf-8"));
		}
		msgs.push(message);
		fs.writeFileSync(p, JSON.stringify(msgs, null, 2));
	});
}

export async function readInbox(
	teamName: string,
	agentName: string,
	unreadOnly = false,
	markAsRead = true,
): Promise<InboxMessage[]> {
	const p = inboxPath(teamName, agentName);
	if (!fs.existsSync(p)) return [];

	return await withLock(p, async () => {
		const allMsgs: InboxMessage[] = JSON.parse(fs.readFileSync(p, "utf-8"));
		let result = allMsgs;

		if (unreadOnly) {
			result = allMsgs.filter((m) => !m.read);
		}

		if (markAsRead && result.length > 0) {
			for (const m of allMsgs) {
				if (result.includes(m)) {
					m.read = true;
				}
			}
			fs.writeFileSync(p, JSON.stringify(allMsgs, null, 2));
		}

		return result;
	});
}

export async function sendPlainMessage(
	teamName: string,
	fromName: string,
	toName: string,
	text: string,
	summary: string,
	color?: string,
) {
	const msg: InboxMessage = {
		from: fromName,
		text,
		timestamp: nowIso(),
		read: false,
		summary,
		color,
	};
	await appendMessage(teamName, toName, msg);
}

/**
 * Broadcasts a message to all team members except the sender.
 * @param teamName The name of the team
 * @param fromName The name of the sender
 * @param text The message text
 * @param summary A short summary of the message
 * @param color An optional color for the message
 */
export async function broadcastMessage(
	teamName: string,
	fromName: string,
	text: string,
	summary: string,
	color?: string,
) {
	const config = await readConfig(teamName);

	// Create an array of delivery promises for all members except the sender
	const deliveryPromises = config.members
		.filter((member) => member.name !== fromName)
		.map((member) => sendPlainMessage(teamName, fromName, member.name, text, summary, color));

	// Execute deliveries in parallel and wait for all to settle
	const results = await Promise.allSettled(deliveryPromises);

	// Log failures for diagnostics
	const failures = results.filter((r): r is PromiseRejectedResult => r.status === "rejected");
	if (failures.length > 0) {
		console.error(`Broadcast partially failed: ${failures.length} messages could not be delivered.`);
		// Optionally log individual errors
		for (const failure of failures) {
			console.error("- Delivery error:", failure.reason);
		}
	}
}
