import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { z } from "zod";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import { users } from "@/db/schema";
import { authConfig } from "./auth.config";
import { verifyPassword } from "@/lib/auth/password";
import { checkRateLimit, recordAttempt } from "@/lib/auth/rate-limit";
import { extractGroups, isAdminGroupMember } from "@/lib/auth/authentik";
import { ROLES } from "@/lib/auth/roles";
import { env } from "@/lib/env";

const credSchema = z.object({
  email: z.string().email(),
  password: z.string().min(1),
});

/**
 * Stellt sicher, dass ein Authentik-Admin (Mitglied der Admin-Gruppe) als
 * platform_admin in `users` existiert — für eine stabile id (Audit/FKs).
 * Idempotent über die eindeutige E-Mail; benötigt KEIN vorheriges Seeding.
 */
async function upsertAuthentikAdmin(email: string, name: string | null) {
  const normalized = email.toLowerCase();
  const rows = await db
    .insert(users)
    .values({
      email: normalized,
      name: name ?? normalized,
      rolle: ROLES.PLATFORM_ADMIN,
      authTyp: "authentik",
      aktiv: true,
      brigadeId: null,
    })
    .onConflictDoUpdate({
      target: users.email,
      set: {
        rolle: ROLES.PLATFORM_ADMIN,
        authTyp: "authentik",
        aktiv: true,
        ...(name ? { name } : {}),
      },
    })
    .returning();
  const row = rows[0];
  if (!row) throw new Error("Authentik-Admin-Upsert lieferte keine Zeile");
  return row;
}

export const { handlers, auth, signIn, signOut } = NextAuth({
  ...authConfig,
  providers: [
    ...authConfig.providers,
    Credentials({
      credentials: { email: {}, password: {} },
      authorize: async (raw) => {
        const parsed = credSchema.safeParse(raw);
        if (!parsed.success) return null;
        const { email, password } = parsed.data;
        const key = `email:${email.toLowerCase()}`;
        // Rate-Limit greift für ALLE Credentials-Logins (default-deny).
        if (!(await checkRateLimit(key))) return null;
        const u = await db.query.users.findFirst({
          where: eq(users.email, email),
        });
        if (!u || !u.aktiv || u.authTyp !== "local" || !u.passwortHash) {
          await recordAttempt(key, "fail");
          return null;
        }
        if (!(await verifyPassword(u.passwortHash, password))) {
          await recordAttempt(key, "fail");
          return null;
        }
        await recordAttempt(key, "ok");
        return {
          id: u.id,
          email: u.email,
          name: u.name,
          role: u.rolle,
          brigadeId: u.brigadeId,
        };
      },
    }),
  ],
  callbacks: {
    ...authConfig.callbacks,
    // Authentik-Login = Admin-Zugang, gesteuert über die Authentik-GRUPPE:
    // Nur Mitglieder von AUTHENTIK_ADMIN_GROUP dürfen rein und werden
    // (idempotent) als platform_admin angelegt. Alle anderen werden abgewiesen.
    async signIn({ user, account, profile }) {
      if (account?.provider === "authentik") {
        const email = user.email;
        if (!email) return false;
        const groups = extractGroups(profile);
        if (!isAdminGroupMember(groups, env.AUTHENTIK_ADMIN_GROUP)) return false;
        const u = await upsertAuthentikAdmin(email, user.name ?? null);
        user.id = u.id;
        user.role = u.rolle;
        user.brigadeId = null;
      }
      return true;
    },
  },
});
