import { awardPoints } from "../../utils/scoring";
import {
  addUserCustomDomain,
  countActiveCustomDomainsSplit,
  CUSTOM_DOMAIN_TYPES,
  type CustomDomainType,
} from "../../db/domains";
import { getProfile } from "../../db/profile";
import { getPlanLimits } from "../../utils/plan-features";
import { usePrisma } from "../../utils/prisma";

// Regex: Domain muss mindestens eine TLD haben (z.B. "casino.de", "x.co.uk")
const DOMAIN_RE = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/;

/**
 * Leitet Frontend-`kind` auf internen `CustomDomainType` ab.
 *
 * Variante A (neues Frontend): { pattern: string, kind: 'web' | 'mail' }
 *   - kind='web'  → type='web'
 *   - kind='mail' → MUSS Domain-Shape haben (mit Punkt + TLD) → type='mail_domain'
 *                   Display-Name-Input (kein Punkt) → throws 400 INVALID_MAIL_DOMAIN
 *                   Wenn pattern volle Adresse (local@domain.tld) → local-part wird gestripped
 *
 * Variante B (direkt / Legacy): { domain: string, type: 'web' | 'mail_domain' | 'mail_display_name' }
 *   - type='mail_display_name' → throws 400 DISPLAY_NAME_NOT_SUPPORTED (v1.0)
 *
 * Variante C (Auto-Detect): { pattern: string } ohne kind
 *   - kind fehlt oder kind='auto' → Auto-Detect anhand pattern-Format
 *   - pattern.includes('@')             → wie kind='mail' (local-part strip, validate, mail_domain)
 *   - pattern.includes('.') && kein '@' → wie kind='web' (validate, web)
 *   - weder '@' noch '.'               → 400 INVALID_PATTERN
 *
 * Display-Name-Blocking kommt in v1.1 mit eigener UX.
 *
 * Returns null wenn ein 400-Error geworfen werden soll — caller wirft dann den Error
 * basierend auf dem zurückgegebenen error-Code.
 */
type ResolveResult =
  | { ok: true; type: CustomDomainType; value: string }
  | { ok: false; error: "INVALID_MAIL_DOMAIN" | "DISPLAY_NAME_NOT_SUPPORTED" | "INVALID_PATTERN" };

