import { SDKCoreJs as sdk } from 'studiocms:sdk';
import { Effect, genLogger, pipeLogger } from '../../effect.js';
import { type Mail, SMTPMailer } from '../../utils/effects/smtp.js';
import type { ConfigFinal, StudioCMSMailerConfig } from '../sdk/types.js';

/**
 * Interface representing the options for sending an email.
 */
export interface MailOptions {
	/**
	 * The recipient(s) of the email. Can be a single email address or an array of email addresses.
	 */
	to: string | string[];

	/**
	 * The subject of the email.
	 */
	subject: string;

	/**
	 * The plain text content of the email. Optional.
	 */
	text?: string | undefined;

	/**
	 * The HTML content of the email. Optional.
	 */
	html?: string | undefined;
}

/**
 * Interface representing an error response from the mailer.
 */
export interface MailerErrorResponse {
	error: string;
}

/**
 * Interface representing a success response from the mailer.
 */
export interface MailerSuccessResponse {
	message: string;
}

/**
 * Interface representing the response from a mail verification operation.
 */
export type MailerResponse = MailerSuccessResponse | MailerErrorResponse;

export class Mailer extends Effect.Service<Mailer>()('studiocms/lib/mailer/Mailer', {
	effect: genLogger('studiocms/lib/mailer/Mailer.effect')(function* () {
		const SMTP = yield* SMTPMailer;

		/**
		 * Logs the response from the mailer.
		 *
		 * @param data - The response from the mailer.
		 * @returns The response from the mailer.
		 */
		const mailerResponse = (data: MailerResponse) => {
			if ('error' in data) {
				console.error(data.error);
				return data;
			}
			console.info(data.message);
			return data;
		};

		/**
		 * Gets the mailer configuration from the database.
		 *
		 * @returns A promise that resolves with the mailer configuration object.
		 */
		const getMailerConfigTable = pipeLogger('studiocms/lib/mailer/Mailer.getMailerConfigTable')(
			sdk.CONFIG.mailerConfig.get()
		);

		/**
		 * Updates the mailer configuration in the database.
		 *
		 * @param config - The new mailer configuration object.
		 * @returns A promise that resolves when the mailer configuration has been updated.
		 */
		const updateMailerConfigTable = (config: ConfigFinal<StudioCMSMailerConfig>) =>
			genLogger('studiocms/lib/mailer/Mailer.updateMailerConfigTable')(function* () {
				yield* sdk.CONFIG.mailerConfig
					.update(config)
					.pipe(
						Effect.catchAll((e) =>
							Effect.succeed(
								mailerResponse({ error: `Error updating mailer configuration: ${String(e)}` })
							)
						)
					);
				return mailerResponse({ message: 'Mailer configuration updated successfully' });
			});

		const createMailerConfigTable = (config: ConfigFinal<StudioCMSMailerConfig>) =>
			pipeLogger('studiocms/lib/mailer/Mailer.createMailerConfigTable')(
				sdk.CONFIG.mailerConfig.init(config)
			);

		/**
		 * Sends an email using the provided mailer configuration and mail options.
		 *
		 * @param mailOptions - The options for the mail, including the subject and other message details.
		 * @returns A promise that resolves with the result of the mail sending operation.
		 * @throws Will throw an error if the mail sending operation fails.
		 *
		 * @example
		 * ```typescript
		 * const mailOptions = {
		 *   subject: 'Test Email',
		 *   to: 'recipient@example.com',
		 *   text: 'This is a test email.'
		 * };
		 *
		 * sendMail(mailerConfig, mailOptions)
		 *   .then(result => console.log('Email sent:', result))
		 *   .catch(error => console.error('Error sending email:', error));
		 * ```
		 */
		const sendMail = ({ subject, ...message }: MailOptions) =>
			genLogger('studiocms/lib/mailer/Mailer.sendMail')(function* () {
				// Create the mail options object
				const toSend: Mail.Options = { subject };

				// Set the to field in the mail options
				toSend.to = Array.isArray(message.to) ? message.to.join(', ') : message.to;

				// If the message has a text field and no html field, set the text field in the mail options
				if (message.text && !message.html) {
					toSend.text = message.text;
				}

				// If the message has an html field, set the html field in the mail options
				if (message.html) {
					toSend.html = message.html;
				}

				const result = yield* SMTP.sendMail(toSend);

				return mailerResponse({ message: `Message sent: ${result.messageId}` });
			});

		/**
		 * Verifies the mail connection using the provided transporter configuration.
		 *
		 * @returns A promise that resolves to an object containing either a success message or an error message.
		 *
		 * @example
		 * ```typescript
		 * const result = await verifyMailConnection();
		 * if ('message' in result) {
		 *   console.log(result.message);
		 * } else {
		 *   console.error(result.error);
		 * }
		 * ```
		 */
		const verifyMailConnection = genLogger('studiocms/lib/mailer/Mailer.verifyMailConnection')(
			function* () {
				const result = yield* SMTP.verifyTransport();

				// If the result is not true, log an error and return an error message
				if (result !== true) {
					return mailerResponse({ error: 'Mail connection verification failed' });
				}

				// Log a success message and return a success message
				return mailerResponse({ message: 'Mail connection verified successfully' });
			}
		);

		/**
		 * Checks if the mailer service is enabled in the StudioCMS configuration.
		 *
		 * This function retrieves the configuration from the StudioCMS SDK and
		 * returns the value of the `enableMailer` property. If the configuration
		 * is not available, it defaults to `false`.
		 */
		const isEnabled = genLogger('studiocms/lib/mailer/Mailer.isEnabled')(function* () {
			const config = yield* sdk.GET.siteConfig();
			const status = config?.data?.enableMailer || false;

			return status;
		});

		return {
			getMailerConfigTable,
			updateMailerConfigTable,
			createMailerConfigTable,
			sendMail,
			verifyMailConnection,
			isEnabled,
		};
	}),
	dependencies: [SMTPMailer.Default],
	accessors: true,
}) {
	static Provide = Effect.provide(this.Default);
}
