import { usePrisma } from "../utils/prisma";

// ─────────────────────────────────────────────────────────────────────────────
// Admin-User-Management — DB-layer
//
// Wird ausschließlich vom Admin-Backend (/api/admin/users/*) verwendet.
// Alle Schreib-Operationen sind audit-relevant — TODO(hans-mueller): sobald
// `audit_log` table existiert, in jeder Update-/Delete-Funktion einen
// Eintrag schreiben (admin-id + target-user + diff + timestamp).
// ─────────────────────────────────────────────────────────────────────────────

export type ListUsersOpts = {
  cursor?: string;
  limit?: number;
  q?: string;
  plan?: "free" | "pro" | "legend";
  /** Default: false → soft-deleted users werden ausgeblendet. */
  includeDeleted?: boolean;
};

export type AdminUserRow = {
  id: string;
  nickname: string | null;
  username: string | null;
  avatar: string | null;
  plan: string;
  streak: number;
  banned: boolean;
  bannedAt: Date | null;
  deletedAt: Date | null;
  createdAt: Date;
  lyraVoiceId: string | null;
  premiumUntil: Date | null;
  proTrialExpiresAt: Date | null;
};

const MAX_LIMIT = 100;
const DEFAULT_LIMIT = 50;

/**
 * List users with cursor-pagination, optional fuzzy-search on nickname/username.
 * Returns one extra row to determine `nextCursor` without a separate count.
 */
export async function listAdminUsers(opts: ListUsersOpts = {}): Promise<{
  items: AdminUserRow[];
  nextCursor: string | null;
}> {
  const db = usePrisma();
  const limit = Math.min(Math.max(opts.limit ?? DEFAULT_LIMIT, 1), MAX_LIMIT);

  const where: Record<string, unknown> = {};
  if (!opts.includeDeleted) where.deletedAt = null;
  if (opts.plan) where.plan = opts.plan;
  if (opts.q && opts.q.trim().length > 0) {
    const term = opts.q.trim();
    where.OR = [
      { nickname: { contains: term, mode: "insensitive" } },
      { username: { contains: term, mode: "insensitive" } },
    ];
  }

  // Cursor-pagination by `id` desc — stable, no skipped rows on inserts.
  // We over-fetch by one to detect "has more".
  const rows = await db.profile.findMany({
    where,
    orderBy: [{ createdAt: "desc" }, { id: "desc" }],
    take: limit + 1,
    ...(opts.cursor ? { cursor: { id: opts.cursor }, skip: 1 } : {}),
    select: {
      id: true,
      nickname: true,
      username: true,
      avatar: true,
      plan: true,
      streak: true,
      banned: true,
      bannedAt: true,
      deletedAt: true,
      createdAt: true,
      lyraVoiceId: true,
      premiumUntil: true,
      proTrialExpiresAt: true,
    },
  });

  const hasMore = rows.length > limit;
  const items = hasMore ? rows.slice(0, limit) : rows;
  const nextCursor = hasMore ? items[items.length - 1]!.id : null;

  return { items, nextCursor };
}

const VALID_PLANS = new Set(["free", "pro", "legend"]);

export type UpdateAdminUserPatch = {
  plan?: string;
  banned?: boolean;
  bannedReason?: string | null;
  lyraVoiceId?: string | null;
};

/**
 * Update plan / ban-status / lyra-voice for a user. Validates plan-enum.
 * Returns the updated row (admin-projection — no PII beyond what admin already
 * sees in the list).
 */
export async function updateAdminUser(
  userId: string,
  patch: UpdateAdminUserPatch,
): Promise<AdminUserRow> {
  const db = usePrisma();
  const data: Record<string, unknown> = {};

  if (patch.plan !== undefined) {
    const planLc = patch.plan.toLowerCase();
    if (!VALID_PLANS.has(planLc)) {
      throw createError({
        statusCode: 400,
        message: `Ungültiger plan-Wert: ${patch.plan} (erwartet: free|pro|legend)`,
      });
    }
    data.plan = planLc;
  }

  if (patch.banned !== undefined) {
    data.banned = patch.banned;
    data.bannedAt = patch.banned ? new Date() : null;
    if (!patch.banned) data.bannedReason = null;
  }
  if (patch.bannedReason !== undefined) {
    data.bannedReason = patch.bannedReason;
  }
  if (patch.lyraVoiceId !== undefined) {
    data.lyraVoiceId = patch.lyraVoiceId;
  }

  if (Object.keys(data).length === 0) {
    throw createError({
      statusCode: 400,
      message: "Keine änderbaren Felder im Body",
    });
  }

  return db.profile.update({
    where: { id: userId },
    data,
    select: {
      id: true,
      nickname: true,
      username: true,
      avatar: true,
      plan: true,
      streak: true,
      banned: true,
      bannedAt: true,
      deletedAt: true,
      createdAt: true,
      lyraVoiceId: true,
      premiumUntil: true,
      proTrialExpiresAt: true,
    },
  });
}

/**
 * DSGVO-konformer Soft-Delete (Art. 17 — Recht auf Vergessenwerden).
 *
 * Was passiert:
 *  1. PII wird scrubbed (nickname → null, username → "deleted-{shortid}",
 *     avatar → null, alle demographics → null)
 *  2. `deletedAt` wird gesetzt → User taucht in normalen Listen nicht mehr auf
 *  3. auth.users-Eintrag bleibt unangetastet (Hard-Delete via Supabase-Admin
 *     erfolgt in separater Operation, sonst FK-Cascade-Risiko bei Posts/Likes)
 *
 * Idempotent — wenn `deletedAt` schon gesetzt ist, wird die Operation übersprungen.
 */
export async function softDeleteAdminUser(userId: string): Promise<{
  ok: boolean;
  alreadyDeleted: boolean;
}> {
  const db = usePrisma();
  const existing = await db.profile.findUnique({
    where: { id: userId },
    select: { deletedAt: true },
  });
  if (!existing) {
    throw createError({ statusCode: 404, message: "User nicht gefunden" });
  }
  if (existing.deletedAt) {
    return { ok: true, alreadyDeleted: true };
  }

  // Kurzer eindeutiger Suffix für scrubbed-username damit unique-constraints
  // (falls künftig vorhanden) nicht kollidieren.
  const shortId = userId.replace(/-/g, "").slice(0, 8);

  await db.profile.update({
    where: { id: userId },
    data: {
      nickname: null,
      username: `deleted-${shortId}`,
      avatar: null,
      // Demographics komplett entfernen
      birthYear: null,
      gender: null,
      maritalStatus: null,
      profession: null,
      employmentStatus: null,
      shiftWork: null,
      industry: null,
      jobTenure: null,
      bundesland: null,
      city: null,
      // Stripe-Refs entfernen — Stripe-Customer wird separat gekündigt
      stripeCustomerId: null,
      stripeSubId: null,
      // Soft-Delete-Marker
      deletedAt: new Date(),
    },
  });

  return { ok: true, alreadyDeleted: false };
}
