import { randomBytes } from "crypto";
import { countActiveMagicBindings, listMagicDevices } from "../../db/devices";
import { countActiveProtectedDevices } from "../../db/protectedDevices";
import { getProfile } from "../../db/profile";
import { getPlanLimits } from "../../utils/plan-features";
import { requireUser } from "../../utils/auth";
import { createAdGuardClient } from "../../utils/adguard";
import { sendDeviceAddedPush } from "../../services/push";
import { generateRemovalPassword } from "../../utils/magic-lock";

/**
 * POST /api/magic/register
 *
 * Body: { deviceId: string, hostname: string, model?: string, osVersion?: string }
 *
 * Mac-App ruft nach Login auf. Registriert das Device als Magic-Client,
 * generiert DNS-Token und provisioniert AdGuard Persistent Client.
 *
 * Idempotent: wenn bereits gebunden → return existing token.
 * Wenn Limit erreicht → 409 mit activeBindings-Liste.
 */
export default defineEventHandler(async (event) => {
  const user = await requireUser(event);
  const body = await readBody(event);
  const { deviceId, hostname, model, osVersion, platform } = body as {
    deviceId?: string;
    hostname?: string;
    model?: string;
    osVersion?: string;
    platform?: string;
  };

  if (!hostname) {
    throw createError({
      statusCode: 400,
      message: "hostname required",
    });
  }

  if (!deviceId) {
    throw createError({
      statusCode: 400,
      message: "deviceId required",
    });
  }

  const effectiveDeviceId = deviceId.trim();

  // Plattform: Mac-App sendet nichts (legacy default), Windows-App sendet "windows"
  const devicePlatform =
    platform === "windows" ? "windows" : "macos";

  const db = usePrisma();

  // 1. Prüfe ob Device bereits als Magic-Client gebunden ist (idempotent)
  let existing = await db.userDevice.findUnique({
    where: { userId_deviceId: { userId: user.id, deviceId } },
    select: {
      id: true,
      userId: true,
      deviceId: true,
      magicDnsToken: true,
      magicEnrolledAt: true,
      magicRevokedAt: true,
      magicRemovalPassword: true,
    },
  });

  // Wenn Token existiert und nicht revoked → return existing
  if (
    existing?.magicDnsToken &&
    existing.magicEnrolledAt &&
    !existing.magicRevokedAt
  ) {
    return {
      success: true,
      data: {
        deviceId,
        dnsToken: existing.magicDnsToken,
        profileUrl: `/api/magic/profile.mobileconfig?token=${existing.magicDnsToken}`,
        existing: true,
      },
    };
  }

  // 2. Plan-gated Desktop-Slot-Check (nur wenn kein vorheriges Binding existiert).
  //    Pro: 1 stationäres Gerät (Mac ODER Windows), Legend: 2 (§ Geräte-Matrix).
  //    Grandfathering-Pattern wie bei Custom-Domains: bestehende Bindings bleiben
  //    nach Downgrade aktiv, nur NEUE Registrierungen werden hier geblockt.
  if (!existing || !existing.magicEnrolledAt) {
    const profile = await getProfile(user.id);
    const desktopLimit = getPlanLimits(profile?.plan ?? "pro").maxProtectedDevices;
    // Cross-Counting: Magic-Bindings + legacy ProtectedDevices (manueller
    // Profil-Download) teilen sich denselben Desktop-Slot-Pool.
    const [magicCount, protectedCount] = await Promise.all([
      countActiveMagicBindings(user.id),
      countActiveProtectedDevices(user.id),
    ]);
    const activeCount = magicCount + protectedCount;
    if (activeCount >= desktopLimit) {
      const activeBindings = await listMagicDevices(user.id);
      throw createError({
        statusCode: 409,
        message: `Geräte-Limit erreicht (max. ${desktopLimit} Computer in deinem Plan).`,
        data: {
          code: "limit_reached",
          limit: desktopLimit,
          activeBindings,
        },
      });
    }
  }

  // 3. Generiere DNS-Token (48 char hex)
  // WICHTIG: hex (DNS-Label-konform, kein `_`) UND ≤63 Zeichen. AdGuard nutzt die
  // clientid im DoH-Pfad `/dns-query/{id}` und lehnt >63 Zeichen ab
  // ("hostname label is too long: got 64, max 63"). randomBytes(32).hex = 64 → 1 zu viel.
  // 24 Bytes = 48 hex (192 bit Entropie), wie der historisch funktionierende Client.
  const dnsToken = randomBytes(24).toString("hex");

  // Hard-Lock: server-gehaltenes Removal-Passwort. Stabil über Re-Registrierungen
  // (sonst würde ein laufender Offboarding-Cooldown sein PW wechseln).
  const removalPassword =
    existing?.magicRemovalPassword ?? generateRemovalPassword();

  // 4. Provisioniere AdGuard Client
  const adguardClientName = `magic_${deviceId}`;
  try {
    await createAdGuardClient(adguardClientName, dnsToken, {
      use_global_settings: false,
      filtering_enabled: true,
      parental_enabled: false,
      safebrowsing_enabled: true,
      blocked_services: [], // TODO: Gambling-Filter via AdGuard Blocked-Services
    });
  } catch (err: any) {
    console.error("[Magic] AdGuard provisioning failed:", err);
    throw createError({
      statusCode: 502,
      message: "DNS-Provisioning fehlgeschlagen",
    });
  }

  // 5. Upsert UserDevice (platform="macos" | "windows")
  //    Bei Migration behalten wir die bestehende deviceId bei.
  const upsertDeviceId = existing?.deviceId || effectiveDeviceId;

  const device = await db.userDevice.upsert({
    where: { userId_deviceId: { userId: user.id, deviceId: upsertDeviceId } },
    create: {
      userId: user.id,
      deviceId: upsertDeviceId,
      platform: devicePlatform,
      model: model ?? null,
      name: hostname,
      osVersion: osVersion ?? null,
      magicDnsToken: dnsToken,
      magicEnrolledAt: new Date(),
      magicHostname: hostname,
      magicRemovalPassword: removalPassword,
    },
    update: {
      magicDnsToken: dnsToken,
      magicEnrolledAt: new Date(),
      magicRevokedAt: null, // Clear falls vorher revoked
      magicHostname: hostname,
      model: model ?? undefined,
      osVersion: osVersion ?? undefined,
      lastSeenAt: new Date(),
      magicRemovalPassword: removalPassword,
      magicReleaseRequestedAt: null, // Re-Bind bricht laufenden Release ab
    },
    select: {
      deviceId: true,
      magicDnsToken: true,
    },
  });

  // Account-Security-Push „Neues Gerät verbunden" — nur bei NEUER Bindung
  // (idempotente Re-Registrierungen oben returnen vorher mit existing:true).
  // Fire-and-forget: blockt die Response nicht.
  void sendDeviceAddedPush({
    userId: user.id,
    deviceLabel: hostname,
    platform: devicePlatform,
  });

  return {
    success: true,
    data: {
      deviceId: device.deviceId,
      dnsToken: device.magicDnsToken,
      profileUrl: `/api/magic/profile.mobileconfig?token=${device.magicDnsToken}`,
      existing: false,
    },
  };
});
