import { eq, and } from "drizzle-orm";
import type { Tx } from "../../lib/audit.js";
import * as schema from "../schema/index.js";
import type { MerkmalSeed } from "./data/merkmale.js";
import type { VehicleTemplateSeed } from "./data/vehicle-templates.js";
import type { EquipmentCategorySeed } from "./data/equipment-categories.js";

/**
 * Idempotente Upserts für Workstream-9-Seeds (Querschnittsstandard 7:
 * ausschließlich `onConflictDoUpdate` auf Natural Keys, mehrfaches Ausführen
 * ändert keine Counts).
 *
 * Natural Keys:
 *  - merkmale.slug
 *  - merkmal_optionen(merkmalId, wert)
 *  - vehicle_templates.code
 *  - vehicle_template_merkmale(templateId, merkmalId)  [PK]
 *  - vehicle_template_aliasse(templateId, alias)
 *  - equipment_categories.name
 *  - equipment_category_merkmale(categoryId, merkmalId) [PK]
 *
 * Alle Funktionen nehmen die laufende Transaktion `tx`, damit der gesamte Seed
 * atomar bleibt.
 */

/** Upsert eines Merkmals (+ Enum-Optionen). Liefert die Merkmal-UUID. */
export async function upsertMerkmal(tx: Tx, m: MerkmalSeed): Promise<string> {
  const [row] = await tx
    .insert(schema.merkmale)
    .values({
      slug: m.slug,
      name: m.name,
      typ: m.typ,
      einheit: m.einheit ?? null,
      geltungsbereich: m.geltungsbereich,
      // Katalog-Merkmale gelten unmittelbar als aktiv (kein Vorschlag).
      status: "active",
    })
    .onConflictDoUpdate({
      target: schema.merkmale.slug,
      set: {
        name: m.name,
        typ: m.typ,
        einheit: m.einheit ?? null,
        geltungsbereich: m.geltungsbereich,
        status: "active",
      },
    })
    .returning({ id: schema.merkmale.id });

  if (!row) throw new Error(`Merkmal-Upsert ohne Rückgabe: ${m.slug}`);

  for (const opt of m.optionen ?? []) {
    await tx
      .insert(schema.merkmalOptionen)
      .values({
        merkmalId: row.id,
        wert: opt.wert,
        label: opt.label,
        reihenfolge: opt.reihenfolge,
      })
      .onConflictDoUpdate({
        target: [schema.merkmalOptionen.merkmalId, schema.merkmalOptionen.wert],
        set: { label: opt.label, reihenfolge: opt.reihenfolge },
      });
  }

  return row.id;
}

/** Upsert einer Vorlage (ohne Merkmale/Aliasse). Liefert die Template-UUID. */
export async function upsertVehicleTemplate(
  tx: Tx,
  t: VehicleTemplateSeed,
  reihenfolge: number,
): Promise<string> {
  const [row] = await tx
    .insert(schema.vehicleTemplates)
    .values({
      code: t.code,
      name: t.name,
      beschreibung: t.beschreibung ?? null,
      reihenfolge,
    })
    .onConflictDoUpdate({
      target: schema.vehicleTemplates.code,
      set: { name: t.name, beschreibung: t.beschreibung ?? null, reihenfolge },
    })
    .returning({ id: schema.vehicleTemplates.id });

  if (!row) throw new Error(`Vorlagen-Upsert ohne Rückgabe: ${t.code}`);
  return row.id;
}

/** Upsert eines Vorlagen-Pflichtmerkmals (PK templateId+merkmalId). */
export async function upsertTemplateMerkmal(
  tx: Tx,
  templateId: string,
  merkmalId: string,
  m: VehicleTemplateSeed["merkmale"][number],
  reihenfolge: number,
): Promise<void> {
  await tx
    .insert(schema.vehicleTemplateMerkmale)
    .values({
      templateId,
      merkmalId,
      vorgabewertNum: m.vorgabewertNum ?? null,
      vorgabewertText: m.vorgabewertText ?? null,
      vorgabewertBool: m.vorgabewertBool ?? null,
      pflicht: m.pflicht ?? false,
      reihenfolge,
    })
    .onConflictDoUpdate({
      target: [
        schema.vehicleTemplateMerkmale.templateId,
        schema.vehicleTemplateMerkmale.merkmalId,
      ],
      set: {
        vorgabewertNum: m.vorgabewertNum ?? null,
        vorgabewertText: m.vorgabewertText ?? null,
        vorgabewertBool: m.vorgabewertBool ?? null,
        pflicht: m.pflicht ?? false,
        reihenfolge,
      },
    });
}

/** Upsert eines Alias (Natural Key templateId+alias). */
export async function upsertTemplateAlias(
  tx: Tx,
  templateId: string,
  alias: string,
  bestaetigt: boolean,
): Promise<void> {
  await tx
    .insert(schema.vehicleTemplateAliasse)
    .values({ templateId, alias, bestaetigt })
    .onConflictDoUpdate({
      target: [
        schema.vehicleTemplateAliasse.templateId,
        schema.vehicleTemplateAliasse.alias,
      ],
      set: { bestaetigt },
    });
}

/** Upsert einer Geräte-Kategorie (Natural Key name). Liefert die UUID. */
export async function upsertEquipmentCategory(
  tx: Tx,
  c: EquipmentCategorySeed,
): Promise<string> {
  const [row] = await tx
    .insert(schema.equipmentCategories)
    .values({ name: c.name, reihenfolge: c.reihenfolge })
    .onConflictDoUpdate({
      target: schema.equipmentCategories.name,
      set: { reihenfolge: c.reihenfolge },
    })
    .returning({ id: schema.equipmentCategories.id });

  if (!row) throw new Error(`Kategorie-Upsert ohne Rückgabe: ${c.name}`);
  return row.id;
}

/**
 * Upsert der Kategorie-Merkmal-Verknüpfung (PK categoryId+merkmalId).
 * Idempotent über `onConflictDoNothing` (keine zusätzlichen Felder zu
 * aktualisieren außer reihenfolge).
 */
export async function upsertCategoryMerkmal(
  tx: Tx,
  categoryId: string,
  merkmalId: string,
  reihenfolge: number,
): Promise<void> {
  await tx
    .insert(schema.equipmentCategoryMerkmale)
    .values({ categoryId, merkmalId, reihenfolge })
    .onConflictDoUpdate({
      target: [
        schema.equipmentCategoryMerkmale.categoryId,
        schema.equipmentCategoryMerkmale.merkmalId,
      ],
      set: { reihenfolge },
    });
}

/**
 * Entfernt Aliasse zu einer Vorlage, die NICHT mehr im Seed stehen
 * (z. B. nachdem ein Alias aus dem Katalog gestrichen wurde). Hält den
 * Aliasse-Bestand exakt deckungsgleich mit dem Seed und damit idempotent,
 * ohne verwaiste Aliasse zu hinterlassen.
 */
export async function pruneTemplateAliasse(
  tx: Tx,
  templateId: string,
  keepAliasse: readonly string[],
): Promise<void> {
  const existing = await tx
    .select({ alias: schema.vehicleTemplateAliasse.alias })
    .from(schema.vehicleTemplateAliasse)
    .where(eq(schema.vehicleTemplateAliasse.templateId, templateId));

  const keep = new Set(keepAliasse);
  for (const e of existing) {
    if (!keep.has(e.alias)) {
      await tx
        .delete(schema.vehicleTemplateAliasse)
        .where(
          and(
            eq(schema.vehicleTemplateAliasse.templateId, templateId),
            eq(schema.vehicleTemplateAliasse.alias, e.alias),
          ),
        );
    }
  }
}
