import "server-only";

import type { ResultSetHeader, RowDataPacket } from "mysql2/promise";
import { ensureOfficialMailSchema, getDbPool } from "@/lib/db";
import { isValidDomainInput } from "@/lib/domain-input";
import { getMailAbsoluteUrl } from "@/lib/mail-urls";
import {
  DKIM_ROTATION_NOTICE_SIGNUP_CUTOFF,
  sendOfficialMailNotificationEmail,
} from "@/lib/official-mail";

const OPERATIONAL_DOMAIN = "officialsite.kr";
const EXCLUDED_TEST_DOMAINS = new Set(["test.com"]);
const MAX_BATCH_SIZE = 10;
const MAX_SUBJECT_LENGTH = 180;
const MAX_BODY_LENGTH = 10_000;

export type DkimReminderType = "initial_dns" | "dkim_rotation";
export type DkimReminderDeliveryStatus = "sending" | "sent" | "failed";

export type KavenixDkimReminderRow = {
  companyName: string;
  contactable: boolean;
  dkimLastCheckedAt: string | null;
  dkimStatus: "pending" | "verified" | "failed" | null;
  domain: string;
  domainId: number;
  lastReminderAt: string | null;
  lastReminderError: string | null;
  lastReminderStatus: DkimReminderDeliveryStatus | null;
  mailboxStatus: "pending_dns" | "active";
  ownerEmail: string;
  recoveryEmail: string | null;
  reminderCount: number;
  reminderType: DkimReminderType;
  userId: number;
};

export type DkimReminderTemplate = {
  body: string;
  subject: string;
};

export const DKIM_REMINDER_PLACEHOLDERS = [
  "{업체명}",
  "{도메인}",
  "{대표메일}",
  "{보조메일}",
  "{도메인연결링크}",
] as const;

export const DEFAULT_DKIM_REMINDER_TEMPLATES: Record<
  DkimReminderType,
  DkimReminderTemplate
> = {
  initial_dns: {
    subject: "[오피셜메일] {업체명} 도메인 연결 설정을 완료해주세요",
    body: `안녕하세요, {업체명} 담당자님.

{도메인} 도메인의 DNS 연결 설정이 아직 완료되지 않아 안내드립니다.

오피셜메일에 로그인한 뒤 도메인 연결 화면에 표시된 MX, SPF, DKIM, DMARC 값을 도메인의 실제 DNS 관리 서비스에 등록해주세요. 등록 후에는 반드시 연결 상태 확인 버튼을 눌러 검증을 완료해야 합니다.

도메인 연결 화면: {도메인연결링크}

설정 위치를 찾기 어렵거나 직접 연결이 어려우시면 도메인 연결 화면의 안내 또는 설정 대행 서비스를 이용하실 수 있습니다.

이미 설정을 완료하셨다면 연결 상태 확인 버튼을 한 번 더 눌러주세요.

감사합니다.
오피셜메일`,
  },
  dkim_rotation: {
    subject: "[오피셜메일] {업체명} 신규 DKIM 키 재연결 안내",
    body: `안녕하세요, {업체명} 담당자님.

메일 서버 이전에 따라 {도메인} 도메인의 DKIM 키가 새로 발급되었으나, 현재 신규 DKIM 키의 연결 확인이 완료되지 않아 안내드립니다.

기존 메일 수신에는 영향이 없지만 Gmail 등 일부 외부 메일 서비스로 발송할 때 DKIM 값이 일치하지 않으면 발송이 거부되거나 스팸으로 분류될 수 있습니다.

오피셜메일의 도메인 연결 화면에서 현재 표시되는 DKIM 값을 복사해 기존 DKIM TXT 레코드를 교체한 뒤 연결 상태 확인 버튼을 눌러주세요.

도메인 연결 화면: {도메인연결링크}

이미 변경을 완료하셨다면 연결 상태 확인 버튼을 한 번 더 눌러주세요. 검증이 완료된 도메인에는 이후 재연결 안내가 표시되지 않습니다.

감사합니다.
오피셜메일`,
  },
};

type DkimReminderQueryRow = RowDataPacket & {
  company_name: string;
  current_dkim_verified: number;
  dkim_last_checked_at: Date | null;
  dkim_status: "pending" | "verified" | "failed" | null;
  domain: string;
  domain_id: number;
  domain_verified_at: Date | null;
  last_reminder_at: Date | null;
  last_reminder_error: string | null;
  last_reminder_status: DkimReminderDeliveryStatus | null;
  mailbox_status: "pending_dns" | "active" | "disabled" | null;
  owner_email: string;
  recovery_email: string | null;
  recovery_email_verified_at: Date | null;
  reminder_count: number;
  user_id: number;
};

function normalizeDate(value: Date | null) {
  return value instanceof Date && !Number.isNaN(value.getTime())
    ? value.toISOString()
    : null;
}

function normalizeEmail(value: string | null) {
  const email = String(value ?? "").trim().toLowerCase();
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) ? email : null;
}

function normalizeReminderType(value: unknown): DkimReminderType | null {
  return value === "initial_dns" || value === "dkim_rotation" ? value : null;
}

