import "server-only";

import { createHash } from "node:crypto";
import { applicationDefault, getApps, initializeApp } from "firebase-admin/app";
import { getMessaging } from "firebase-admin/messaging";
import type { RowDataPacket } from "mysql2/promise";

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

const MAX_DEVICE_TOKEN_LENGTH = 2048;

type PushDeviceRow = RowDataPacket & {
  device_token: string;
  id: number;
  last_notified_message_id: number;
  locale: string | null;
  notification_mode: string;
  platform: string;
};

type PushMessageRow = RowDataPacket & {
  from_address: string;
  from_name: string | null;
  id: number;
  received_at: Date;
  remote_folder: string | null;
  remote_uid: number | null;
  snippet: string;
  subject: string;
};

function normalizeEmail(value: string) {
  return value.trim().toLowerCase();
}

function tokenHash(token: string) {
  return createHash("sha256").update(token, "utf8").digest("hex");
}

function getFirebaseMessaging() {
  if (getApps().length === 0) {
    initializeApp({
      credential: applicationDefault(),
      projectId: process.env.FIREBASE_PROJECT_ID?.trim() || undefined,
    });
  }

  return getMessaging();
}

function getFirebaseFailureCode(error: unknown) {
  const code = String((error as { code?: unknown } | null)?.code ?? "").trim();
  return code || "messaging/unknown-error";
}

function isInvalidDeviceToken(code: string) {
  return (
    code === "messaging/invalid-registration-token" ||
    code === "messaging/registration-token-not-registered"
  );
}

export async function registerMailboxPushDevice(input: {
  accountEmail: string;
  deviceToken: string;
  locale?: string | null;
  notificationMode?: string | null;
  platform: string;
}) {
  await ensureOfficialMailSchema();
  const accountEmail = normalizeEmail(input.accountEmail);
  const deviceToken = input.deviceToken.trim();
  const platform = input.platform.trim().toLowerCase();
  const locale = String(input.locale ?? "").trim().slice(0, 8) || null;
  const notificationMode =
    input.notificationMode === "account_subtext" ? "account_subtext" : "system";

  if (!accountEmail || !deviceToken || deviceToken.length > MAX_DEVICE_TOKEN_LENGTH) {
    throw new Error("invalid-push-device");
  }

  if (!new Set(["android", "ios"]).has(platform)) {
    throw new Error("unsupported-push-platform");
  }

  const [userRows] = await getDbPool().query<(RowDataPacket & { id: number })[]>(
    "SELECT id FROM users WHERE email = ? LIMIT 1",
    [accountEmail],
  );
  const userId = Number(userRows[0]?.id ?? 0);

  if (!userId) {
    throw new Error("push-user-not-found");
  }

  const [mailboxRows] = await getDbPool().query<(RowDataPacket & { id: number })[]>(
    "SELECT id FROM mailboxes WHERE user_id = ? AND email = ? ORDER BY id DESC LIMIT 1",
    [userId, accountEmail],
  );

  if (!mailboxRows[0]?.id) {
    throw new Error("push-mailbox-not-found");
  }

  const [messageRows] = await getDbPool().query<(RowDataPacket & { id: number })[]>(
    `
      SELECT COALESCE(MAX(message.id), 0) AS id
      FROM mailbox_messages message
      INNER JOIN mailboxes mailbox ON mailbox.id = message.mailbox_id
      INNER JOIN mailbox_folders folder ON folder.id = message.folder_id
      WHERE mailbox.email = ?
        AND folder.system_name = 'inbox'
        AND message.direction = 'inbound'
    `,
    [accountEmail],
  );
  const baselineMessageId = Number(messageRows[0]?.id ?? 0);

  await getDbPool().query(
    `
      INSERT INTO mailbox_push_devices (
        user_id,
        mailbox_email,
        provider,
        platform,
        locale,
        notification_mode,
        device_token,
        token_hash,
        enabled,
        last_notified_message_id,
        last_registered_at
      ) VALUES (?, ?, 'fcm', ?, ?, ?, ?, ?, 1, ?, NOW())
      ON DUPLICATE KEY UPDATE
        user_id = VALUES(user_id),
        mailbox_email = VALUES(mailbox_email),
        platform = VALUES(platform),
        locale = VALUES(locale),
        notification_mode = VALUES(notification_mode),
        device_token = VALUES(device_token),
        enabled = 1,
        last_notified_message_id = GREATEST(
          mailbox_push_devices.last_notified_message_id,
          VALUES(last_notified_message_id)
        ),
        last_registered_at = NOW(),
        last_failure_at = NULL,
        failure_code = NULL,
        updated_at = NOW()
    `,
    [
      userId,
      accountEmail,
      platform,
      locale,
      notificationMode,
      deviceToken,
      tokenHash(deviceToken),
      baselineMessageId,
    ],
  );

  return { registered: true };
}

