import "server-only";
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "crypto";
import { ImapFlow, type AppendResponseObject, type ImapFlowOptions, type ListResponse } from "imapflow";
import { simpleParser } from "mailparser";
import nodemailer from "nodemailer";

type BuiltInRemoteFolderSystemName = "inbox" | "sent" | "drafts" | "spam" | "trash";
type RemoteFolderSystemName = BuiltInRemoteFolderSystemName | "custom";
type RemoteSyncTarget =
  | BuiltInRemoteFolderSystemName
  | {
      name: string;
      remoteName: string;
    };

type RemoteMailRuntimeConfig = {
  credentialSecret: Buffer;
  imapAllowSelfSigned: boolean;
  imapHost: string;
  imapPort: number;
  imapSecure: boolean;
  smtpAllowSelfSigned: boolean;
  smtpHost: string;
  smtpPort: number;
  smtpRequireTls: boolean;
  smtpSecure: boolean;
  syncLimit: number;
  tlsServerName?: string;
};

export type RemoteMailboxCredentials = {
  email: string;
  password: string;
};

export type RemoteMailboxFolder = {
  name: string;
  remoteName: string;
  systemName: RemoteFolderSystemName;
  totalCount: number;
  unreadCount: number;
};

export type RemoteMailboxMessage = {
  bodyHtml: string | null;
  bodyText: string;
  direction: "inbound" | "outbound" | "draft";
  fromAddress: string;
  fromName: string | null;
  isRead: boolean;
  messageIdHeader: string | null;
  rawSource: string;
  receivedAt: Date;
  remoteFlags: string[];
  remoteFolder: string;
  remoteUid: number;
  snippet: string;
  subject: string;
  toAddresses: string;
  transportResponse: string | null;
  transportStatus: "saved" | "queued" | "sent" | "failed";
};

export type RemoteMailboxAttachmentMeta = {
  contentDisposition: "attachment" | "inline";
  contentId: string | null;
  contentType: string;
  extension: string;
  filename: string;
  index: number;
  isInline: boolean;
  isPreviewable: boolean;
  sizeBytes: number;
};

export type RemoteMailboxAttachmentPayload = RemoteMailboxAttachmentMeta & {
  content: Buffer;
};

export type RemoteMailboxMoveResult = {
  destination: string;
  remoteUid: number | null;
};

export type RemoteMailboxSyncResult = {
  currentFolderMessages: RemoteMailboxMessage[];
  currentFolderState: RemoteMailboxFolder | null;
  folders: RemoteMailboxFolder[];
};

type RemoteMailboxImapClientOptions = Pick<
  ImapFlowOptions,
  "disableAutoIdle" | "emitLogs" | "logger" | "maxIdleTime" | "socketTimeout"
>;

const REMOTE_FOLDER_DEFINITIONS: Array<{
  displayName: string;
  fallbacks: string[];
  specialUse?: string;
  systemName: BuiltInRemoteFolderSystemName;
}> = [
  {
    displayName: "받은메일",
    fallbacks: ["INBOX"],
    systemName: "inbox",
  },
  {
    displayName: "보낸메일",
    fallbacks: ["Sent", "Sent Messages", "Sent Mail"],
    specialUse: "\\Sent",
    systemName: "sent",
  },
  {
    displayName: "임시보관함",
    fallbacks: ["Drafts"],
    specialUse: "\\Drafts",
    systemName: "drafts",
  },
  {
    displayName: "스팸함",
    fallbacks: ["Junk", "Spam"],
    specialUse: "\\Junk",
    systemName: "spam",
  },
  {
    displayName: "휴지통",
    fallbacks: ["Trash", "Deleted Messages"],
    specialUse: "\\Trash",
    systemName: "trash",
  },
];

function readBooleanEnv(name: string, fallback: boolean) {
  const rawValue = process.env[name];

  if (!rawValue) {
    return fallback;
  }

  switch (rawValue.trim().toLowerCase()) {
    case "1":
    case "true":
    case "yes":
    case "on":
      return true;
    case "0":
    case "false":
    case "no":
    case "off":
      return false;
    default:
      return fallback;
  }
}