function publicDeliveryError(error: unknown) {
  const code = error instanceof Error ? error.message : "";

  if (code.includes("notification-mailbox-missing")) {
    return "관리자 발송 메일함을 찾을 수 없습니다.";
  }

  if (code.includes("smtp")) {
    return "메일 서버에서 알림 발송을 완료하지 못했습니다.";
  }

  return "알림 메일 발송에 실패했습니다.";
}

function escapeHtml(value: string) {
  return value
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#39;");
}

function renderPlainTextEmailHtml(value: string) {
  const linked = escapeHtml(value).replace(
    /https:\/\/[^\s<]+/g,
    (url) => `<a href="${url}" style="color:#2f6df6;text-decoration:underline">${url}</a>`,
  );

  return `<div style="font-family:Arial,'Noto Sans KR',sans-serif;font-size:14px;line-height:1.8;color:#24344f;white-space:normal">${linked.replaceAll("\n", "<br>")}</div>`;
}

function assertTemplate(value: unknown, field: "body" | "subject") {
  const text = String(value ?? "").trim();
  const maxLength = field === "subject" ? MAX_SUBJECT_LENGTH : MAX_BODY_LENGTH;

  if (!text) {
    throw new Error(field === "subject" ? "reminder-subject-required" : "reminder-body-required");
  }

  if (text.length > maxLength) {
    throw new Error(field === "subject" ? "reminder-subject-too-long" : "reminder-body-too-long");
  }

  const allowed = new Set<string>(DKIM_REMINDER_PLACEHOLDERS);
  const unsupported = [...text.matchAll(/\{[^{}]+\}/g)]
    .map(([token]) => token)
    .find((token) => !allowed.has(token));

  if (unsupported) {
    throw new Error(`reminder-unsupported-placeholder:${unsupported}`);
  }

  return text;
}

export function renderDkimReminderTemplate(
  template: string,
  row: Pick<
    KavenixDkimReminderRow,
    "companyName" | "domain" | "ownerEmail" | "recoveryEmail"
  >,
) {
  const replacements: Record<string, string> = {
    "{업체명}": row.companyName.trim() || "고객",
    "{도메인}": row.domain,
    "{대표메일}": row.ownerEmail,
    "{보조메일}": row.recoveryEmail ?? "",
    "{도메인연결링크}": getMailAbsoluteUrl("/mail?panel=setup&section=domain"),
  };

  return Object.entries(replacements).reduce(
    (result, [token, replacement]) => result.replaceAll(token, replacement),
    template,
  );
}

export async function getKavenixDkimReminderRows() {
  await ensureOfficialMailSchema();
  const [rows] = await getDbPool().query<DkimReminderQueryRow[]>(
    `
      SELECT
        u.id AS user_id,
        u.company_name,
        u.email AS owner_email,
        u.recovery_email,
        u.recovery_email_verified_at,
        d.id AS domain_id,
        d.domain,
        d.verified_at AS domain_verified_at,
        m.status AS mailbox_status,
        dkim.status AS dkim_status,
        dkim.last_checked_at AS dkim_last_checked_at,
        EXISTS(
          SELECT 1
          FROM dns_records current_dkim
          WHERE current_dkim.domain_id = d.id
            AND current_dkim.record_type = 'TXT'
            AND current_dkim.host_name = CONCAT(d.dkim_selector, '._domainkey')
            AND current_dkim.value_text = CONCAT('v=DKIM1; k=rsa; p=', d.dkim_public_key)
            AND current_dkim.status = 'verified'
            AND current_dkim.last_checked_at IS NOT NULL
        ) AS current_dkim_verified,
        latest_reminder.status AS last_reminder_status,
        latest_reminder.error_message AS last_reminder_error,
        COALESCE(latest_reminder.sent_at, latest_reminder.created_at) AS last_reminder_at,
        (
          SELECT COUNT(*)
          FROM domain_dkim_reminder_deliveries reminder_count
          WHERE reminder_count.domain_id = d.id
            AND reminder_count.status = 'sent'
        ) AS reminder_count
      FROM domains d
      INNER JOIN users u ON u.id = d.user_id
      LEFT JOIN mailboxes m
        ON m.id = (
          SELECT MAX(owner_mailbox.id)
          FROM mailboxes owner_mailbox
          WHERE owner_mailbox.domain_id = d.id
            AND owner_mailbox.user_id = d.user_id
        )
      LEFT JOIN dns_records dkim
        ON dkim.id = (
          SELECT MAX(latest_dkim.id)
          FROM dns_records latest_dkim
          WHERE latest_dkim.domain_id = d.id
            AND latest_dkim.record_type = 'TXT'
            AND latest_dkim.host_name = CONCAT(d.dkim_selector, '._domainkey')
        )
      LEFT JOIN domain_dkim_reminder_deliveries latest_reminder
        ON latest_reminder.id = (
          SELECT MAX(reminder.id)
          FROM domain_dkim_reminder_deliveries reminder
          WHERE reminder.domain_id = d.id
        )
      WHERE u.created_at <= ?
        AND d.dkim_enabled = 1
        AND d.mailcow_cleanup_at IS NULL
      ORDER BY u.created_at ASC, d.id ASC
    `,
    [DKIM_ROTATION_NOTICE_SIGNUP_CUTOFF],
  );

  return rows.flatMap<KavenixDkimReminderRow>((row) => {
    const domain = row.domain.trim().toLowerCase();
    const recoveryEmail = normalizeEmail(row.recovery_email);

    if (
      row.current_dkim_verified === 1 ||
      !isValidDomainInput(domain) ||
      domain === OPERATIONAL_DOMAIN ||
      EXCLUDED_TEST_DOMAINS.has(domain) ||
      (row.mailbox_status !== "active" && row.mailbox_status !== "pending_dns")
    ) {
      return [];
    }

    return [
      {
        companyName: row.company_name,
        contactable: Boolean(recoveryEmail && row.recovery_email_verified_at),
        dkimLastCheckedAt: normalizeDate(row.dkim_last_checked_at),
        dkimStatus: row.dkim_status,
        domain,
        domainId: Number(row.domain_id),
        lastReminderAt: normalizeDate(row.last_reminder_at),
        lastReminderError: row.last_reminder_error,
        lastReminderStatus: row.last_reminder_status,
        mailboxStatus: row.mailbox_status,
        ownerEmail: row.owner_email,
        recoveryEmail,
        reminderCount: Number(row.reminder_count ?? 0),
        // A DKIM re-check can temporarily mark the aggregate domain status as failed.
        // Keep domains that completed setup before in the rotation workflow.
        reminderType:
          row.domain_verified_at || row.mailbox_status === "active"
            ? "dkim_rotation"
            : "initial_dns",
        userId: Number(row.user_id),
      },
    ];
  });
}

