import { randomBytes } from "node:crypto";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import { brigades, users } from "@/db/schema";
import { hashPassword } from "@/lib/auth/password";
import { geocodeAddress } from "@/lib/geo/nominatim";
import { writeAudit } from "@/lib/audit";
import type { BrigadeProvisionInput } from "@/lib/validation/brigade";

/**
 * Erzeugt ein Einmal-Initialpasswort (URL-sicher, 12 Zeichen base64url aus 9
 * Zufalls-Bytes). Wird dem Admin nur einmal angezeigt; nur der Hash wird
 * gespeichert.
 */
export function generateTempPassword(): string {
  return randomBytes(9).toString("base64url");
}

export interface ProvisionResult {
  brigadeId: string;
  userId: string;
  tempPassword: string;
  geocoded: boolean;
}

/**
 * Legt eine Brigade samt erstem `wehr_admin` (lokales Konto) an. Geocoding
 * inline (Adresse -> lat/lng selbst geschrieben). Audit `brigade.create` und
 * `user.create` laufen in derselben Transaktion wie die Inserts.
 *
 * `actorUserId` ist der ausführende platform_admin (aus der Session des
 * aufrufenden Server-Action-Guards).
 */
export async function createBrigadeWithFirstAdmin(
  input: BrigadeProvisionInput,
  actorUserId: string,
): Promise<ProvisionResult> {
  const adr = `${input.strasse}, ${input.plz} ${input.ort}, Österreich`;
  const geo = await geocodeAddress(adr);
  const temp = generateTempPassword();
  const hash = await hashPassword(temp);

  return db.transaction(async (tx) => {
    const [b] = await tx
      .insert(brigades)
      .values({
        name: input.name,
        art: "FF",
        strasse: input.strasse,
        plz: input.plz,
        ort: input.ort,
        bundesland: "Niederösterreich",
        lat: geo.status === "ok" ? geo.coords.lat : null,
        lng: geo.status === "ok" ? geo.coords.lng : null,
        geocodeQuery: adr,
        geocodeStatus: geo.status,
        geocodedAt: new Date(),
        telefon: input.telefon,
        email: input.email ?? null,
        wehrfuehrer: input.wehrfuehrer ?? null,
        aktiv: true,
      })
      .returning();
    if (!b) throw new Error("Brigade konnte nicht angelegt werden.");

    const [u] = await tx
      .insert(users)
      .values({
        brigadeId: b.id,
        rolle: "wehr_admin",
        authTyp: "local",
        email: input.adminEmail,
        name: input.adminName,
        passwortHash: hash,
        aktiv: true,
        erstelltVon: actorUserId,
      })
      .returning();
    if (!u) throw new Error("Benutzer konnte nicht angelegt werden.");

    await writeAudit(
      actorUserId,
      "brigade.create",
      "brigade",
      b.id,
      { geocoded: geo.status === "ok" },
      tx,
    );
    await writeAudit(
      actorUserId,
      "user.create",
      "user",
      u.id,
      { rolle: "wehr_admin", authTyp: "local" },
      tx,
    );

    return {
      brigadeId: b.id,
      userId: u.id,
      tempPassword: temp,
      geocoded: geo.status === "ok",
    };
  });
}

/**
 * Setzt das Passwort eines lokalen Wehr-Benutzers zurück und liefert das neue
 * Einmal-Passwort. Audit `user.reset`. Authentik-Konten haben kein lokales
 * Passwort und werden nicht zurückgesetzt.
 */
export async function resetUserPassword(
  userId: string,
  actorUserId: string,
): Promise<{ tempPassword: string }> {
  const temp = generateTempPassword();
  const hash = await hashPassword(temp);
  await db.transaction(async (tx) => {
    const [u] = await tx
      .select({ authTyp: users.authTyp })
      .from(users)
      .where(eq(users.id, userId));
    if (!u || u.authTyp !== "local") {
      throw new Error("Nur lokale Konten können zurückgesetzt werden.");
    }
    await tx
      .update(users)
      .set({ passwortHash: hash })
      .where(eq(users.id, userId));
    await writeAudit(actorUserId, "user.reset", "user", userId, undefined, tx);
  });
  return { tempPassword: temp };
}
