import { z } from 'zod';

export interface EnumMetaEntry {
  order: number;
  labelKey: string;
  /** Optional behavior caps — shape varies per enum; kept open. */
  [k: string]: unknown;
}

type Values = readonly [string, ...string[]];

interface BehaviorConfig<V extends Values> {
  name: string;
  values: V;
  meta: Record<V[number], EnumMetaEntry>;
  labels?: never;
}

interface LabelConfig<V extends Values> {
  name: string;
  values: V;
  labels: Record<V[number], string>;
  meta?: never;
}

export type EnumConfig<V extends Values> = BehaviorConfig<V> | LabelConfig<V>;

export interface BehaviorEnum<V extends Values> {
  readonly name: string;
  readonly values: V;
  readonly schema: z.ZodEnum<{ [K in V[number]]: K }>;
  is(x: unknown): x is V[number];
  metaFor(v: V[number]): EnumMetaEntry;
  readonly meta: Record<V[number], EnumMetaEntry>;
  readonly ordered: readonly V[number][];
}

export interface LabelEnum<V extends Values> {
  readonly name: string;
  readonly values: V;
  readonly schema: z.ZodEnum<{ [K in V[number]]: K }>;
  is(x: unknown): x is V[number];
  labelKeyFor(v: V[number]): string;
  readonly labels: Record<V[number], string>;
}

export function defineEnum<V extends Values>(config: BehaviorConfig<V>): BehaviorEnum<V>;
export function defineEnum<V extends Values>(config: LabelConfig<V>): LabelEnum<V>;
export function defineEnum<V extends Values>(
  config: EnumConfig<V>,
): BehaviorEnum<V> | LabelEnum<V> {
  const { name, values } = config;
  const schema = z.enum(values as unknown as [string, ...string[]]) as unknown as z.ZodEnum<{
    [K in V[number]]: K;
  }>;
  const set = new Set<string>(values);
  const is = (x: unknown): x is V[number] => typeof x === 'string' && set.has(x);

  if ('meta' in config && config.meta) {
    const meta = config.meta;
    const ordered = [...values].sort(
      (a, b) => meta[a as V[number]].order - meta[b as V[number]].order,
    ) as V[number][];
    return { name, values, schema, is, meta, metaFor: (v: V[number]) => meta[v], ordered };
  }

  const labels = (config as LabelConfig<V>).labels;
  return { name, values, schema, is, labels, labelKeyFor: (v: V[number]) => labels[v] };
}

export { enumLabel } from './enum-labels';
