import prisma from "../config/database";

export type CatResult<T> = { data: T } | { error: string; status: number };

/** ASCII slug: strip diacritics, lowercase, non-alphanumerics → underscore. */
export function slugifyCategory(label: string): string {
  return (
    label
      .normalize("NFD")
      // U+0300–U+036F = Combining Diacritical Marks. Written as Unicode escapes
      // (not literal bytes) so the regex is robust to source-file encoding.
      .replace(/[\u0300-\u036f]/g, "")
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, "_")
      .replace(/^_+|_+$/g, "")
  );
}

export async function listPlanCategories(includeInactive = true) {
  return prisma.plan_categories.findMany({
    where: includeInactive ? {} : { is_active: true },
    orderBy: [{ sort_order: "asc" }, { id: "asc" }],
  });
}

export async function isCategoryKeyActive(key: string): Promise<boolean> {
  const cat = await prisma.plan_categories.findFirst({
    where: { key, is_active: true },
    select: { id: true },
  });
  return cat !== null;
}

async function uniqueKey(base: string): Promise<string> {
  const root = base || "kategorie";
  // Bound the probe loop so a pathological run of collisions can't spin
  // indefinitely. After MAX_PROBES suffixes (_2 … _101) fall back to a
  // timestamp-suffixed key, which is effectively collision-free.
  const MAX_PROBES = 100;
  let candidate = root;
  for (let n = 2; n < 2 + MAX_PROBES; n++) {
    const exists = await prisma.plan_categories.findUnique({
      where: { key: candidate },
      select: { id: true },
    });
    if (!exists) return candidate;
    candidate = `${root}_${n}`;
  }
  return `${root}_${Date.now()}`;
}

export async function createPlanCategory(input: {
  label: string;
  color: string;
}): Promise<
  CatResult<{
    id: number;
    key: string;
    label: string;
    color: string;
    sort_order: number;
    is_active: boolean;
  }>
> {
  const key = await uniqueKey(slugifyCategory(input.label));
  const max = await prisma.plan_categories.aggregate({
    _max: { sort_order: true },
  });
  const sort_order = (max._max.sort_order ?? 0) + 1;
  const data = await prisma.plan_categories.create({
    data: {
      key,
      label: input.label,
      color: input.color,
      sort_order,
      is_active: true,
    },
  });
  return { data };
}

export async function updatePlanCategory(
  id: number,
  input: { label?: string; color?: string; is_active?: boolean },
): Promise<
  CatResult<{
    id: number;
    key: string;
    label: string;
    color: string;
    sort_order: number;
    is_active: boolean;
  }>
> {
  const existing = await prisma.plan_categories.findUnique({ where: { id } });
  if (!existing) return { error: "Kategorie nenalezena", status: 404 };

  // Guard: never deactivate the last active category.
  if (input.is_active === false && existing.is_active) {
    const activeCount = await prisma.plan_categories.count({
      where: { is_active: true },
    });
    if (activeCount <= 1) {
      return {
        error: "Musí zůstat alespoň jedna aktivní kategorie",
        status: 400,
      };
    }
  }

  const data = await prisma.plan_categories.update({
    where: { id },
    data: {
      label: input.label ?? undefined,
      color: input.color ?? undefined,
      is_active: input.is_active ?? undefined,
    },
  });
  return { data };
}

export async function deactivatePlanCategory(
  id: number,
): Promise<CatResult<{ ok: true }>> {
  const res = await updatePlanCategory(id, { is_active: false });
  if ("error" in res) return res;
  return { data: { ok: true } };
}

/**
 * Hard-delete a category. Blocked when the category is still used by any live
 * (non-soft-deleted) plan entry or override — those rows would otherwise lose
 * their label/color — in which case the caller should hide it instead. Also
 * refuses to remove the last active category so the create form is never empty.
 */
export async function deletePlanCategory(
  id: number,
): Promise<CatResult<{ ok: true }>> {
  const existing = await prisma.plan_categories.findUnique({ where: { id } });
  if (!existing) return { error: "Kategorie nenalezena", status: 404 };

  const [entryCount, overrideCount] = await Promise.all([
    prisma.work_plan_entries.count({
      where: { category: existing.key, is_deleted: false },
    }),
    prisma.work_plan_overrides.count({
      where: { category: existing.key, is_deleted: false },
    }),
  ]);
  if (entryCount + overrideCount > 0) {
    return {
      error:
        "Kategorie je používána v plánu a nelze ji smazat. Můžete ji skrýt.",
      status: 409,
    };
  }

  if (existing.is_active) {
    const activeCount = await prisma.plan_categories.count({
      where: { is_active: true },
    });
    if (activeCount <= 1) {
      return {
        error: "Musí zůstat alespoň jedna aktivní kategorie",
        status: 400,
      };
    }
  }

  await prisma.plan_categories.delete({ where: { id } });
  return { data: { ok: true } };
}