function resolveTypeAndValue(body: any): ResolveResult {
  // Variante A + C: pattern vorhanden (mit oder ohne kind)
  if (body?.kind !== undefined || body?.pattern !== undefined) {
    const rawKind = (body?.kind as string)?.trim();
    const pattern = (body?.pattern as string)?.trim() ?? "";

    // Variante C: kind fehlt oder kind='auto' → Auto-Detect anhand pattern-Format
    if (rawKind === undefined || rawKind === null || rawKind === "" || rawKind === "auto") {
      if (pattern.includes("@")) {
        // Wie kind='mail': local-part strippen, als mail_domain speichern
        const atIdx = pattern.lastIndexOf("@");
        const normalized = pattern.slice(atIdx + 1).toLowerCase().replace(/^https?:\/\//, "").trim();
        if (normalized.includes(".") && DOMAIN_RE.test(normalized)) {
          return { ok: true, type: "mail_domain", value: normalized };
        }
        return { ok: false, error: "INVALID_MAIL_DOMAIN" };
      }

      if (pattern.includes(".")) {
        // Wie kind='web'
        return { ok: true, type: "web", value: pattern };
      }

      // Weder '@' noch '.' → kein erkennbares Format
      return { ok: false, error: "INVALID_PATTERN" };
    }

    const kind = rawKind;

    if (kind === "web") {
      return { ok: true, type: "web", value: pattern };
    }

    if (kind === "mail") {
      // Defensiv: wenn volle Adresse übergeben (local@domain.tld) → local-part strippen
      let normalized = pattern;
      if (normalized.includes("@")) {
        const atIdx = normalized.lastIndexOf("@");
        normalized = normalized.slice(atIdx + 1);
      }
      normalized = normalized.toLowerCase().replace(/^https?:\/\//, "").trim();

      // Domain-Shape prüfen: muss Punkt haben und Domain-Regex bestehen
      if (normalized.includes(".") && DOMAIN_RE.test(normalized)) {
        return { ok: true, type: "mail_domain", value: normalized };
      }

      // Sieht nach Display-Name aus (kein Punkt, kein @-Domain) → in v1.0 nicht unterstützt
      return { ok: false, error: "INVALID_MAIL_DOMAIN" };
    }

    // Unbekanntes kind → 400 via validTypes-Check unten
    return { ok: true, type: kind as CustomDomainType, value: pattern };
  }

  // Variante B: domain + type
  const rawType = (body?.type as string)?.trim() ?? "web";
  const value = (body?.domain as string)?.trim() ?? "";

  // v1.0: mail_display_name wird nicht mehr akzeptiert
  if (rawType === "mail_display_name") {
    return { ok: false, error: "DISPLAY_NAME_NOT_SUPPORTED" };
  }

  return { ok: true, type: rawType as CustomDomainType, value };
}

export default defineEventHandler(async (event) => {
  const user = await requireUser(event);
  const body = await readBody(event);

  const resolved = resolveTypeAndValue(body);

  if (!resolved.ok) {
    if (resolved.error === "INVALID_MAIL_DOMAIN") {
      throw createError({
        statusCode: 400,
        data: {
          error: "INVALID_MAIL_DOMAIN",
          message:
            "Mail-Patterns brauchen eine Domain (z.B. only4-subscribers.com). Display-Name-Blocking kommt in einer späteren Version.",
        },
      });
    }
    if (resolved.error === "DISPLAY_NAME_NOT_SUPPORTED") {
      throw createError({
        statusCode: 400,
        data: {
          error: "DISPLAY_NAME_NOT_SUPPORTED",
          message:
            "Mail-Patterns brauchen eine Domain (z.B. only4-subscribers.com). Display-Name-Blocking kommt in einer späteren Version.",
        },
      });
    }
    if (resolved.error === "INVALID_PATTERN") {
      throw createError({
        statusCode: 400,
        data: {
          error: "INVALID_PATTERN",
          message:
            "Bitte eine Domain (z.B. casino.com) oder Mail-Adresse (z.B. info@casino.com) eingeben.",
        },
      });
    }
  }

  const { type, value: rawValue } = resolved as { ok: true; type: CustomDomainType; value: string };

  if (!CUSTOM_DOMAIN_TYPES.includes(type as CustomDomainType)) {
    throw createError({
      statusCode: 400,
      data: { error: "INVALID_TYPE", validTypes: CUSTOM_DOMAIN_TYPES },
    });
  }

  // domain/pattern validieren + normalisieren (nur web + mail_domain hier)
  // mail_domain: value wurde in resolveTypeAndValue bereits normalisiert (lowercase, local-part stripped)
  let value = rawValue;
  value = value.toLowerCase().replace(/^https?:\/\//, "");
  if (!value || !DOMAIN_RE.test(value)) {
    throw createError({ statusCode: 400, data: { error: "INVALID_DOMAIN" } });
  }
  if (type === "mail_domain" && !value.includes(".")) {
    throw createError({
      statusCode: 400,
      data: { error: "MAIL_DOMAIN_MISSING_TLD" },
    });
  }

  // Pre-check: domain already on the global blocklist? Don't burn a slot for
  // something the 208k-domain global filter already covers. Return 200 with a
  // flag so the frontend can surface "already protected, no slot needed"
  // without the user paying for it. mail_domain is included in the same check
  // because mail-domains land in the same blocklist set the daemon scans.
  const db = usePrisma();
  const globalMatch = await db.blocklistDomain.findFirst({
    where: { domain: value, isActive: true },
    select: { domain: true },
  });
  if (globalMatch) {
    return { alreadyGlobal: true, domain: value };
  }

  // Per-type Slot-Limit prüfen
  const profile = await getProfile(user.id);
  const limits = getPlanLimits(profile?.plan ?? "free");

  // Welcher Bucket?
  const bucket: "web" | "mail" = type === "web" ? "web" : "mail";
  const bucketLimit = limits.customDomains[bucket];

  if (bucketLimit !== Infinity) {
    const split = await countActiveCustomDomainsSplit(user.id);
    const currentCount = split[bucket];
    if (currentCount >= bucketLimit) {
      const errorCode = bucket === "web" ? "WEB_LIMIT_REACHED" : "MAIL_LIMIT_REACHED";
      throw createError({
        statusCode: 403,
        data: {
          error: errorCode,
          resource: "custom_domains",
          bucket,
          current: currentCount,
          limit: bucketLimit,
        },
      });
    }
  }

  try {
    const data = await addUserCustomDomain(user.id, value, "manual", type);

    await awardPoints(user.id, "custom_domain_submitted", { domain: value }).catch(
      () => {},
    );

    // Fire-and-forget: Scan sofort triggern damit neue Mail-Domain-Pattern innerhalb
    // von Sekunden wirkt — ohne auf den Cron-Intervall (30min) zu warten.
    // Casts on event: useRuntimeConfig braucht den event-Context für Nitro.
    if (type === "mail_domain" || type === "mail_display_name") {
      const config = useRuntimeConfig(event);
      const adminSecret = (config.adminSecret as string) || process.env.ADMIN_SECRET || "";
      $fetch("/api/mail/scan-internal", {
        method: "POST",
        headers: { "x-admin-secret": adminSecret },
        body: { userId: user.id },
      }).catch((err: unknown) => {
        // Fire-and-forget: Fehler loggen, aber POST-Response nicht blockieren.
        // Der Scan ist best-effort — nächster Cron holt nach.
        console.warn(
          `[custom-domains] post-add scan-trigger failed for user ${user.id}:`,
          err,
        );
      });
    }

    return data;
  } catch (err: any) {
    const msg =
      err.message?.includes("duplicate") || err.code === "P2002"
        ? "Eintrag bereits vorhanden"
        : err.message ?? "Fehler";
    throw createError({ statusCode: 400, message: msg });
  }
});