function readIntegerEnv(name: string, fallback: number) {
  const rawValue = process.env[name];

  if (!rawValue) {
    return fallback;
  }

  const parsed = Number(rawValue);
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}

function resolvePublicHostFromUrl(value: string | undefined) {
  if (!value) {
    return undefined;
  }

  try {
    return new URL(value).hostname;
  } catch {
    return undefined;
  }
}

function resolveTlsServerName() {
  const configuredServerName = process.env.MAIL_TLS_SERVERNAME?.trim();

  if (configuredServerName) {
    return configuredServerName;
  }

  const configuredRemoteHost =
    process.env.MAIL_REMOTE_HOST?.trim() ||
    process.env.MAIL_IMAP_HOST?.trim() ||
    process.env.MAIL_SMTP_HOST?.trim() ||
    process.env.MX_HOSTNAME?.trim();

  if (configuredRemoteHost) {
    return configuredRemoteHost;
  }

  return resolvePublicHostFromUrl(
    process.env.NEXT_PUBLIC_MAIL_APP_URL ?? process.env.NEXT_PUBLIC_APP_URL,
  );
}

function resolveRemoteMailHost(fallback: string) {
  const configuredRemoteHost = process.env.MAIL_REMOTE_HOST?.trim();

  if (configuredRemoteHost) {
    return configuredRemoteHost;
  }

  const mxHostname = process.env.MX_HOSTNAME?.trim();

  if (mxHostname) {
    return mxHostname;
  }

  const publicMailHost = resolvePublicHostFromUrl(
    process.env.NEXT_PUBLIC_MAIL_APP_URL?.trim() ?? process.env.NEXT_PUBLIC_APP_URL?.trim(),
  );

  if (publicMailHost) {
    return publicMailHost;
  }

  return fallback;
}

function getRemoteMailRuntimeConfig(): RemoteMailRuntimeConfig {
  const defaultAllowSelfSigned = readBooleanEnv("MAILCOW_ALLOW_SELF_SIGNED", true);
  const credentialSeed = process.env.MAILBOX_CREDENTIAL_SECRET ?? process.env.DB_PASSWORD ?? "official-mail";
  const defaultRemoteHost = resolveRemoteMailHost("mx1.officialsite.kr");

  return {
    credentialSecret: createHash("sha256").update(credentialSeed).digest(),
    imapAllowSelfSigned: readBooleanEnv("MAIL_IMAP_ALLOW_SELF_SIGNED", defaultAllowSelfSigned),
    imapHost: (process.env.MAIL_IMAP_HOST ?? defaultRemoteHost).trim(),
    imapPort: readIntegerEnv("MAIL_IMAP_PORT", 993),
    imapSecure: readBooleanEnv("MAIL_IMAP_SECURE", true),
    smtpAllowSelfSigned: readBooleanEnv("MAIL_SMTP_ALLOW_SELF_SIGNED", defaultAllowSelfSigned),
    smtpHost: (process.env.MAIL_SMTP_HOST ?? defaultRemoteHost).trim(),
    smtpPort: readIntegerEnv("MAIL_SMTP_PORT", 587),
    smtpRequireTls: readBooleanEnv("MAIL_SMTP_REQUIRE_TLS", true),
    smtpSecure: readBooleanEnv("MAIL_SMTP_SECURE", false),
    syncLimit: readIntegerEnv("MAIL_IMAP_SYNC_LIMIT", 60),
    tlsServerName: resolveTlsServerName(),
  };
}

function normalizeCipherPayload(value: string) {
  const trimmed = value.trim();

  if (!trimmed) {
    throw new Error("mailbox-auth-missing");
  }

  return trimmed.split(".");
}

function formatAddressList(values?: Array<{ address?: string; name?: string }> | null) {
  if (!values?.length) {
    return "";
  }

  return values
    .map((value) => {
      const address = value.address?.trim();
      const name = value.name?.trim();

      if (!address) {
        return "";
      }

      return name ? `"${name.replace(/"/g, '\\"')}" <${address}>` : address;
    })
    .filter(Boolean)
    .join(", ");
}

