import { db } from "@/db";
import { tags, offer_macros, services } from "@/db/schema";
import { eq, sql } from "drizzle-orm";
import { getSetting, writeSetting } from "@/lib/settings";

// ── Centralized taxonomy (Notion-style select properties) ───────────────────
// Each taxonomy field has a PERSISTENT, independent option pool stored as a JSON
// array in the `settings` table (key `tax_pool_<fieldId>`). Pools survive
// unassignment: deselecting a value on a row never touches the pool — only an
// explicit delete from the settings panel does (cascade-strips it everywhere).

export type TaxonomyFieldId =
  | "offer_categoria"
  | "offer_ticket"
  | "offer_tipo"
  | "offer_obiettivo"
  | "service_fase"
  | "service_offerta"
  | "service_pacchetto";

export type TaxonomyGroup = "offer" | "catalog";

// Where the field's REAL values live — used for seeding (distinct in-use),
// cascade-delete, and rename propagation.
type Storage =
  | { kind: "column"; table: "offer_macros" | "services"; column: "category" | "ticket" | "fase" }
  | { kind: "tag"; entityType: string };

type FieldConfig = {
  label: string;
  group: TaxonomyGroup;
  settingsKey: string;
  storage: Storage;
};

export const TAXONOMY_FIELDS: Record<TaxonomyFieldId, FieldConfig> = {
  offer_categoria: {
    label: "Categoria",
    group: "offer",
    settingsKey: "tax_pool_offer_categoria",
    storage: { kind: "column", table: "offer_macros", column: "category" },
  },
  offer_ticket: {
    label: "Ticket",
    group: "offer",
    settingsKey: "tax_pool_offer_ticket",
    storage: { kind: "column", table: "offer_macros", column: "ticket" },
  },
  offer_tipo: {
    label: "Tipo",
    group: "offer",
    settingsKey: "tax_pool_offer_tipo",
    storage: { kind: "tag", entityType: "offer_macros.tipo" },
  },
  offer_obiettivo: {
    label: "Obiettivo",
    group: "offer",
    settingsKey: "tax_pool_offer_obiettivo",
    storage: { kind: "tag", entityType: "offer_macros.obiettivo" },
  },
  service_fase: {
    label: "Fase",
    group: "catalog",
    settingsKey: "tax_pool_service_fase",
    storage: { kind: "column", table: "services", column: "fase" },
  },
  service_offerta: {
    label: "Offerta",
    group: "catalog",
    settingsKey: "tax_pool_service_offerta",
    storage: { kind: "tag", entityType: "services" },
  },
  service_pacchetto: {
    label: "Pacchetto",
    group: "catalog",
    settingsKey: "tax_pool_service_pacchetto",
    storage: { kind: "tag", entityType: "services.pacchetto" },
  },
};

export const TAXONOMY_FIELD_IDS = Object.keys(TAXONOMY_FIELDS) as TaxonomyFieldId[];

function sortUnique(values: string[]): string[] {
  return Array.from(new Set(values.filter((v) => v && v.trim().length > 0))).sort((a, b) =>
    a.localeCompare(b, "it")
  );
}

// Resolves a column-storage descriptor to its drizzle column reference.
// Columns are distinct across the config (category/ticket/fase), so switching
// on `column` is unambiguous and keeps drizzle's typing intact.
function columnRef(storage: Extract<Storage, { kind: "column" }>) {
  if (storage.column === "category") return offer_macros.category;
  if (storage.column === "ticket") return offer_macros.ticket;
  return services.fase;
}

// Distinct in-use values for the field — used to seed the pool on first read.
async function readInUseValues(storage: Storage): Promise<string[]> {
  if (storage.kind === "tag") {
    const rows = await db
      .selectDistinct({ name: tags.name })
      .from(tags)
      .where(eq(tags.entity_type, storage.entityType));
    return rows.map((r) => r.name);
  }
  const rows = await db.selectDistinct({ value: columnRef(storage) }).from(
    storage.table === "offer_macros" ? offer_macros : services
  );
  return rows.map((r) => r.value).filter((v): v is string => v != null);
}

