import { describe, it, expect, vi, beforeEach } from "vitest";

// --- Mocks ---------------------------------------------------------------

const PROPOSED = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
const ZIEL = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb";

// Geteilter, veraenderbarer Zustand. vi.hoisted laeuft vor den (gehoisteten)
// vi.mock-Factories, sodass diese den State sicher referenzieren koennen.
const state = vi.hoisted(() => ({
  // merkmale-Zeilen, die der Top-Level db.select() liefert.
  merkmaleRows: [] as Array<{ id: string; typ: string; status: string }>,
  // Reihenfolge der tx.select()-Ergebnisse fuer vehicle_template_merkmale:
  // [0] = proposed-Templates, [1] = ziel-Templates.
  vtmSelectQueue: [] as Array<Array<{ templateId: string }>>,
  ops: [] as { type: string; table: string; vals?: unknown }[],
}));

function tableName(arg: unknown): string {
  return (arg as { __name?: string })?.__name ?? "unknown";
}

vi.mock("@/db/schema", () => ({
  merkmale: { __name: "merkmale" },
  merkmalValues: { __name: "merkmal_values" },
  vehicleTemplateMerkmale: { __name: "vehicle_template_merkmale" },
}));

function makeTx() {
  return {
    select: () => ({
      from: () => ({
        where: () => Promise.resolve(state.vtmSelectQueue.shift() ?? []),
      }),
    }),
    update: (table: unknown) => ({
      set: (vals: Record<string, unknown>) => ({
        where: () => {
          state.ops.push({ type: "update", table: tableName(table), vals });
          return Promise.resolve(undefined);
        },
      }),
    }),
    delete: (table: unknown) => ({
      where: () => {
        state.ops.push({ type: "delete", table: tableName(table) });
        return Promise.resolve(undefined);
      },
    }),
  };
}

vi.mock("@/db", () => ({
  db: {
    select: () => ({
      from: () => Promise.resolve(state.merkmaleRows),
    }),
    transaction: (cb: (tx: ReturnType<typeof makeTx>) => Promise<unknown>) =>
      cb(makeTx()),
  },
}));

vi.mock("@/lib/auth/guards", () => ({
  requirePlatformAdmin: () =>
    Promise.resolve({ user: { id: "actor-1" } }),
}));

vi.mock("@/lib/audit", () => ({
  writeAudit: () => Promise.resolve(),
}));

vi.mock("next/cache", () => ({
  revalidatePath: () => undefined,
}));

// drizzle-orm Helfer (eq/and/inArray) muessen echte Aufrufe ueberstehen.
vi.mock("drizzle-orm", () => ({
  eq: (...a: unknown[]) => ({ op: "eq", a }),
  and: (...a: unknown[]) => ({ op: "and", a }),
  inArray: (...a: unknown[]) => ({ op: "inArray", a }),
}));

// -------------------------------------------------------------------------

import { mergeMerkmal } from "@/app/(admin)/_actions/proposals";

describe("mergeMerkmal", () => {
  beforeEach(() => {
    state.ops.length = 0;
    state.vtmSelectQueue = [];
    state.merkmaleRows = [
      { id: PROPOSED, typ: "number", status: "proposed" },
      { id: ZIEL, typ: "number", status: "active" },
    ];
  });

  it("lehnt unterschiedliche Typen ab", async () => {
    state.merkmaleRows = [
      { id: PROPOSED, typ: "boolean", status: "proposed" },
      { id: ZIEL, typ: "number", status: "active" },
    ];
    const res = await mergeMerkmal({ proposedId: PROPOSED, zielId: ZIEL });
    expect(res.ok).toBe(false);
  });

  it("haengt ohne Kollision alle vtm-Zeilen um (kein Delete der proposed-vtm)", async () => {
    // proposed in Template T1, Ziel in keinem -> keine Kollision.
    state.vtmSelectQueue = [[{ templateId: "T1" }], []];
    const res = await mergeMerkmal({ proposedId: PROPOSED, zielId: ZIEL });
    expect(res.ok).toBe(true);
    // Es darf kein Kollisions-Delete auf vtm geben, nur das finale
    // merkmale-Delete.
    const vtmDeletes = state.ops.filter(
      (o) => o.type === "delete" && o.table === "vehicle_template_merkmale",
    );
    expect(vtmDeletes).toHaveLength(0);
    const vtmUpdates = state.ops.filter(
      (o) => o.type === "update" && o.table === "vehicle_template_merkmale",
    );
    expect(vtmUpdates).toHaveLength(1);
    expect(
      state.ops.some((o) => o.type === "delete" && o.table === "merkmale"),
    ).toBe(true);
  });

  it("loescht kollidierende proposed-vtm-Zeilen vor dem Umhaengen", async () => {
    // proposed und Ziel teilen sich Template T1 -> Kollision auf PK.
    state.vtmSelectQueue = [[{ templateId: "T1" }], [{ templateId: "T1" }]];
    const res = await mergeMerkmal({ proposedId: PROPOSED, zielId: ZIEL });
    expect(res.ok).toBe(true);
    const vtmDeletes = state.ops.filter(
      (o) => o.type === "delete" && o.table === "vehicle_template_merkmale",
    );
    expect(vtmDeletes).toHaveLength(1);
    // Delete des Kollisions-Eintrags vor dem Umhaengen.
    const deleteIdx = state.ops.findIndex(
      (o) => o.type === "delete" && o.table === "vehicle_template_merkmale",
    );
    const updateIdx = state.ops.findIndex(
      (o) => o.type === "update" && o.table === "vehicle_template_merkmale",
    );
    expect(deleteIdx).toBeLessThan(updateIdx);
  });
});