function formatParsedAddressField(
  value:
    | {
        text?: string;
        value?: Array<{ address?: string; name?: string }>;
      }
    | Array<{
        text?: string;
        value?: Array<{ address?: string; name?: string }>;
      }>
    | undefined,
) {
  if (!value) {
    return "";
  }

  if (Array.isArray(value)) {
    return value
      .map((entry) => entry.text?.trim() || formatAddressList(entry.value))
      .filter(Boolean)
      .join(", ");
  }

  return value.text?.trim() || formatAddressList(value.value);
}

function stripHtml(value: string) {
  return value
    .replace(/<style[\s\S]*?<\/style>/gi, " ")
    .replace(/<script[\s\S]*?<\/script>/gi, " ")
    .replace(/<[^>]+>/g, " ")
    .replace(/&nbsp;/gi, " ")
    .replace(/\s+/g, " ")
    .trim();
}

function createSnippet(value: string) {
  return value.replace(/\s+/g, " ").trim().slice(0, 140);
}

function transportStatusForFolder(systemName: RemoteFolderSystemName) {
  if (systemName === "sent") {
    return "sent" as const;
  }

  return "saved" as const;
}

function directionForFolder(systemName: RemoteFolderSystemName) {
  if (systemName === "sent") {
    return "outbound" as const;
  }

  if (systemName === "drafts") {
    return "draft" as const;
  }

  return "inbound" as const;
}

function buildMessageBodyText(text: string | undefined, html: string | undefined) {
  const normalizedText = text?.trim();

  if (normalizedText) {
    return normalizedText;
  }

  if (typeof html === "string" && html.trim()) {
    return stripHtml(html);
  }

  return "";
}

function normalizeAttachmentFilename(value: string | undefined, index: number) {
  const trimmed = value?.trim();

  if (trimmed) {
    return trimmed;
  }

  return `attachment-${index + 1}`;
}

function attachmentExtensionFromFilename(filename: string) {
  const match = /\.([a-z0-9]{1,12})$/i.exec(filename.trim());
  return match ? match[1].toLowerCase() : "";
}

function isPreviewableAttachment(contentType: string, filename: string) {
  const normalizedContentType = contentType.trim().toLowerCase();
  const extension = attachmentExtensionFromFilename(filename);

  if (
    normalizedContentType.startsWith("image/") ||
    normalizedContentType.startsWith("text/") ||
    normalizedContentType === "application/pdf"
  ) {
    return true;
  }

  return ["pdf", "png", "jpg", "jpeg", "gif", "webp", "svg", "txt", "md", "json", "csv"].includes(
    extension,
  );
}

function normalizeMalformedAttachmentSpacing(rawSource: string) {
  return rawSource.replace(
    /(Content-Transfer-Encoding:\s*base64\r?\nContent-Disposition:[^\r\n]+(?:\r?\nContent-ID:\s*<[^>]+>)?)\r?\n(?=[A-Za-z0-9+/]{20,}(?:={0,2})(?:\r?\n|$))/gi,
    "$1\r\n\r\n",
  );
}

async function parseRawSource(rawSource: string) {
  return simpleParser(Buffer.from(normalizeMalformedAttachmentSpacing(rawSource), "utf-8"));
}

export async function extractAttachmentsFromRawSource(
  rawSource: string,
): Promise<RemoteMailboxAttachmentMeta[]> {
  const parsed = await parseRawSource(rawSource);

  return parsed.attachments.map((attachment, index) => {
    const filename = normalizeAttachmentFilename(attachment.filename ?? undefined, index);
    const contentType = attachment.contentType?.trim() || "application/octet-stream";

    return {
      contentDisposition: attachment.contentDisposition === "inline" ? "inline" : "attachment",
      contentId: attachment.cid?.trim() || null,
      contentType,
      extension: attachmentExtensionFromFilename(filename),
      filename,
      index,
      isInline: attachment.contentDisposition === "inline",
      isPreviewable: isPreviewableAttachment(contentType, filename),
      sizeBytes:
        typeof attachment.size === "number"
          ? attachment.size
          : Buffer.isBuffer(attachment.content)
            ? attachment.content.length
            : 0,
    } satisfies RemoteMailboxAttachmentMeta;
  });
}

