import { and, eq, inArray, asc, min as sqlMin, max as sqlMax, sql } from "drizzle-orm";
import { db } from "@/db";
import { merkmale, merkmalOptionen } from "@/db/schema/merkmale";
import { merkmalValues } from "@/db/schema/merkmal-values";
import type { EntityTyp, FacetDef, MerkmalTyp } from "./types";

/**
 * Lädt die Facetten für das dynamische Filter-UI aus dem AKTIVEN Merkmal-Katalog.
 *
 * Auswahlkriterien (Plan WS5):
 * - nur `status = 'active'` (kein `proposed`),
 * - `geltungsbereich IN (entityTyp, 'both')`,
 * - `typ <> 'text'` (Freitext wird nicht als Filter angeboten).
 *
 * Anreicherung:
 * - number: beobachtetes min/max aus `merkmal_values` (für Slider-Grenzen),
 * - enum: Optionen aus `merkmal_optionen`, nach `reihenfolge` sortiert.
 */
export async function getFacets(entityTyp: EntityTyp): Promise<FacetDef[]> {
  const rows = await db
    .select({
      id: merkmale.id,
      name: merkmale.name,
      typ: merkmale.typ,
      einheit: merkmale.einheit,
    })
    .from(merkmale)
    .where(
      and(
        eq(merkmale.status, "active"),
        inArray(merkmale.geltungsbereich, [entityTyp, "both"]),
        sql`${merkmale.typ} <> 'text'`,
      ),
    )
    .orderBy(asc(merkmale.name));

  if (rows.length === 0) return [];

  const numberIds = rows.filter((r) => r.typ === "number").map((r) => r.id);
  const enumIds = rows.filter((r) => r.typ === "enum").map((r) => r.id);

  const ranges = numberIds.length > 0 ? await loadRanges(entityTyp, numberIds) : new Map();
  const optionen = enumIds.length > 0 ? await loadOptionen(enumIds) : new Map();

  return rows.map((r): FacetDef => {
    const base: FacetDef = {
      merkmalId: r.id,
      name: r.name,
      typ: r.typ as MerkmalTyp,
      einheit: r.einheit,
    };
    if (r.typ === "number") {
      const range = ranges.get(r.id);
      if (range) {
        base.min = range.min ?? undefined;
        base.max = range.max ?? undefined;
      }
    } else if (r.typ === "enum") {
      base.optionen = optionen.get(r.id) ?? [];
    }
    return base;
  });
}

async function loadRanges(
  entityTyp: EntityTyp,
  merkmalIds: string[],
): Promise<Map<string, { min: number | null; max: number | null }>> {
  const rows = await db
    .select({
      merkmalId: merkmalValues.merkmalId,
      min: sqlMin(merkmalValues.valueNum),
      max: sqlMax(merkmalValues.valueNum),
    })
    .from(merkmalValues)
    .where(
      and(
        eq(merkmalValues.entityTyp, entityTyp),
        inArray(merkmalValues.merkmalId, merkmalIds),
      ),
    )
    .groupBy(merkmalValues.merkmalId);

  return new Map(
    rows.map((r) => [
      r.merkmalId,
      { min: r.min == null ? null : Number(r.min), max: r.max == null ? null : Number(r.max) },
    ]),
  );
}

async function loadOptionen(
  merkmalIds: string[],
): Promise<Map<string, { wert: string; label: string }[]>> {
  const rows = await db
    .select({
      merkmalId: merkmalOptionen.merkmalId,
      wert: merkmalOptionen.wert,
      label: merkmalOptionen.label,
    })
    .from(merkmalOptionen)
    .where(inArray(merkmalOptionen.merkmalId, merkmalIds))
    .orderBy(asc(merkmalOptionen.reihenfolge), asc(merkmalOptionen.label));

  const out = new Map<string, { wert: string; label: string }[]>();
  for (const r of rows) {
    const list = out.get(r.merkmalId) ?? [];
    list.push({ wert: r.wert, label: r.label });
    out.set(r.merkmalId, list);
  }
  return out;
}
