import { describe, it, expect } from "vitest";
import { getTableConfig } from "drizzle-orm/pg-core";
import * as schema from "@/db/schema";

/**
 * Statische Schema-Tests (laufen ohne laufende Datenbank).
 *
 * Diese Tests prüfen die Drizzle-Schema-Definition strukturell:
 * Tabellen, Spalten, Enums, Indizes, Uniques, Fremdschlüssel.
 * Der EAV-Round-Trip und der partielle Unique-Index werden zusätzlich
 * in der Migrations-SQL (migration.test.ts) bzw. gegen eine echte DB
 * (db-roundtrip.test.ts) verifiziert.
 */

describe("Enums", () => {
  it("definiert role mit den drei Rollen und DB-Name 'role'", () => {
    expect(schema.roleEnum.enumName).toBe("role");
    expect(schema.roleEnum.enumValues).toEqual([
      "platform_admin",
      "wehr_admin",
      "wehr_read",
    ]);
  });

  it("definiert auth_typ", () => {
    expect(schema.authTypEnum.enumName).toBe("auth_typ");
    expect(schema.authTypEnum.enumValues).toEqual(["authentik", "local"]);
  });

  it("definiert asset_status mit allen drei Werten", () => {
    expect(schema.assetStatusEnum.enumName).toBe("asset_status");
    expect(schema.assetStatusEnum.enumValues).toEqual([
      "einsatzbereit",
      "wartung",
      "ausser_dienst",
    ]);
  });

  it("definiert merkmal_typ, merkmal_status, geltungsbereich, entity_typ", () => {
    expect(schema.merkmalTypEnum.enumName).toBe("merkmal_typ");
    expect(schema.merkmalTypEnum.enumValues).toEqual([
      "number",
      "enum",
      "boolean",
      "text",
    ]);
    expect(schema.merkmalStatusEnum.enumName).toBe("merkmal_status");
    expect(schema.merkmalStatusEnum.enumValues).toEqual(["active", "proposed"]);
    expect(schema.geltungsbereichEnum.enumName).toBe("geltungsbereich");
    expect(schema.geltungsbereichEnum.enumValues).toEqual([
      "vehicle",
      "equipment",
      "both",
    ]);
    expect(schema.entityTypEnum.enumName).toBe("entity_typ");
    expect(schema.entityTypEnum.enumValues).toEqual(["vehicle", "equipment"]);
  });

  it("hat genau 7 Enums", () => {
    const enumExports = [
      schema.roleEnum,
      schema.authTypEnum,
      schema.merkmalTypEnum,
      schema.merkmalStatusEnum,
      schema.geltungsbereichEnum,
      schema.assetStatusEnum,
      schema.entityTypEnum,
    ];
    const names = new Set(enumExports.map((e) => e.enumName));
    expect(names.size).toBe(7);
    expect(names).toEqual(
      new Set([
        "role",
        "auth_typ",
        "merkmal_typ",
        "merkmal_status",
        "geltungsbereich",
        "asset_status",
        "entity_typ",
      ]),
    );
  });
});

describe("merkmal_values (EAV)", () => {
  it("hat die vier geforderten Indizes", () => {
    const cfg = getTableConfig(schema.merkmalValues);
    const idxNames = new Set(cfg.indexes.map((i) => i.config.name));
    expect(idxNames).toContain("mv_merkmal_num_idx");
    expect(idxNames).toContain("mv_merkmal_bool_idx");
    expect(idxNames).toContain("mv_merkmal_text_idx");
    expect(idxNames).toContain("mv_entity_idx");
  });

  it("hat typisierte Wert-Spalten und polymorphe entity-Spalten", () => {
    const cfg = getTableConfig(schema.merkmalValues);
    const cols = new Set(cfg.columns.map((c) => c.name));
    for (const c of [
      "merkmal_id",
      "entity_typ",
      "entity_id",
      "value_num",
      "value_text",
      "value_bool",
    ]) {
      expect(cols).toContain(c);
    }
  });
});

describe("users", () => {
  it("hat funkrufname NICHT (Funkrufname lebt auf vehicles)", () => {
    const cfg = getTableConfig(schema.users);
    const cols = new Set(cfg.columns.map((c) => c.name));
    expect(cols.has("funkrufname")).toBe(false);
  });

  it("hat Unique auf email und nullbares brigade_id + passwort_hash", () => {
    const cfg = getTableConfig(schema.users);
    const uqNames = new Set(cfg.uniqueConstraints.map((u) => u.name));
    expect(uqNames).toContain("users_email_uq");
    const brigade = cfg.columns.find((c) => c.name === "brigade_id");
    const hash = cfg.columns.find((c) => c.name === "passwort_hash");
    expect(brigade?.notNull).toBe(false);
    expect(hash?.notNull).toBe(false);
  });
});