export async function getAttachmentPayloadFromRawSource(
  rawSource: string,
  index: number,
): Promise<RemoteMailboxAttachmentPayload | null> {
  const parsed = await parseRawSource(rawSource);
  const attachment = parsed.attachments[index];

  if (!attachment || !Buffer.isBuffer(attachment.content)) {
    return null;
  }

  const filename = normalizeAttachmentFilename(attachment.filename ?? undefined, index);
  const contentType = attachment.contentType?.trim() || "application/octet-stream";

  return {
    content: attachment.content,
    contentDisposition: attachment.contentDisposition === "inline" ? "inline" : "attachment",
    contentId: attachment.cid?.trim() || null,
    contentType,
    extension: attachmentExtensionFromFilename(filename),
    filename,
    index,
    isInline: attachment.contentDisposition === "inline",
    isPreviewable: isPreviewableAttachment(contentType, filename),
    sizeBytes:
      typeof attachment.size === "number" ? attachment.size : attachment.content.length,
  } satisfies RemoteMailboxAttachmentPayload;
}

function pickFolderEntry(mailboxes: ListResponse[], systemName: BuiltInRemoteFolderSystemName) {
  const definition = REMOTE_FOLDER_DEFINITIONS.find((item) => item.systemName === systemName);

  if (!definition) {
    return null;
  }

  const bySpecialUse =
    definition.specialUse &&
    mailboxes.find((mailbox) => mailbox.specialUse === definition.specialUse);

  if (bySpecialUse) {
    return bySpecialUse;
  }

  return (
    mailboxes.find((mailbox) =>
      definition.fallbacks.some((fallback) => mailbox.path.toLowerCase() === fallback.toLowerCase()),
    ) ?? null
  );
}

async function withImapClient<T>(
  credentials: RemoteMailboxCredentials,
  callback: (client: ImapFlow) => Promise<T>,
) {
  const client = createRemoteMailboxImapClient(credentials);

  try {
    await client.connect();
    return await callback(client);
  } catch (error) {
    const message = error instanceof Error ? error.message : "Remote IMAP request failed";
    throw new Error(`mailbox-imap-failed:${message}`);
  } finally {
    try {
      await client.logout();
    } catch {
      client.close();
    }
  }
}

export function createRemoteMailboxImapClient(
  credentials: RemoteMailboxCredentials,
  options?: RemoteMailboxImapClientOptions,
) {
  const config = getRemoteMailRuntimeConfig();

  return new ImapFlow({
    auth: {
      pass: credentials.password,
      user: credentials.email,
    },
    disableAutoIdle: options?.disableAutoIdle,
    emitLogs: options?.emitLogs,
    host: config.imapHost,
    logger: options?.logger ?? false,
    maxIdleTime: options?.maxIdleTime,
    port: config.imapPort,
    secure: config.imapSecure,
    socketTimeout: options?.socketTimeout,
    tls: {
      rejectUnauthorized: !config.imapAllowSelfSigned,
      servername: config.tlsServerName,
    },
  });
}

function normalizeMailboxFolder(mailbox: ListResponse | null, systemName: RemoteFolderSystemName) {
  const definition = REMOTE_FOLDER_DEFINITIONS.find((item) => item.systemName === systemName);

  return {
    name: definition?.displayName ?? systemName,
    remoteName: mailbox?.path ?? definition?.fallbacks[0] ?? "INBOX",
    systemName,
    totalCount: mailbox?.status?.messages ?? 0,
    unreadCount: mailbox?.status?.unseen ?? 0,
  } satisfies RemoteMailboxFolder;
}

function normalizeCustomMailboxFolder(mailbox: ListResponse | null, input: { name: string; remoteName: string }) {
  return {
    name: input.name,
    remoteName: mailbox?.path ?? input.remoteName,
    systemName: "custom",
    totalCount: mailbox?.status?.messages ?? 0,
    unreadCount: mailbox?.status?.unseen ?? 0,
  } satisfies RemoteMailboxFolder;
}

