import type {
	custom,
	discord,
	email,
	gotify,
	lark,
	mattermost,
	ntfy,
	pushover,
	resend,
	slack,
	teams,
	telegram,
} from "@dokploy/server/db/schema";
import nodemailer from "nodemailer";
import { Resend } from "resend";

export const sendEmailNotification = async (
	connection: typeof email.$inferInsert,
	subject: string,
	htmlContent: string,
	attachments?: { filename: string; content: Buffer }[],
) => {
	try {
		const {
			smtpServer,
			smtpPort,
			username,
			password,
			fromAddress,
			toAddresses,
		} = connection;
		const transporter = nodemailer.createTransport({
			host: smtpServer,
			port: smtpPort,
			auth: { user: username, pass: password },
		});

		await transporter.sendMail({
			from: fromAddress,
			to: toAddresses.join(", "),
			subject,
			html: htmlContent,
			textEncoding: "base64",
			attachments,
		});
	} catch (err) {
		console.log(err);
		throw new Error(
			`Failed to send email notification ${err instanceof Error ? err.message : "Unknown error"}`,
		);
	}
};

export const sendResendNotification = async (
	connection: typeof resend.$inferInsert,
	subject: string,
	htmlContent: string,
) => {
	try {
		const client = new Resend(connection.apiKey);

		const result = await client.emails.send({
			from: connection.fromAddress,
			to: connection.toAddresses,
			subject,
			html: htmlContent,
		});

		if (result.error) {
			throw new Error(result.error.message);
		}
	} catch (err) {
		console.log(err);
		throw new Error(
			`Failed to send Resend notification ${err instanceof Error ? err.message : "Unknown error"}`,
		);
	}
};

export const sendDiscordNotification = async (
	connection: typeof discord.$inferInsert,
	embed: any,
) => {
	try {
		const response = await fetch(connection.webhookUrl, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({ embeds: [embed] }),
		});
		if (!response.ok) {
			throw new Error(
				`Failed to send discord notification ${response.statusText}`,
			);
		}
	} catch (err) {
		console.log("error", err);
		throw new Error(
			`Failed to send discord notification ${err instanceof Error ? err.message : "Unknown error"}`,
		);
	}
};

export const sendTelegramNotification = async (
	connection: typeof telegram.$inferInsert,
	messageText: string,
	inlineButton?: {
		text: string;
		url: string;
	}[][],
) => {
	try {
		const url = `https://api.telegram.org/bot${connection.botToken}/sendMessage`;
		await fetch(url, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify({
				chat_id: connection.chatId,
				message_thread_id: connection.messageThreadId,
				text: messageText,
				parse_mode: "HTML",
				disable_web_page_preview: true,
				reply_markup: {
					inline_keyboard: inlineButton,
				},
			}),
		});
	} catch (err) {
		console.log(err);
	}
};

export const sendSlackNotification = async (
	connection: typeof slack.$inferInsert,
	message: any,
) => {
	try {
		const response = await fetch(connection.webhookUrl, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify(message),
		});
		if (!response.ok) {
			throw new Error(
				`Failed to send slack notification ${response.statusText}`,
			);
		}
	} catch (err) {
		console.log("error", err);
		throw new Error(
			`Failed to send slack notification ${err instanceof Error ? err.message : "Unknown error"}`,
		);
	}
};

export const sendGotifyNotification = async (
	connection: typeof gotify.$inferInsert,
	title: string,
	message: string,
) => {
	const response = await fetch(`${connection.serverUrl}/message`, {
		method: "POST",
		headers: {
			"Content-Type": "application/json",
			"X-Gotify-Key": connection.appToken,
		},
		body: JSON.stringify({
			title: title,
			message: message,
			priority: connection.priority,
			extras: {
				"client::display": {
					contentType: "text/plain",
				},
			},
		}),
	});

	if (!response.ok) {
		throw new Error(
			`Failed to send Gotify notification: ${response.statusText}`,
		);
	}
};

export const sendNtfyNotification = async (
	connection: typeof ntfy.$inferInsert,
	title: string,
	tags: string,
	actions: string,
	message: string,
) => {
	const response = await fetch(`${connection.serverUrl}/${connection.topic}`, {
		method: "POST",
		headers: {
			...(connection.accessToken && {
				Authorization: `Bearer ${connection.accessToken}`,
			}),
			"X-Priority": connection.priority?.toString() || "3",
			"X-Title": title,
			"X-Tags": tags,
			"X-Actions": actions,
		},
		body: message,
	});

	if (!response.ok) {
		throw new Error(`Failed to send ntfy notification: ${response.statusText}`);
	}
};

