"use server";

import { and, eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { notFound } from "next/navigation";
import { db } from "@/db";
import { equipment } from "@/db/schema";
import { requireWehrAdmin } from "@/lib/auth/guards";
import { writeAudit } from "@/lib/audit";
import {
  equipmentCreateSchema,
  equipmentUpdateSchema,
  equipmentStatusSchema,
  equipmentIdSchema,
} from "@/lib/validation/equipment";
import { buildMerkmalValuesSchema } from "@/lib/validation/vehicle";
import { getMerkmaleForCategory } from "@/server/data/merkmale";
import {
  getEquipmentForBrigade,
} from "@/server/data/equipment";
import { vehicleBelongsToBrigade } from "@/server/data/vehicles";
import { upsertMerkmalValues } from "@/server/merkmale/upsertValues";
import type { MerkmalDefinition } from "@/lib/merkmale/types";

export type ActionResult =
  | { ok: true; id: string }
  | { ok: false; error: string };

/**
 * Liefert die Merkmal-Definitionen einer Geräte-Kategorie (für die
 * Editor-Vorbefüllung). Guard zuerst (default-deny, auch für Lesen).
 */
export async function getCategoryMerkmaleAction(
  categoryId: string,
): Promise<MerkmalDefinition[]> {
  await requireWehrAdmin();
  if (!categoryId) return [];
  return getMerkmaleForCategory(categoryId);
}

/**
 * Prüft, dass eine optional gewählte `vehicleId` zu EINEM Fahrzeug DERSELBEN
 * Wehr gehört. `undefined` => „im Gerätehaus" (zulässig). Verhindert die
 * Zuordnung zu fremden Fahrzeugen (Scoping).
 */
async function assertVehicleScope(
  vehicleId: string | undefined,
  brigadeId: string,
): Promise<boolean> {
  if (!vehicleId) return true;
  return vehicleBelongsToBrigade(vehicleId, brigadeId);
}

/** Legt ein Gerät der eigenen Wehr an (Guard zuerst, Audit equipment.create). */
export async function createEquipment(
  input: unknown,
  rawWerte: unknown,
): Promise<ActionResult> {
  const s = await requireWehrAdmin();
  const parsed = equipmentCreateSchema.safeParse(input);
  if (!parsed.success) {
    return { ok: false, error: parsed.error.issues[0]?.message ?? "Ungültige Eingabe." };
  }
  const d = parsed.data;
  if (!(await assertVehicleScope(d.vehicleId, s.user.brigadeId))) {
    return { ok: false, error: "Fahrzeug nicht gefunden." };
  }
  const defs = await getMerkmaleForCategory(d.categoryId);
  const werteParsed = buildMerkmalValuesSchema(defs).safeParse(rawWerte ?? []);
  if (!werteParsed.success) {
    return { ok: false, error: werteParsed.error.issues[0]?.message ?? "Ungültige Merkmal-Werte." };
  }

  const id = await db.transaction(async (tx) => {
    const [e] = await tx
      .insert(equipment)
      .values({
        brigadeId: s.user.brigadeId,
        categoryId: d.categoryId,
        vehicleId: d.vehicleId ?? null,
        name: d.name,
      })
      .returning({ id: equipment.id });
    if (!e) throw new Error("Gerät konnte nicht angelegt werden.");
    await upsertMerkmalValues(tx, "equipment", e.id, werteParsed.data);
    await writeAudit(
      s.user.id,
      "equipment.create",
      "equipment",
      e.id,
      { categoryId: d.categoryId, zugeordnet: d.vehicleId != null },
      tx,
    );
    return e.id;
  });

  revalidatePath("/verwaltung/geraete");
  return { ok: true, id };
}

/** Bearbeitet ein Gerät, NUR wenn es der eigenen Wehr gehört. */
export async function updateEquipment(
  input: unknown,
  rawWerte: unknown,
): Promise<ActionResult> {
  const s = await requireWehrAdmin();
  const parsed = equipmentUpdateSchema.safeParse(input);
  if (!parsed.success) {
    return { ok: false, error: parsed.error.issues[0]?.message ?? "Ungültige Eingabe." };
  }
  const d = parsed.data;
  const existing = await getEquipmentForBrigade(d.id, s.user.brigadeId);
  if (!existing) return { ok: false, error: "Gerät nicht gefunden." };
  if (!(await assertVehicleScope(d.vehicleId, s.user.brigadeId))) {
    return { ok: false, error: "Fahrzeug nicht gefunden." };
  }
  const defs = await getMerkmaleForCategory(d.categoryId);
  const werteParsed = buildMerkmalValuesSchema(defs).safeParse(rawWerte ?? []);
  if (!werteParsed.success) {
    return { ok: false, error: werteParsed.error.issues[0]?.message ?? "Ungültige Merkmal-Werte." };
  }

  await db.transaction(async (tx) => {
    await tx
      .update(equipment)
      .set({
        name: d.name,
        categoryId: d.categoryId,
        vehicleId: d.vehicleId ?? null,
      })
      .where(
        and(eq(equipment.id, d.id), eq(equipment.brigadeId, s.user.brigadeId)),
      );
    await upsertMerkmalValues(tx, "equipment", d.id, werteParsed.data);
    await writeAudit(s.user.id, "equipment.update", "equipment", d.id, undefined, tx);
  });

  revalidatePath("/verwaltung/geraete");
  revalidatePath(`/verwaltung/geraete/${d.id}`);
  return { ok: true, id: d.id };
}

/** Setzt den Status eines eigenen Geräts (Audit equipment.status). */
export async function setEquipmentStatus(input: unknown): Promise<ActionResult> {
  const s = await requireWehrAdmin();
  const parsed = equipmentStatusSchema.safeParse(input);
  if (!parsed.success) return { ok: false, error: "Ungültige Eingabe." };
  const existing = await getEquipmentForBrigade(parsed.data.id, s.user.brigadeId);
  if (!existing) return { ok: false, error: "Gerät nicht gefunden." };

  await db.transaction(async (tx) => {
    await tx
      .update(equipment)
      .set({ status: parsed.data.status })
      .where(
        and(
          eq(equipment.id, parsed.data.id),
          eq(equipment.brigadeId, s.user.brigadeId),
        ),
      );
    await writeAudit(
      s.user.id,
      "equipment.status",
      "equipment",
      parsed.data.id,
      { status: parsed.data.status },
      tx,
    );
  });
  revalidatePath("/verwaltung/geraete");
  return { ok: true, id: parsed.data.id };
}

/** Löscht ein eigenes Gerät (Audit equipment.delete). */
export async function deleteEquipment(input: unknown): Promise<ActionResult> {
  const s = await requireWehrAdmin();
  const parsed = equipmentIdSchema.safeParse(input);
  if (!parsed.success) return { ok: false, error: "Ungültige ID." };
  const existing = await getEquipmentForBrigade(parsed.data.id, s.user.brigadeId);
  if (!existing) {
    notFound();
  }

  await db.transaction(async (tx) => {
    await tx
      .delete(equipment)
      .where(
        and(
          eq(equipment.id, parsed.data.id),
          eq(equipment.brigadeId, s.user.brigadeId),
        ),
      );
    await writeAudit(s.user.id, "equipment.delete", "equipment", parsed.data.id, undefined, tx);
  });
  revalidatePath("/verwaltung/geraete");
  return { ok: true, id: parsed.data.id };
}