async function fetchCurrentFolderMessages(
  client: ImapFlow,
  mailboxAddress: string,
  remoteFolder: RemoteMailboxFolder,
  limit: number,
) {
  const lock = await client.getMailboxLock(remoteFolder.remoteName, { readOnly: true });

  try {
    const mailboxState = client.mailbox;
    const exists = mailboxState ? mailboxState.exists ?? 0 : 0;

    if (!exists) {
      return [] as RemoteMailboxMessage[];
    }

    const sequenceStart = Math.max(1, exists - limit + 1);
    const messages: RemoteMailboxMessage[] = [];

    for await (const message of client.fetch(
      `${sequenceStart}:${exists}`,
      {
        envelope: true,
        flags: true,
        internalDate: true,
        source: true,
      },
      { uid: false },
    )) {
      const sourceBuffer = message.source ?? Buffer.from("");
      const parsed = await simpleParser(sourceBuffer);
      const bodyHtml = typeof parsed.html === "string" && parsed.html.trim() ? parsed.html.trim() : null;
      const bodyText = buildMessageBodyText(parsed.text ?? undefined, bodyHtml ?? undefined);
      const envelopeFrom = message.envelope?.from?.[0];
      const parsedFrom = parsed.from?.value?.[0];
      const receivedAt =
        message.internalDate instanceof Date
          ? message.internalDate
          : message.internalDate
            ? new Date(message.internalDate)
            : new Date();
      const toAddresses = formatParsedAddressField(parsed.to) || formatAddressList(message.envelope?.to);

      messages.push({
        bodyHtml,
        bodyText,
        direction: directionForFolder(remoteFolder.systemName),
        fromAddress: parsedFrom?.address?.trim() || envelopeFrom?.address?.trim() || mailboxAddress,
        fromName: parsedFrom?.name?.trim() || envelopeFrom?.name?.trim() || null,
        isRead: Boolean(message.flags?.has("\\Seen")),
        messageIdHeader: parsed.messageId?.trim() || message.envelope?.messageId?.trim() || null,
        rawSource: sourceBuffer.toString("utf-8"),
        receivedAt,
        remoteFlags: [...(message.flags ?? new Set<string>())].sort(),
        remoteFolder: remoteFolder.remoteName,
        remoteUid: message.uid,
        snippet: createSnippet(bodyText),
        subject: parsed.subject?.trim() || message.envelope?.subject?.trim() || "(no subject)",
        toAddresses: toAddresses || mailboxAddress,
        transportResponse:
          remoteFolder.systemName === "sent"
            ? "발송된 메일을 메일함과 동기화했습니다."
            : "메일함과 동기화한 메일입니다.",
        transportStatus: transportStatusForFolder(remoteFolder.systemName),
      });
    }

    return messages.sort((left, right) => {
      const dateDiff = right.receivedAt.getTime() - left.receivedAt.getTime();
      return dateDiff !== 0 ? dateDiff : right.remoteUid - left.remoteUid;
    });
  } finally {
    lock.release();
  }
}

async function ensureRemoteMailbox(client: ImapFlow, path: string) {
  try {
    await client.mailboxCreate(path);
  } catch {
    // mailboxCreate is idempotent enough for our purposes. If the server rejects
    // creation because the mailbox exists or uses a different naming policy, we
    // still attempt to append into the requested path afterwards.
  }
}

export async function createRemoteMailboxFolder(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
) {
  const normalizedFolder = remoteFolder.trim();

  if (!normalizedFolder) {
    return;
  }

  await withImapClient(credentials, async (client) => {
    await ensureRemoteMailbox(client, normalizedFolder);
  });
}

export async function renameRemoteMailboxFolder(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
  nextRemoteFolder: string,
) {
  const normalizedFolder = remoteFolder.trim();
  const normalizedNextFolder = nextRemoteFolder.trim();

  if (!normalizedFolder || !normalizedNextFolder || normalizedFolder === normalizedNextFolder) {
    return;
  }

  await withImapClient(credentials, async (client) => {
    await client.mailboxRename(normalizedFolder, normalizedNextFolder);
  });
}

