import { ImapFlow } from "imapflow";
import {
  getMailConnections,
  deleteOldMailBlocked,
  getAlreadyBlockedUidSet,
  insertMailBlocked,
  updateMailConnectionScanStats,
} from "../../db/mail";
import { getBlocklistedDomainsSet } from "../../db/domains";
import { getProfile } from "../../db/profile";
import { getPlanLimits } from "../../utils/plan-features";
// Single-Source-of-Truth (Mo's Finding #4)
// @ts-expect-error — .mjs ohne types, GAMBLING_KEYWORDS ist string[]
import { GAMBLING_KEYWORDS } from "../../utils/gambling-keywords.mjs";


/**
 * POST /api/mail/scan-internal
 * Called by cron or IMAP proxy. Scans ALL mailbox folders.
 * Free: only custom domains + keywords. Pro/Legend: global blocklist + custom.
 */
export default defineEventHandler(async (event) => {
  const secret = getHeader(event, "x-admin-secret");
  const adminSecret = process.env.NUXT_ADMIN_SECRET || process.env.ADMIN_SECRET;
  if (!secret || !adminSecret || secret !== adminSecret) {
    throw createError({ statusCode: 401, message: "Unauthorized" });
  }

  const body = (await readBody(event)) as { userId?: string };
  const userId = body?.userId;
  if (!userId)
    throw createError({ statusCode: 400, message: "userId missing" });

  const connections = await getMailConnections(userId);
  if (connections.length === 0) return { scanned: 0, blocked: 0 };

  // Plan-aware blocklist
  // Grace-Period: wenn globalBlocklistGraceUntil noch in der Zukunft liegt,
  // behandeln wir den User als 'full' auch wenn sein Plan 'curated' sagt.
  const profile = await getProfile(userId);
  const limits = getPlanLimits(profile?.plan ?? "free");
  const inGrace =
    profile?.globalBlocklistGraceUntil != null &&
    new Date(profile.globalBlocklistGraceUntil) > new Date();
  const includeGlobal = limits.globalBlocklist === "full" || inGrace;

  await deleteOldMailBlocked(userId);

  let totalScanned = 0;
  let totalBlocked = 0;

  for (const connection of connections) {
    let password: string;
    try {
      password = decrypt(connection.passwordEncrypted);
    } catch {
      continue;
    }

    // useStarttls=true → STARTTLS (secure=false + requireTLS=true)
    // rejectUnauthorized=false → self-signed Certs zulassen (nur Custom-IMAP)
    const useImplicitTls = !connection.useStarttls;
    const imap = new ImapFlow({
      host: connection.imapHost,
      port: connection.imapPort,
      secure: useImplicitTls,
      ...(connection.useStarttls ? { requireTLS: true } : {}),
      auth: { user: connection.email, pass: password },
      logger: false,
      tls: { rejectUnauthorized: connection.rejectUnauthorized ?? true },
    });

    let scanned = 0;
    let newlyBlocked = 0;

    try {
      await imap.connect();

      // Scan ALL mailbox folders (not just hardcoded list)
      const mailboxes = await imap.list();
      const scannable = mailboxes.filter(
        (mb: any) => !mb.flags?.has("\\Noselect"),
      );
      console.log(
        `[scan-internal] ${connection.email} scanning ${scannable.length} folders`,
      );

      for (const mb of scannable) {
        let lock: any;
        try {
          lock = await imap.getMailboxLock(mb.path);
        } catch {
          continue;
        }
        try {
          const SCAN_LIMIT = 200;
          const status = await imap.status(mb.path, { messages: true });
          const msgCount = (status as any).messages ?? 0;
          if (msgCount === 0) continue;

          const fetchRange =
            msgCount > SCAN_LIMIT ? `${msgCount - SCAN_LIMIT + 1}:*` : "1:*";
          const allMessages = await imap.fetchAll(fetchRange, {
            envelope: true,
          });
          scanned += allMessages.length;
          totalScanned += allMessages.length;

          const allUids = allMessages.map(
            (m: any) => `${mb.path}:${String(m.uid ?? m.seq)}`,
          );
          const [blockedDomainSet, alreadyBlockedSet] = await Promise.all([
            getBlocklistedDomainsSet(
              allMessages
                .map(
                  (m: any) =>
                    (m.envelope?.from?.[0]?.address ?? "")
                      .toLowerCase()
                      .split("@")[1] ?? "",
                )
                .filter(Boolean),
              userId,
              includeGlobal,
            ),
            getAlreadyBlockedUidSet(allUids, userId),
          ]);

          const toInsert: Parameters<typeof insertMailBlocked>[0] = [];
          const uidsToDelete: string[] = [];

          for (const msg of allMessages) {
            const from = msg.envelope?.from?.[0];
            const senderEmail = (from?.address ?? "").toLowerCase();
            const senderName = from?.name ?? null;
            const subject = (msg.envelope?.subject ?? "").trim();
            const msgDate = msg.envelope?.date ?? new Date();
            const uid = `${mb.path}:${String(msg.uid ?? msg.seq)}`;

            const haystack = `${senderEmail} ${subject}`.toLowerCase();
            const isGamblingKeyword = GAMBLING_KEYWORDS.some((kw) =>
              haystack.includes(kw),
            );
            const senderDomain = senderEmail.split("@")[1] ?? "";
            const isBlocklisted = senderDomain
              ? blockedDomainSet.has(senderDomain)
              : false;

            if (!isGamblingKeyword && !isBlocklisted) continue;
            if (alreadyBlockedSet.has(uid)) continue;

            uidsToDelete.push(String(msg.uid));
            toInsert.push({
              userId,
              connectionId: connection.id,
              gmailMessageId: uid,
              senderEmail: senderEmail || "unbekannt",
              senderName,
              subject: subject.slice(0, 200) || "(kein Betreff)",
              receivedAt: msgDate,
              action: "deleted",
            });
            newlyBlocked++;
          }

          if (uidsToDelete.length > 0) {
            try {
              await imap.messageDelete(uidsToDelete.join(","), { uid: true });
            } catch {
              try {
                for (const uid of uidsToDelete) {
                  await imap
                    .messageFlagsAdd(uid, ["\\Deleted"], { uid: true })
                    .catch(() => {});
                }
                await (imap as any).expunge().catch(() => {});
              } catch {
                /* ignore */
              }
            }
            console.log(
              `[scan-internal] ${connection.email} | ${mb.path} | deleted ${uidsToDelete.length} gambling mails`,
            );
          }

          await insertMailBlocked(toInsert);
        } finally {
          lock.release();
        }
      }

      await imap.logout();
    } catch {
      try {
        await imap.logout();
      } catch {}
    }

    totalBlocked += newlyBlocked;
    await updateMailConnectionScanStats(
      connection.id,
      scanned,
      newlyBlocked,
      connection.emailsBlocked,
      connection.emailsScanned,
      connection.scanInterval,
    );
  }

  return { scanned: totalScanned, blocked: totalBlocked };
});
