import { ImapFlow } from "imapflow";
import {
  getMailConnections,
  deleteOldMailBlocked,
  getAlreadyBlockedUidSet,
  insertMailBlocked,
  upsertMailBlockedStat,
  updateMailConnectionScanStats,
  insertMailClassificationSample,
} from "../../db/mail";
import { getBlocklistedDomainsSet, getCustomMailDisplayNames } from "../../db/domains";
import { getProfile } from "../../db/profile";
import { getPlanLimits } from "../../utils/plan-features";
import { resolveProviderMeta } from "../../utils/imap-providers";
import { resolveImapAuth } from "../../utils/mail-auth";
import { classifyMail } from "../../utils/mail-classifier";

/**
 * POST /api/mail/scan
 * Scannt ALLE Ordner (INBOX, Spam, Papierkorb, All Mail …) nach Gambling-Mails.
 * Free-User: nur eigene Domains + Keywords. Pro/Legend: globale Blocklist + eigene.
 *
 * Klassifikations-Pipeline: Layer 0–4 via mail-classifier.ts.
 * Layer 5 (Sample-Capture): nach jeder Klassifikation.
 */
export default defineEventHandler(async (event) => {
  const user = await requireUser(event);

  const connections = await getMailConnections(user.id);
  if (connections.length === 0) {
    throw createError({
      statusCode: 404,
      message: "Kein Mail-Konto verbunden",
    });
  }

  // Consent-Gate (DSGVO Art. 9): Connections ohne explizite Einwilligung überspringen
  const skippedNoConsent = connections.filter((c) => !c.consentAt).length;
  const eligibleConnections = connections.filter((c) => c.consentAt);

  if (skippedNoConsent > 0) {
    console.log(
      `[scan] skipping ${skippedNoConsent} connections — no consent_at (pending re-consent)`,
    );
  }

  // Plan-aware: Free users get only custom domains, Pro/Legend get global blocklist
  const profile = await getProfile(user.id);
  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(user.id);

  const config = useRuntimeConfig(event);
  const msClientId: string = (config.msOauthClientId as string) || process.env.MS_OAUTH_CLIENT_ID || "";

  let totalScanned = 0;
  let totalBlocked = 0;

  for (const connection of eligibleConnections) {
    let imapAuth: { user: string; accessToken: string } | { user: string; pass: string };
    try {
      imapAuth = await resolveImapAuth(connection, msClientId);
    } catch {
      continue;
    }

    const useImplicitTls = !connection.useStarttls;
    const imap = new ImapFlow({
      host: connection.imapHost,
      port: connection.imapPort,
      secure: useImplicitTls,
      ...(connection.useStarttls ? { requireTLS: true } : {}),
      auth: imapAuth,
      logger: false,
      tls: { rejectUnauthorized: connection.rejectUnauthorized ?? true },
    });

    let scanned = 0;
    let newlyBlocked = 0;

    try {
      await imap.connect();

      const mailboxes = await imap.list();
      const scannable = mailboxes.filter(
        (mb: any) => !mb.flags?.has("\\Noselect"),
      );

      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 senderDomains = allMessages
            .map((m: any) =>
              ((m.envelope?.from?.[0]?.address ?? "").toLowerCase().split("@")[1] ?? ""),
            )
            .filter(Boolean);

          const [blockedDomainSet, alreadyBlockedSet, customDisplayNames] = await Promise.all([
            getBlocklistedDomainsSet(senderDomains, user.id, includeGlobal),
            getAlreadyBlockedUidSet(allUids, user.id),
            getCustomMailDisplayNames(user.id),
          ]);

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

          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)}`;

            // Layer 0: Already blocked → skip, kein Sample
            if (alreadyBlockedSet.has(uid)) continue;

            const result = await classifyMail({
              mail: { senderEmail, senderName, subject },
              blockedDomainSet,
              customDisplayNames,
            });

            // Layer 5: Sample-Capture (immer, außer Layer 0)
            const senderDomain = senderEmail.split("@")[1] ?? null;
            sampleInserts.push({
              userId: user.id,
              connectionId: connection.id,
              senderName: senderName?.slice(0, 255) ?? null,
              senderDomain: senderDomain?.slice(0, 255) ?? null,
              relayDecodedDomain: result.relayDecodedDomain?.slice(0, 255) ?? null,
              subject: subject.slice(0, 998) || null,
              features: result.features as unknown as Record<string, unknown>,
              finalAction: result.action,
              triggerSource: result.triggerSource,
            });

            if (result.action !== "blocked") continue;

            uidsToDelete.push(String(msg.uid));
            toInsert.push({
              userId: user.id,
              connectionId: connection.id,
              gmailMessageId: uid,
              senderEmail: senderEmail || "unbekannt",
              senderName,
              subject: subject.slice(0, 200) || "(kein Betreff)",
              receivedAt: msgDate,
              action: "deleted",
              triggerSource: result.triggerSource,
            });
            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.expunge();
              } catch {
                /* ignore */
              }
            }
          }

          await insertMailBlocked(toInsert);

          // Samples fire-and-forget
          if (sampleInserts.length > 0) {
            Promise.all(sampleInserts.map((s) => insertMailClassificationSample(s))).catch((err) => {
              console.warn("[scan] sample insert failed (non-fatal):", err);
            });
          }

          if (toInsert.length > 0) {
            const providerMeta = resolveProviderMeta(connection.imapHost);
            await upsertMailBlockedStat({
              userId: user.id,
              mailConnectionId: connection.id,
              provider: providerMeta.provider,
              providerLabel: providerMeta.providerLabel,
              count: toInsert.length,
            });
          }
        } 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 { ok: true, scanned: totalScanned, blocked: totalBlocked, skippedNoConsent };
});