export async function deleteRemoteMailboxFolder(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
) {
  const normalizedFolder = remoteFolder.trim();

  if (!normalizedFolder) {
    return;
  }

  await withImapClient(credentials, async (client) => {
    await client.mailboxDelete(normalizedFolder);
  });
}

export function encryptMailboxPassword(password: string) {
  const config = getRemoteMailRuntimeConfig();
  const iv = randomBytes(12);
  const cipher = createCipheriv("aes-256-gcm", config.credentialSecret, iv);
  const encrypted = Buffer.concat([cipher.update(password, "utf8"), cipher.final()]);
  const authTag = cipher.getAuthTag();

  return [
    "v1",
    iv.toString("base64url"),
    authTag.toString("base64url"),
    encrypted.toString("base64url"),
  ].join(".");
}

export function decryptMailboxPassword(payload: string) {
  const [version, ivEncoded, authTagEncoded, encryptedEncoded] = normalizeCipherPayload(payload);

  if (version !== "v1" || !ivEncoded || !authTagEncoded || !encryptedEncoded) {
    throw new Error("mailbox-auth-invalid");
  }

  const config = getRemoteMailRuntimeConfig();
  const decipher = createDecipheriv(
    "aes-256-gcm",
    config.credentialSecret,
    Buffer.from(ivEncoded, "base64url"),
  );

  decipher.setAuthTag(Buffer.from(authTagEncoded, "base64url"));

  const decrypted = Buffer.concat([
    decipher.update(Buffer.from(encryptedEncoded, "base64url")),
    decipher.final(),
  ]);

  return decrypted.toString("utf8");
}

export async function syncRemoteMailbox(
  credentials: RemoteMailboxCredentials,
  currentFolder: RemoteSyncTarget,
) {
  const config = getRemoteMailRuntimeConfig();

  return withImapClient(credentials, async (client) => {
    const list = await client.list({
      specialUseHints: {
        drafts: "Drafts",
        junk: "Junk",
        sent: "Sent",
        trash: "Trash",
      },
      statusQuery: {
        messages: true,
        unseen: true,
      },
    });
    const folders = REMOTE_FOLDER_DEFINITIONS.map((definition) =>
      normalizeMailboxFolder(pickFolderEntry(list, definition.systemName), definition.systemName),
    );
    const remoteFolder =
      typeof currentFolder === "string"
        ? folders.find((folder) => folder.systemName === currentFolder) ??
          folders.find((folder) => folder.systemName === "inbox") ??
          folders[0]
        : normalizeCustomMailboxFolder(
            list.find((mailbox) => mailbox.path.toLowerCase() === currentFolder.remoteName.toLowerCase()) ?? null,
            currentFolder,
          );

    if (!remoteFolder) {
      return {
        currentFolderMessages: [],
        currentFolderState: null,
        folders,
      } satisfies RemoteMailboxSyncResult;
    }

    return {
      currentFolderMessages: await fetchCurrentFolderMessages(
        client,
        credentials.email,
        remoteFolder,
        config.syncLimit,
      ),
      currentFolderState: remoteFolder,
      folders,
    } satisfies RemoteMailboxSyncResult;
  });
}

export async function markRemoteMessageAsSeen(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
  remoteUid: number,
) {
  if (!remoteFolder || !remoteUid) {
    return false;
  }

  return withImapClient(credentials, async (client) => {
    const lock = await client.getMailboxLock(remoteFolder);

    try {
      return await client.messageFlagsAdd(remoteUid, ["\\Seen"], { uid: true });
    } finally {
      lock.release();
    }
  });
}

export async function updateRemoteMessageFlag(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
  remoteUid: number,
  flag: string,
  enabled: boolean,
) {
  if (!remoteFolder || !remoteUid || !flag.trim()) {
    return false;
  }

  return withImapClient(credentials, async (client) => {
    const lock = await client.getMailboxLock(remoteFolder);

    try {
      return enabled
        ? await client.messageFlagsAdd(remoteUid, [flag], { uid: true })
        : await client.messageFlagsRemove(remoteUid, [flag], { uid: true });
    } finally {
      lock.release();
    }
  });
}