export const sendMattermostNotification = async (
	connection: typeof mattermost.$inferInsert,
	message: any,
) => {
	const payload = {
		...message,
		// Only include username if it's provided and not empty
		...(message.username?.trim() && { username: message.username }),
		// Only include channel if it's provided and not empty
		...(message.channel?.trim() && {
			channel: `#${message.channel.replace("#", "")}`,
		}),
	};

	const response = await fetch(connection.webhookUrl, {
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify(payload),
	});

	if (!response.ok) {
		throw new Error(
			`Failed to send Mattermost notification: ${response.statusText}`,
		);
	}
};

export const sendCustomNotification = async (
	connection: typeof custom.$inferInsert,
	payload: Record<string, any>,
) => {
	try {
		// Merge default headers with custom headers (now already an object from jsonb)
		const headers: Record<string, string> = {
			"Content-Type": "application/json",
			...(connection.headers || {}),
		};

		// Default body with payload
		const body = JSON.stringify(payload);

		const response = await fetch(connection.endpoint, {
			method: "POST",
			headers,
			body,
		});

		if (!response.ok) {
			throw new Error(
				`Failed to send custom notification: ${response.statusText}`,
			);
		}

		return response;
	} catch (error) {
		console.error("Error sending custom notification:", error);
		throw error;
	}
};

export const sendLarkNotification = async (
	connection: typeof lark.$inferInsert,
	message: any,
) => {
	try {
		await fetch(connection.webhookUrl, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify(message),
		});
	} catch (err) {
		console.log(err);
	}
};

export interface TeamsAdaptiveCardMessage {
	title: string;
	themeColor?: string;
	facts?: { name: string; value: string }[];
	potentialAction?: { type: "Action.OpenUrl"; title: string; url: string };
}

export const sendTeamsNotification = async (
	connection: typeof teams.$inferInsert,
	message: TeamsAdaptiveCardMessage,
) => {
	try {
		const bodyElements: Record<string, unknown>[] = [
			{
				type: "TextBlock",
				text: message.title,
				size: "Medium",
				weight: "Bolder",
				wrap: true,
			},
		];

		if (message.facts && message.facts.length > 0) {
			bodyElements.push({
				type: "FactSet",
				facts: message.facts.map((f) => ({
					title: f.name,
					value: f.value,
				})),
			});
		}

		const cardContent: Record<string, unknown> = {
			type: "AdaptiveCard",
			$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
			version: "1.4",
			body: bodyElements,
		};

		if (message.potentialAction) {
			cardContent.actions = [
				{
					type: "Action.OpenUrl",
					title: message.potentialAction.title,
					url: message.potentialAction.url,
				},
			];
		}

		const payload = {
			type: "message",
			attachments: [
				{
					contentType: "application/vnd.microsoft.card.adaptive",
					content: cardContent,
				},
			],
		};

		const response = await fetch(connection.webhookUrl, {
			method: "POST",
			headers: { "Content-Type": "application/json" },
			body: JSON.stringify(payload),
		});

		if (!response.ok) {
			throw new Error(
				`Failed to send Teams notification: ${response.statusText}`,
			);
		}
	} catch (err) {
		console.log(err);
		throw new Error(
			`Failed to send Teams notification ${err instanceof Error ? err.message : "Unknown error"}`,
		);
	}
};

export const sendPushoverNotification = async (
	connection: typeof pushover.$inferInsert,
	title: string,
	message: string,
) => {
	const formData = new URLSearchParams();
	formData.append("token", connection.apiToken);
	formData.append("user", connection.userKey);
	formData.append("title", title);
	formData.append("message", message);
	formData.append("priority", connection.priority?.toString() || "0");

	// For emergency priority (2), retry and expire are required
	if (connection.priority === 2) {
		formData.append("retry", connection.retry?.toString() || "30");
		formData.append("expire", connection.expire?.toString() || "3600");
	}

	const response = await fetch("https://api.pushover.net/1/messages.json", {
		method: "POST",
		body: formData,
	});

	if (!response.ok) {
		throw new Error(
			`Failed to send Pushover notification: ${response.statusText}`,
		);
	}
};
