export type EngineOverrideKind = "number" | "boolean";

export type EngineOverrideSpec = {
  key: string;
  label: string;
  description: string;
  type: EngineOverrideKind;
  defaultValue: number | boolean;
  min?: number;
  max?: number;
  step?: number;
};

export type EngineOverrideEntry = {
  id: string;
  key: string;
  value: number | boolean;
};

export const ENGINE_OVERRIDE_SPECS: EngineOverrideSpec[] = [
  {
    key: "heightBlurStep",
    label: "Height Blur Step",
    description: "Controls how much blur is added per height level while previewing stacked height layers.",
    type: "number",
    defaultValue: 0.1,
    min: 0,
    max: 1,
    step: 0.05,
  },
  {
    key: "rendererDebug",
    label: "Renderer Debug",
    description: "Shows the live renderer diagnostics panel and enables extra render debugging helpers.",
    type: "boolean",
    defaultValue: false,
  },
];

const ENGINE_OVERRIDE_SPEC_BY_KEY = new Map(ENGINE_OVERRIDE_SPECS.map((spec) => [spec.key, spec]));

export function getEngineOverrideSpec(key: unknown): EngineOverrideSpec | null {
  const normalizedKey = String(key || "").trim();
  return ENGINE_OVERRIDE_SPEC_BY_KEY.get(normalizedKey) || null;
}

export function getDefaultEngineOverrideValue(key: unknown): number | boolean | null {
  const spec = getEngineOverrideSpec(key);
  return spec ? spec.defaultValue : null;
}

export function normalizeEngineOverrideValue(key: unknown, value: unknown): number | boolean | null {
  const spec = getEngineOverrideSpec(key);
  if (!spec) {
    return null;
  }
  if (spec.type === "boolean") {
    if (typeof value === "string") {
      const normalized = String(value || "").trim().toLowerCase();
      if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on") {
        return true;
      }
      if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "off") {
        return false;
      }
    }
    return Boolean(value);
  }
  const normalizedNumber = Number(value);
  const fallbackNumber = Number(spec.defaultValue) || 0;
  const safeNumber = Number.isFinite(normalizedNumber) ? normalizedNumber : fallbackNumber;
  const min = Number.isFinite(Number(spec.min)) ? Number(spec.min) : safeNumber;
  const max = Number.isFinite(Number(spec.max)) ? Number(spec.max) : safeNumber;
  return Math.max(min, Math.min(max, safeNumber));
}

export function normalizeEngineOverrideEntry(value: unknown, fallbackIndex = 0): EngineOverrideEntry | null {
  const source = value && typeof value === "object" && !Array.isArray(value)
    ? value as Record<string, unknown>
    : null;
  if (!source) {
    return null;
  }
  const spec = getEngineOverrideSpec(source.key);
  if (!spec) {
    return null;
  }
  const normalizedValue = normalizeEngineOverrideValue(spec.key, source.value);
  if (normalizedValue == null) {
    return null;
  }
  const fallbackId = `override_${spec.key}_${Math.max(1, Number(fallbackIndex) || 1)}`;
  return {
    id: String(source.id || fallbackId).trim() || fallbackId,
    key: spec.key,
    value: normalizedValue,
  };
}

export function normalizeEngineOverrideEntries(value: unknown): EngineOverrideEntry[] {
  const entries = Array.isArray(value) ? value : [];
  const byKey = new Map<string, EngineOverrideEntry>();
  entries.forEach((entry, index) => {
    const normalized = normalizeEngineOverrideEntry(entry, index + 1);
    if (!normalized) {
      return;
    }
    byKey.set(normalized.key, normalized);
  });
  return ENGINE_OVERRIDE_SPECS
    .map((spec) => byKey.get(spec.key) || null)
    .filter((entry): entry is EngineOverrideEntry => Boolean(entry));
}

export function buildEngineOverrideEntry(key: unknown, existingEntries: unknown = []): EngineOverrideEntry | null {
  const spec = getEngineOverrideSpec(key);
  if (!spec) {
    return null;
  }
  const existingIds = new Set(
    normalizeEngineOverrideEntries(existingEntries)
      .map((entry) => String(entry.id || "").trim())
      .filter(Boolean),
  );
  let nextIndex = existingIds.size + 1;
  let nextId = `override_${spec.key}_${nextIndex}`;
  while (existingIds.has(nextId)) {
    nextIndex += 1;
    nextId = `override_${spec.key}_${nextIndex}`;
  }
  return {
    id: nextId,
    key: spec.key,
    value: spec.defaultValue,
  };
}

export function getFirstUnusedEngineOverrideKey(entries: unknown): string {
  const usedKeys = new Set(
    normalizeEngineOverrideEntries(entries)
      .map((entry) => String(entry.key || "").trim())
      .filter(Boolean),
  );
  const nextSpec = ENGINE_OVERRIDE_SPECS.find((spec) => !usedKeys.has(spec.key));
  return nextSpec ? nextSpec.key : "";
}

export function getEngineOverrideValue(entries: unknown, key: unknown, fallback: unknown = null): number | boolean | null {
  const normalizedKey = String(key || "").trim();
  const match = normalizeEngineOverrideEntries(entries).find((entry) => entry.key === normalizedKey) || null;
  if (!match) {
    return fallback as number | boolean | null;
  }
  return match.value;
}

export function describeEngineOverrideValue(entry: EngineOverrideEntry | null | undefined): string {
  if (!entry) {
    return "";
  }
  const spec = getEngineOverrideSpec(entry.key);
  if (!spec) {
    return String(entry.value ?? "");
  }
  if (spec.type === "boolean") {
    return entry.value ? "On" : "Off";
  }
  return String(entry.value);
}