export async function moveRemoteMessage(
  credentials: RemoteMailboxCredentials,
  sourceRemoteFolder: string,
  remoteUid: number,
  destinationRemoteFolder: string,
): Promise<RemoteMailboxMoveResult | false> {
  if (!sourceRemoteFolder || !destinationRemoteFolder || !remoteUid) {
    return false;
  }

  return withImapClient(credentials, async (client) => {
    await ensureRemoteMailbox(client, destinationRemoteFolder);
    const lock = await client.getMailboxLock(sourceRemoteFolder);

    try {
      const result = await client.messageMove(remoteUid, destinationRemoteFolder, { uid: true });

      if (!result) {
        return false;
      }

      return {
        destination: result.destination || destinationRemoteFolder,
        remoteUid: result.uidMap?.get(remoteUid) ?? null,
      };
    } finally {
      lock.release();
    }
  });
}

export async function deleteRemoteMessages(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
  remoteUids: number[],
) {
  const normalizedUids = [...new Set(
    remoteUids.map((value) => Number(value)).filter((value) => Number.isInteger(value) && value > 0),
  )];

  if (!remoteFolder || normalizedUids.length === 0) {
    return false;
  }

  return withImapClient(credentials, async (client) => {
    const lock = await client.getMailboxLock(remoteFolder);

    try {
      return await client.messageDelete(normalizedUids, { uid: true });
    } finally {
      lock.release();
    }
  });
}

async function appendRemoteMessage(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
  rawSource: string,
  flags: string[],
) {
  return withImapClient(credentials, async (client) => {
    await ensureRemoteMailbox(client, remoteFolder);
    const appendResult = (await client.append(
      remoteFolder,
      rawSource,
      flags,
      new Date(),
    )) as AppendResponseObject | false;

    return appendResult || null;
  });
}

export async function appendMessageToRemoteFolder(
  credentials: RemoteMailboxCredentials,
  remoteFolder: string,
  rawSource: string,
  flags: string[] = [],
) {
  return appendRemoteMessage(credentials, remoteFolder, rawSource, flags);
}

export async function appendDraftMessage(
  credentials: RemoteMailboxCredentials,
  rawSource: string,
) {
  const remoteFolder =
    REMOTE_FOLDER_DEFINITIONS.find((item) => item.systemName === "drafts")?.fallbacks[0] ?? "Drafts";

  return appendRemoteMessage(credentials, remoteFolder, rawSource, ["\\Draft"]);
}

export async function appendSentMessage(
  credentials: RemoteMailboxCredentials,
  rawSource: string,
) {
  const remoteFolder =
    REMOTE_FOLDER_DEFINITIONS.find((item) => item.systemName === "sent")?.fallbacks[0] ?? "Sent";

  return appendRemoteMessage(credentials, remoteFolder, rawSource, ["\\Seen"]);
}

export async function sendSmtpMessage(
  credentials: RemoteMailboxCredentials,
  input: {
    rawSource: string;
    recipients: string[];
  },
) {
  const config = getRemoteMailRuntimeConfig();
  const transporter = nodemailer.createTransport({
    auth: {
      pass: credentials.password,
      user: credentials.email,
    },
    host: config.smtpHost,
    port: config.smtpPort,
    requireTLS: config.smtpRequireTls,
    secure: config.smtpSecure,
    tls: {
      rejectUnauthorized: !config.smtpAllowSelfSigned,
      servername: config.tlsServerName,
    },
  });

  try {
    const info = await transporter.sendMail({
      envelope: {
        from: credentials.email,
        to: input.recipients,
      },
      raw: input.rawSource,
    });

    return {
      messageId: info.messageId,
      response: info.response ?? "SMTP accepted the message.",
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : "Remote SMTP request failed";
    throw new Error(`mailbox-smtp-failed:${message}`);
  }
}