export async function sendKavenixDkimReminders(input: {
  bodyTemplate: unknown;
  domainIds: unknown;
  reminderType: unknown;
  requestedByEmail: string;
  subjectTemplate: unknown;
}) {
  const reminderType = normalizeReminderType(input.reminderType);
  const subjectTemplate = assertTemplate(input.subjectTemplate, "subject");
  const bodyTemplate = assertTemplate(input.bodyTemplate, "body");
  const domainIds = Array.isArray(input.domainIds)
    ? [...new Set(input.domainIds.map(Number))].filter(
        (domainId) => Number.isInteger(domainId) && domainId > 0,
      )
    : [];

  if (!reminderType) {
    throw new Error("reminder-type-invalid");
  }

  if (domainIds.length === 0 || domainIds.length > MAX_BATCH_SIZE) {
    throw new Error("reminder-domain-selection-invalid");
  }

  const currentRows = await getKavenixDkimReminderRows();
  const byDomainId = new Map(currentRows.map((row) => [row.domainId, row]));
  const results: Array<{
    domainId: number;
    error: string | null;
    sentAt: string | null;
    status: "failed" | "sent" | "skipped";
  }> = [];

  for (const domainId of domainIds) {
    const row = byDomainId.get(domainId);

    if (!row || row.reminderType !== reminderType || !row.contactable || !row.recoveryEmail) {
      results.push({
        domainId,
        error: "현재 발송 대상이 아니거나 인증된 보조 이메일이 없습니다.",
        sentAt: null,
        status: "skipped",
      });
      continue;
    }

    const subject = renderDkimReminderTemplate(subjectTemplate, row);
    const bodyText = renderDkimReminderTemplate(bodyTemplate, row);
    const [insertResult] = await getDbPool().query<ResultSetHeader>(
      `
        INSERT INTO domain_dkim_reminder_deliveries (
          domain_id,
          user_id,
          reminder_type,
          recipient_email,
          subject,
          body_text,
          status,
          requested_by_email
        ) VALUES (?, ?, ?, ?, ?, ?, 'sending', ?)
      `,
      [
        row.domainId,
        row.userId,
        reminderType,
        row.recoveryEmail,
        subject,
        bodyText,
        input.requestedByEmail.trim().toLowerCase(),
      ],
    );
    const deliveryId = Number(insertResult.insertId);

    try {
      await sendOfficialMailNotificationEmail({
        bodyHtml: renderPlainTextEmailHtml(bodyText),
        bodyText,
        recipientEmail: row.recoveryEmail,
        subject,
      });
      const sentAt = new Date();

      await getDbPool().query(
        `
          UPDATE domain_dkim_reminder_deliveries
          SET status = 'sent', sent_at = ?, error_message = NULL, updated_at = NOW()
          WHERE id = ?
        `,
        [sentAt, deliveryId],
      );
      results.push({
        domainId,
        error: null,
        sentAt: sentAt.toISOString(),
        status: "sent",
      });
    } catch (error) {
      const publicError = publicDeliveryError(error);
      const storedError = (error instanceof Error ? error.message : String(error)).slice(0, 500);

      await getDbPool().query(
        `
          UPDATE domain_dkim_reminder_deliveries
          SET status = 'failed', error_message = ?, updated_at = NOW()
          WHERE id = ?
        `,
        [storedError, deliveryId],
      );
      results.push({
        domainId,
        error: publicError,
        sentAt: null,
        status: "failed",
      });
    }
  }

  return results;
}