describe("vehicles", () => {
  it("hat funkrufname als Spalte und status-Enum", () => {
    const cfg = getTableConfig(schema.vehicles);
    const cols = cfg.columns;
    const names = new Set(cols.map((c) => c.name));
    expect(names).toContain("funkrufname");
    const status = cols.find((c) => c.name === "status");
    expect(status?.enumValues).toEqual([
      "einsatzbereit",
      "wartung",
      "ausser_dienst",
    ]);
  });
});

describe("merkmale", () => {
  it("hat slug NOT NULL UNIQUE", () => {
    const cfg = getTableConfig(schema.merkmale);
    const slug = cfg.columns.find((c) => c.name === "slug");
    expect(slug?.notNull).toBe(true);
    const uqNames = new Set(cfg.uniqueConstraints.map((u) => u.name));
    expect(uqNames).toContain("merkmale_slug_uq");
  });
});

describe("vehicle_template_aliasse", () => {
  it("hat bestaetigt-Flag und Unique(template_id, alias) — kein jsonb", () => {
    const cfg = getTableConfig(schema.vehicleTemplateAliasse);
    const cols = cfg.columns;
    const names = new Set(cols.map((c) => c.name));
    expect(names).toContain("bestaetigt");
    expect(names).toContain("alias");
    // kein jsonb auf der Tabelle
    expect(cols.some((c) => c.dataType === "json")).toBe(false);
    const uqNames = new Set(cfg.uniqueConstraints.map((u) => u.name));
    expect(uqNames).toContain("vehicle_template_aliasse_uq");
  });
});

describe("vehicle_templates", () => {
  it("hat Unique auf code", () => {
    const cfg = getTableConfig(schema.vehicleTemplates);
    const uqNames = new Set(cfg.uniqueConstraints.map((u) => u.name));
    expect(uqNames).toContain("vehicle_templates_code_uq");
  });
});

describe("vehicle_template_merkmale", () => {
  it("hat drei typisierte Vorgabewert-Spalten", () => {
    const cfg = getTableConfig(schema.vehicleTemplateMerkmale);
    const names = new Set(cfg.columns.map((c) => c.name));
    expect(names).toContain("vorgabewert_num");
    expect(names).toContain("vorgabewert_text");
    expect(names).toContain("vorgabewert_bool");
  });
});

describe("brigades", () => {
  it("hat lat/lng, geocode-Felder, wehrfuehrer (ASCII) und bundesland-Default", () => {
    const cfg = getTableConfig(schema.brigades);
    const names = new Set(cfg.columns.map((c) => c.name));
    for (const c of [
      "lat",
      "lng",
      "geocode_query",
      "geocoded_at",
      "geocode_status",
      "wehrfuehrer",
      "funkrufname_schema",
      "aktiv",
      "bundesland",
    ]) {
      expect(names).toContain(c);
    }
  });
});

describe("login_attempts & audit_log", () => {
  it("login_attempts hat key/erfolg/zeitpunkt + Index", () => {
    const cfg = getTableConfig(schema.loginAttempts);
    const names = new Set(cfg.columns.map((c) => c.name));
    for (const c of ["key", "erfolg", "zeitpunkt"]) {
      expect(names).toContain(c);
    }
    const idxNames = new Set(cfg.indexes.map((i) => i.config.name));
    expect(idxNames).toContain("login_attempts_key_zeit_idx");
  });

  it("audit_log hat details jsonb und Indizes", () => {
    const cfg = getTableConfig(schema.auditLog);
    const names = new Set(cfg.columns.map((c) => c.name));
    for (const c of ["actor_user_id", "aktion", "ziel_typ", "ziel_id", "details", "zeitpunkt"]) {
      expect(names).toContain(c);
    }
    const details = cfg.columns.find((c) => c.name === "details");
    expect(details?.dataType).toBe("json");
  });
});

describe("equipment", () => {
  it("hat nullbares vehicle_id (= im Gerätehaus) und category_id", () => {
    const cfg = getTableConfig(schema.equipment);
    const names = new Set(cfg.columns.map((c) => c.name));
    expect(names).toContain("category_id");
    const vehicleId = cfg.columns.find((c) => c.name === "vehicle_id");
    expect(vehicleId?.notNull).toBe(false);
  });
});
