import "server-only";

import { isIP } from "node:net";
import type { RowDataPacket } from "mysql2/promise";

import { ensureOfficialMailSchema, getDbPool } from "@/lib/db";

export const DEFAULT_MAIL_SENDING_SERVER_KEY = "default";

export type MailSendingServerSecurityMode = "starttls" | "tls";

export type MailSendingServerOption = {
  assignedDomainCount: number;
  createdAt: string | null;
  enabled: boolean;
  host: string;
  id: number | null;
  isDefault: boolean;
  key: string;
  label: string;
  port: number;
  securityMode: MailSendingServerSecurityMode;
  tlsServerName: string | null;
  updatedAt: string | null;
};

export type MailSendingServerRuntimeConfig = {
  allowSelfSigned: boolean;
  host: string;
  key: string;
  port: number;
  requireTls: boolean;
  secure: boolean;
  tlsServerName?: string;
};

export type MailSendingServerInput = {
  enabled: boolean;
  host: string;
  label: string;
  port: number;
  securityMode: MailSendingServerSecurityMode;
  tlsServerName: string | null;
};

export type PreloadedMailSendingServer = {
  sending_server_enabled: number | null;
  sending_server_host: string | null;
  sending_server_key: string | null;
  sending_server_port: number | null;
  sending_server_security_mode: MailSendingServerSecurityMode | null;
  sending_server_tls_server_name: string | null;
};

type MailSendingServerRow = RowDataPacket & {
  assigned_domain_count: number;
  created_at: Date;
  enabled: number;
  host: string;
  id: number;
  label: string;
  port: number;
  security_mode: MailSendingServerSecurityMode;
  server_key: string;
  tls_server_name: string | null;
  updated_at: Date;
};

type CountRow = RowDataPacket & {
  total: number;
};

type DomainSendingServerRow = RowDataPacket & {
  enabled: number | null;
  host: string | null;
  port: number | null;
  security_mode: MailSendingServerSecurityMode | null;
  sending_server_key: string | null;
  tls_server_name: string | null;
};

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

  if (!value) {
    return fallback;
  }

  switch (value.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 integerValue(value: unknown, fallback: number) {
  const parsed = typeof value === "number" ? value : Number(value);
  return Number.isInteger(parsed) && parsed > 0 && parsed <= 65_535
    ? parsed
    : fallback;
}

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

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

function defaultRemoteHost() {
  return (
    process.env.MAIL_REMOTE_HOST?.trim() ||
    process.env.MX_HOSTNAME?.trim() ||
    hostnameFromUrl(
      process.env.NEXT_PUBLIC_MAIL_APP_URL?.trim() ??
        process.env.NEXT_PUBLIC_APP_URL?.trim(),
    ) ||
    "mx1.officialsite.kr"
  );
}

function defaultTlsServerName(defaultHost: string) {
  return (
    process.env.MAIL_TLS_SERVERNAME?.trim() ||
    process.env.MAIL_SMTP_HOST?.trim() ||
    process.env.MAIL_REMOTE_HOST?.trim() ||
    process.env.MX_HOSTNAME?.trim() ||
    defaultHost
  );
}

function getDefaultMailSendingServer(): MailSendingServerRuntimeConfig {
  const host = process.env.MAIL_SMTP_HOST?.trim() || defaultRemoteHost();
  const defaultAllowSelfSigned = readBooleanEnv(
    "MAILCOW_ALLOW_SELF_SIGNED",
    true,
  );

  return {
    allowSelfSigned: readBooleanEnv(
      "MAIL_SMTP_ALLOW_SELF_SIGNED",
      defaultAllowSelfSigned,
    ),
    host,
    key: DEFAULT_MAIL_SENDING_SERVER_KEY,
    port: integerValue(process.env.MAIL_SMTP_PORT, 587),
    requireTls: readBooleanEnv("MAIL_SMTP_REQUIRE_TLS", true),
    secure: readBooleanEnv("MAIL_SMTP_SECURE", false),
    tlsServerName: defaultTlsServerName(host),
  };
}

function normalizeServerKey(value: unknown) {
  if (typeof value !== "string") {
    return null;
  }

  const normalized = value.trim().toLowerCase();
  return /^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)
    ? normalized
    : null;
}

