type Option<TExtra = unknown> = {
  value: string | number;
  label: string;
} & TExtra;

type CompositeKeys<T> = `${Extract<keyof T, string>}${string}`; // e.g., "firstName | lastName"

interface GenerateOptionsConfig<
  T extends object,
  TExtraKeys extends keyof T = never,
> {
  useDefaults?: boolean;
  valueKey?: keyof T;
  labelKey?: keyof T | CompositeKeys<T>;
  extraKeys?: TExtraKeys[];
}

export function generateOptions<
  T extends object,
  TExtraKeys extends keyof T = never,
>(
  items: T[],
  config: GenerateOptionsConfig<T, TExtraKeys> = { useDefaults: true },
): Option<Pick<T, TExtraKeys>>[] {
  const { useDefaults = true, valueKey, labelKey, extraKeys = [] } = config;

  const resolvedValueKey = useDefaults ? ("id" as keyof T) : valueKey!;
  const resolvedLabelKey = useDefaults ? ("name" as keyof T) : labelKey!;

  return items.map((item) => {
    let label: string;

    if (
      typeof resolvedLabelKey === "string" &&
      /[|,\- ]/.test(resolvedLabelKey)
    ) {
      // Match keys and separators (e.g., "firstName | lastName or "firstName - lastName" or "firstName, lastName")
      const parts = resolvedLabelKey.split(/([|,\- ])/); // Keep separators in the array
      label = parts
        .map((part) => {
          const trimmed = part.trim();
          if (/^[a-zA-Z0-9_]+$/.test(trimmed) && trimmed in item) {
            return item[trimmed as keyof T];
          }
          return part; // It's a separator
        })
        .join("");
    } else {
      label = String(item[resolvedLabelKey as keyof T]);
    }

    const baseOption = {
      value: item[resolvedValueKey] as string | number,
      label,
    };

    const extra = extraKeys.reduce(
      (acc, key) => {
        acc[key] = item[key];
        return acc;
      },
      {} as Pick<T, TExtraKeys>,
    );

    return {
      ...baseOption,
      ...extra,
    };
  });
}
