import {
  and,
  or,
  eq,
  ilike,
  gte,
  lte,
  inArray,
  sql,
  type SQL,
  type AnyColumn,
} from "drizzle-orm";
import { db } from "@/db";
import { vehicles } from "@/db/schema/assets";
import { merkmalValues } from "@/db/schema/merkmal-values";
import { expandNameQuery } from "@/lib/admin/codes";
import type { EntityTyp, FilterValue, SearchHit, SearchParams } from "./types";

/**
 * Baut die Bedingung für EINEN Merkmal-Filter als korrelierte EXISTS-Subquery
 * gegen `merkmal_values`. Mehrere Filter werden vom Aufrufer UND-verknüpft
 * (je `merkmal_id` eine eigene EXISTS-Klausel), damit ein Fahrzeug ALLE
 * Bedingungen erfüllen muss. Rein (nimmt die Entity-id-Spalte als Parameter),
 * damit die Klausel-Erzeugung ohne laufende DB testbar ist.
 */
export function buildFilterExists(
  filter: FilterValue,
  entityTyp: EntityTyp,
  entityIdCol: AnyColumn,
): SQL {
  const base = and(
    eq(merkmalValues.merkmalId, filter.merkmalId),
    eq(merkmalValues.entityTyp, entityTyp),
    sql`${merkmalValues.entityId} = ${entityIdCol}`,
  );

  let valueCond: SQL | undefined;
  if (filter.typ === "number") {
    const parts: SQL[] = [];
    if (filter.gte !== undefined) parts.push(gte(merkmalValues.valueNum, filter.gte));
    if (filter.lte !== undefined) parts.push(lte(merkmalValues.valueNum, filter.lte));
    valueCond = parts.length > 0 ? and(...parts) : undefined;
  } else if (filter.typ === "boolean") {
    valueCond = eq(merkmalValues.valueBool, filter.eq);
  } else {
    // enum
    valueCond =
      filter.in.length > 0 ? inArray(merkmalValues.valueText, filter.in) : sql`false`;
  }

  const whereInner = valueCond ? and(base, valueCond) : base;
  return sql`EXISTS (SELECT 1 FROM ${merkmalValues} WHERE ${whereInner})`;
}

/**
 * Baut die OR-Bedingung über die expandierten Namensvarianten (HLFA->HLF) auf
 * `name` UND `funkrufname`. Rein/testbar. Liefert `undefined`, wenn kein
 * Suchtext gegeben ist.
 */
export function buildNameCondition(q: string | undefined): SQL | undefined {
  if (!q || q.trim() === "") return undefined;
  const { nameLikes } = expandNameQuery(q);
  const likes = nameLikes.length > 0 ? nameLikes : [q.trim()];
  const conds: SQL[] = [];
  for (const like of likes) {
    const pattern = `%${like}%`;
    conds.push(ilike(vehicles.name, pattern));
    conds.push(ilike(vehicles.funkrufname, pattern));
  }
  return or(...conds);
}

/**
 * Serverseitige, typisierte Fahrzeug-Suche. Liefert UNGEORDNETE `SearchHit[]`
 * (ETA-Sortierung übernimmt der Geo-Workstream). Filter sind UND-verknüpft
 * (je Merkmal eine EXISTS-Klausel). Allrad-Regel via `expandNameQuery`.
 */
export async function searchVehicles(params: SearchParams): Promise<SearchHit[]> {
  const conds: SQL[] = [];

  const nameCond = buildNameCondition(params.q);
  if (nameCond) conds.push(nameCond);

  if (params.nurEinsatzbereit) {
    conds.push(eq(vehicles.status, "einsatzbereit"));
  }

  // Allrad implizit durch HLFA-Eingabe: zusätzlich Merkmal Allradantrieb=Ja,
  // FALLS der Aufrufer eine entsprechende Allrad-merkmalId liefert. Da die
  // merkmalId katalogabhängig ist, wird sie hier NICHT geraten; das Filter-UI
  // setzt sie als regulären boolean-Filter. Die Namensexpansion allein deckt
  // den Trefferraum bereits ab (HLF == HLFA derselben Vorlage).

  for (const f of params.filter) {
    conds.push(buildFilterExists(f, "vehicle", vehicles.id));
  }

  const where = conds.length > 0 ? and(...conds) : undefined;

  const rows = await db
    .select({
      entityId: vehicles.id,
      brigadeId: vehicles.brigadeId,
      name: vehicles.name,
      funkrufname: vehicles.funkrufname,
      status: vehicles.status,
    })
    .from(vehicles)
    .where(where);

  return rows.map((r) => ({
    entityTyp: "vehicle" as const,
    entityId: r.entityId,
    brigadeId: r.brigadeId,
    name: r.name,
    funkrufname: r.funkrufname,
    status: r.status,
  }));
}