export async function unregisterMailboxPushDevice(input: {
  accountEmail: string;
  deviceToken?: string | null;
}) {
  await ensureOfficialMailSchema();
  const accountEmail = normalizeEmail(input.accountEmail);
  const deviceToken = String(input.deviceToken ?? "").trim();

  if (!accountEmail) {
    return { unregistered: false };
  }

  if (deviceToken) {
    await getDbPool().query(
      `
        UPDATE mailbox_push_devices device
        INNER JOIN users user ON user.id = device.user_id
        SET device.enabled = 0, device.updated_at = NOW()
        WHERE user.email = ? AND device.token_hash = ?
      `,
      [accountEmail, tokenHash(deviceToken)],
    );
  } else {
    await getDbPool().query(
      `
        UPDATE mailbox_push_devices device
        INNER JOIN users user ON user.id = device.user_id
        SET device.enabled = 0, device.updated_at = NOW()
        WHERE user.email = ?
      `,
      [accountEmail],
    );
  }

  return { unregistered: true };
}

export async function sendPendingMailboxPushNotifications(accountEmail: string) {
  await ensureOfficialMailSchema();
  const mailboxEmail = normalizeEmail(accountEmail);

  if (!mailboxEmail) {
    return { attempted: 0, delivered: 0, disabled: 0 };
  }

  const [devices] = await getDbPool().query<PushDeviceRow[]>(
    `
      SELECT id, device_token, platform, locale, notification_mode, last_notified_message_id
      FROM mailbox_push_devices
      WHERE mailbox_email = ?
        AND provider = 'fcm'
        AND enabled = 1
      ORDER BY id ASC
    `,
    [mailboxEmail],
  );

  if (devices.length === 0) {
    return { attempted: 0, delivered: 0, disabled: 0 };
  }

  const minimumCursor = Math.min(
    ...devices.map((device) => Number(device.last_notified_message_id ?? 0)),
  );
  const [messages] = await getDbPool().query<PushMessageRow[]>(
    `
      SELECT
        message.id,
        message.remote_uid,
        message.remote_folder,
        message.from_name,
        message.from_address,
        message.subject,
        message.snippet,
        message.received_at
      FROM mailbox_messages message
      INNER JOIN mailboxes mailbox ON mailbox.id = message.mailbox_id
      INNER JOIN mailbox_folders folder ON folder.id = message.folder_id
      WHERE mailbox.email = ?
        AND folder.system_name = 'inbox'
        AND message.direction = 'inbound'
        AND message.id > ?
      ORDER BY message.id ASC
      LIMIT 200
    `,
    [mailboxEmail, minimumCursor],
  );

  if (messages.length === 0) {
    return { attempted: 0, delivered: 0, disabled: 0 };
  }

  const messaging = getFirebaseMessaging();
  let delivered = 0;
  let disabled = 0;

  for (const device of devices) {
    const pending = messages.filter(
      (message) => Number(message.id) > Number(device.last_notified_message_id ?? 0),
    );

    if (pending.length === 0) {
      continue;
    }

    const latest = pending[pending.length - 1];
    const sender = latest.from_name?.trim() || latest.from_address.trim() || "새 메일";
    const subject = latest.subject.trim() || "(제목 없음)";
    const title = pending.length === 1 ? sender : `새 메일 ${pending.length}개`;
    const body = pending.length === 1 ? subject : `${sender} 외 메일이 도착했습니다.`;
    const data = {
      type: "mail",
      mailbox: mailboxEmail,
      folder: "inbox",
      messageId: String(latest.id),
      remoteFolder: latest.remote_folder?.trim() || "INBOX",
      remoteUid: latest.remote_uid ? String(latest.remote_uid) : "",
      notificationTitle: title,
      notificationBody: body,
    };

    try {
      await messaging.send(
        device.platform === "android" &&
          device.notification_mode === "account_subtext"
          ? {
              token: device.device_token,
              data,
              android: {
                priority: "high",
              },
            }
          : device.platform === "ios"
            ? {
              token: device.device_token,
              data,
              apns: {
                headers: {
                  "apns-priority": "10",
                },
                payload: {
                  aps: {
                    alert: {
                      title,
                      subtitle: mailboxEmail,
                      body,
                    },
                    sound: "default",
                    threadId: mailboxEmail,
                  },
                },
              },
            }
            : {
                token: device.device_token,
                notification: { title, body },
                data,
                android: {
                  priority: "high",
                  notification: {
                    channelId: "mail_received",
                    clickAction: "FLUTTER_NOTIFICATION_CLICK",
                    sound: "default",
                  },
                },
              },
      );

      await getDbPool().query(
        `
          UPDATE mailbox_push_devices
          SET
            last_notified_message_id = ?,
            last_success_at = NOW(),
            last_failure_at = NULL,
            failure_code = NULL,
            updated_at = NOW()
          WHERE id = ?
        `,
        [latest.id, device.id],
      );
      delivered += 1;
    } catch (error) {
      const code = getFirebaseFailureCode(error);
      const disableDevice = isInvalidDeviceToken(code);

      await getDbPool().query(
        `
          UPDATE mailbox_push_devices
          SET
            enabled = IF(?, 0, enabled),
            last_failure_at = NOW(),
            failure_code = ?,
            updated_at = NOW()
          WHERE id = ?
        `,
        [disableDevice ? 1 : 0, code.slice(0, 120), device.id],
      );

      if (disableDevice) {
        disabled += 1;
      }
      console.warn(`[mail-push] delivery failed mailbox=${mailboxEmail} code=${code}`);
    }
  }

  return { attempted: devices.length, delivered, disabled };
}