function normalizeDnsHostname(value: unknown) {
  if (typeof value !== "string") {
    return null;
  }

  const normalized = value.trim().toLowerCase().replace(/\.$/, "");

  if (
    !normalized ||
    normalized.length > 253 ||
    !/^[a-z0-9.-]+$/.test(normalized) ||
    normalized.split(".").some(
      (label) =>
        !label ||
        label.length > 63 ||
        label.startsWith("-") ||
        label.endsWith("-"),
    )
  ) {
    return null;
  }

  return normalized;
}

function normalizeEndpointHost(value: unknown) {
  if (typeof value !== "string") {
    return null;
  }

  const normalized = value.trim().toLowerCase().replace(/\.$/, "");

  if (!normalized || normalized.length > 253) {
    return null;
  }

  return isIP(normalized) ? normalized : normalizeDnsHostname(normalized);
}

function runtimeFromStored(input: {
  enabled: number | boolean | null;
  host: string | null;
  key: string | null;
  port: number | null;
  securityMode: MailSendingServerSecurityMode | null;
  tlsServerName: string | null;
}) {
  const key = normalizeServerKey(input.key);
  const enabled = input.enabled === true || Number(input.enabled) === 1;

  if (
    !key ||
    !enabled ||
    !input.host ||
    !input.port ||
    !input.securityMode
  ) {
    throw new Error(`smtp-server-not-configured:${key ?? "unknown"}`);
  }

  return {
    allowSelfSigned: false,
    host: input.host,
    key,
    port: Number(input.port),
    requireTls: input.securityMode === "starttls",
    secure: input.securityMode === "tls",
    tlsServerName: input.tlsServerName || undefined,
  } satisfies MailSendingServerRuntimeConfig;
}

function optionFromRow(row: MailSendingServerRow): MailSendingServerOption {
  return {
    assignedDomainCount: Number(row.assigned_domain_count),
    createdAt: row.created_at.toISOString(),
    enabled: Number(row.enabled) === 1,
    host: row.host,
    id: Number(row.id),
    isDefault: false,
    key: row.server_key,
    label: row.label,
    port: Number(row.port),
    securityMode: row.security_mode,
    tlsServerName: row.tls_server_name,
    updatedAt: row.updated_at.toISOString(),
  };
}

export function normalizeMailSendingServerInput(value: unknown): MailSendingServerInput {
  const raw = value && typeof value === "object"
    ? value as Record<string, unknown>
    : {};
  const label = typeof raw.label === "string" ? raw.label.trim() : "";
  const host = normalizeEndpointHost(raw.host);
  const port = Number(raw.port);
  const securityMode = raw.securityMode;
  const rawTlsServerName = typeof raw.tlsServerName === "string"
    ? raw.tlsServerName.trim()
    : "";
  const tlsServerName = rawTlsServerName
    ? normalizeDnsHostname(rawTlsServerName)
    : host && !isIP(host)
      ? host
      : null;

  if (!label || label.length > 80) {
    throw new Error("kavenix-sending-server-label-invalid");
  }

  if (!host) {
    throw new Error("kavenix-sending-server-host-invalid");
  }

  if (!Number.isInteger(port) || port < 1 || port > 65_535) {
    throw new Error("kavenix-sending-server-port-invalid");
  }

  if (securityMode !== "starttls" && securityMode !== "tls") {
    throw new Error("kavenix-sending-server-security-invalid");
  }

  if (rawTlsServerName && !tlsServerName) {
    throw new Error("kavenix-sending-server-tls-host-invalid");
  }

  return {
    enabled: raw.enabled !== false,
    host,
    label,
    port,
    securityMode,
    tlsServerName,
  };
}

export async function getMailSendingServerOptions(): Promise<MailSendingServerOption[]> {
  await ensureOfficialMailSchema();

  const [[rows], [defaultCountRows]] = await Promise.all([
    getDbPool().query<MailSendingServerRow[]>(
      `
        SELECT
          server.id,
          server.server_key,
          server.label,
          server.host,
          server.port,
          server.security_mode,
          server.tls_server_name,
          server.enabled,
          server.created_at,
          server.updated_at,
          (
            SELECT COUNT(*)
            FROM domains domain_assignment
            WHERE domain_assignment.sending_server_key = server.server_key
              AND domain_assignment.mailcow_cleanup_at IS NULL
          ) AS assigned_domain_count
        FROM mail_sending_servers server
        ORDER BY server.enabled DESC, server.label ASC, server.id ASC
      `,
    ),
    getDbPool().query<CountRow[]>(
      `
        SELECT COUNT(*) AS total
        FROM domains
        WHERE sending_server_key IS NULL
          AND mailcow_cleanup_at IS NULL
      `,
    ),
  ]);
  const defaultServer = getDefaultMailSendingServer();

  return [
    {
      assignedDomainCount: Number(defaultCountRows[0]?.total ?? 0),
      createdAt: null,
      enabled: true,
      host: defaultServer.host,
      id: null,
      isDefault: true,
      key: defaultServer.key,
      label: process.env.MAIL_SMTP_DEFAULT_LABEL?.trim() || "기본 메일 서버",
      port: defaultServer.port,
      securityMode: defaultServer.secure ? "tls" : "starttls",
      tlsServerName: defaultServer.tlsServerName ?? null,
      updatedAt: null,
    },
    ...rows.map(optionFromRow),
  ];
}