// Reads the pool. If the settings row is ABSENT (never seeded), lazy-seeds it
// from in-use values and persists — WITHOUT revalidatePath (safe in render).
// A persisted "[]" is respected (intentionally emptied → no reseed).
export async function getPool(fieldId: TaxonomyFieldId): Promise<string[]> {
  const { settingsKey, storage } = TAXONOMY_FIELDS[fieldId];
  const raw = await getSetting(settingsKey);

  if (raw === null) {
    const seeded = sortUnique(await readInUseValues(storage));
    await writeSetting(settingsKey, JSON.stringify(seeded));
    return seeded;
  }

  try {
    const parsed = JSON.parse(raw);
    return Array.isArray(parsed) ? sortUnique(parsed as string[]) : [];
  } catch {
    return [];
  }
}

export async function getAllPools(): Promise<Record<TaxonomyFieldId, string[]>> {
  const entries = await Promise.all(
    TAXONOMY_FIELD_IDS.map(async (id) => [id, await getPool(id)] as const)
  );
  return Object.fromEntries(entries) as Record<TaxonomyFieldId, string[]>;
}

// Registers a value into the pool (idempotent). Used by the settings "Aggiungi"
// and by inline-create sync across the offer editor / catalog. Never revalidates.
export async function addPoolValue(fieldId: TaxonomyFieldId, value: string): Promise<void> {
  const trimmed = value.trim();
  if (!trimmed) return;
  const current = await getPool(fieldId);
  if (current.includes(trimmed)) return;
  await writeSetting(TAXONOMY_FIELDS[fieldId].settingsKey, JSON.stringify(sortUnique([...current, trimmed])));
}

// Removes a value from the pool AND cascade-strips it from every row using it
// (delete tag rows / null out the column). Settings-only (global delete).
export async function removePoolValue(fieldId: TaxonomyFieldId, value: string): Promise<void> {
  const { settingsKey, storage } = TAXONOMY_FIELDS[fieldId];
  const current = await getPool(fieldId);
  await writeSetting(settingsKey, JSON.stringify(current.filter((v) => v !== value)));

  if (storage.kind === "tag") {
    await db
      .delete(tags)
      .where(sql`${tags.entity_type} = ${storage.entityType} and ${tags.name} = ${value}`);
  } else if (storage.column === "category") {
    await db.update(offer_macros).set({ category: null }).where(eq(offer_macros.category, value));
  } else if (storage.column === "ticket") {
    await db.update(offer_macros).set({ ticket: null }).where(eq(offer_macros.ticket, value));
  } else {
    await db.update(services).set({ fase: null }).where(eq(services.fase, value));
  }
}

// Renames a value in the pool AND propagates to every row using it.
export async function renamePoolValue(
  fieldId: TaxonomyFieldId,
  oldValue: string,
  newValue: string
): Promise<void> {
  const next = newValue.trim();
  if (!next || next === oldValue) return;
  const { settingsKey, storage } = TAXONOMY_FIELDS[fieldId];

  const current = await getPool(fieldId);
  await writeSetting(
    settingsKey,
    JSON.stringify(sortUnique(current.map((v) => (v === oldValue ? next : v))))
  );

  if (storage.kind === "tag") {
    await db
      .update(tags)
      .set({ name: next })
      .where(sql`${tags.entity_type} = ${storage.entityType} and ${tags.name} = ${oldValue}`);
  } else if (storage.column === "category") {
    await db.update(offer_macros).set({ category: next }).where(eq(offer_macros.category, oldValue));
  } else if (storage.column === "ticket") {
    await db.update(offer_macros).set({ ticket: next }).where(eq(offer_macros.ticket, oldValue));
  } else {
    await db.update(services).set({ fase: next }).where(eq(services.fase, oldValue));
  }
}