export async function requireConfiguredMailSendingServerKey(value: unknown) {
  const key = normalizeServerKey(value);

  if (!key) {
    throw new Error("kavenix-sending-server-invalid");
  }

  if (key === DEFAULT_MAIL_SENDING_SERVER_KEY) {
    return key;
  }

  await ensureOfficialMailSchema();
  const [rows] = await getDbPool().query<(RowDataPacket & { id: number })[]>(
    `
      SELECT id
      FROM mail_sending_servers
      WHERE server_key = ?
        AND enabled = 1
      LIMIT 1
    `,
    [key],
  );

  if (!rows[0]) {
    throw new Error("kavenix-sending-server-not-configured");
  }

  return key;
}

export async function getConfiguredMailSendingServer(
  key: string | null | undefined,
) {
  const normalizedKey = normalizeServerKey(key) ?? DEFAULT_MAIL_SENDING_SERVER_KEY;

  if (normalizedKey === DEFAULT_MAIL_SENDING_SERVER_KEY) {
    return getDefaultMailSendingServer();
  }

  await ensureOfficialMailSchema();
  const [rows] = await getDbPool().query<DomainSendingServerRow[]>(
    `
      SELECT
        server_key AS sending_server_key,
        host,
        port,
        security_mode,
        tls_server_name,
        enabled
      FROM mail_sending_servers
      WHERE server_key = ?
      LIMIT 1
    `,
    [normalizedKey],
  );
  const server = rows[0];

  return runtimeFromStored({
    enabled: server?.enabled ?? null,
    host: server?.host ?? null,
    key: normalizedKey,
    port: server?.port ?? null,
    securityMode: server?.security_mode ?? null,
    tlsServerName: server?.tls_server_name ?? null,
  });
}

export function getPreloadedMailSendingServer(
  server: PreloadedMailSendingServer,
) {
  if (!server.sending_server_key) {
    return getDefaultMailSendingServer();
  }

  return runtimeFromStored({
    enabled: server.sending_server_enabled,
    host: server.sending_server_host,
    key: server.sending_server_key,
    port: server.sending_server_port,
    securityMode: server.sending_server_security_mode,
    tlsServerName: server.sending_server_tls_server_name,
  });
}

function senderDomain(email: string) {
  const separatorIndex = email.lastIndexOf("@");

  if (separatorIndex <= 0 || separatorIndex === email.length - 1) {
    return null;
  }

  return email.slice(separatorIndex + 1).trim().toLowerCase();
}

export async function resolveMailSendingServerForSender(email: string) {
  const domain = senderDomain(email);

  if (!domain) {
    return getDefaultMailSendingServer();
  }

  await ensureOfficialMailSchema();

  const [rows] = await getDbPool().query<DomainSendingServerRow[]>(
    `
      SELECT
        domain.sending_server_key,
        server.host,
        server.port,
        server.security_mode,
        server.tls_server_name,
        server.enabled
      FROM domains domain
      LEFT JOIN mail_sending_servers server
        ON server.server_key = domain.sending_server_key
      WHERE LOWER(domain.domain) = ?
        AND domain.mailcow_cleanup_at IS NULL
      ORDER BY domain.id DESC
      LIMIT 1
    `,
    [domain],
  );
  const server = rows[0];

  if (!server?.sending_server_key) {
    return getDefaultMailSendingServer();
  }

  return runtimeFromStored({
    enabled: server.enabled,
    host: server.host,
    key: server.sending_server_key,
    port: server.port,
    securityMode: server.security_mode,
    tlsServerName: server.tls_server_name,
  });
}
