import { clsx } from 'clsx';

type VariantConfig = Record<string, Record<string, string>>;

type VariantSelection<V extends VariantConfig> = {
  [K in keyof V]?: keyof V[K] | null | undefined;
};

type CVAConfig<V extends VariantConfig> = {
  variants?: V;
  defaultVariants?: VariantSelection<V>;
};

export function cva(base: string): (props?: { className?: string }) => string;
export function cva<V extends VariantConfig>(
  base: string,
  config: CVAConfig<V>,
): (props?: VariantSelection<V> & { className?: string }) => string;
export function cva<V extends VariantConfig>(base: string, config?: CVAConfig<V>) {
  return (props?: VariantSelection<V> & { className?: string }): string => {
    const classes: (string | undefined)[] = [base];

    if (config?.variants) {
      for (const key of Object.keys(config.variants)) {
        const value = (props?.[key as keyof V] ?? config.defaultVariants?.[key as keyof V]) as
          | string
          | undefined;
        if (value != null && config.variants[key]?.[value]) {
          classes.push(config.variants[key][value]);
        }
      }
    }

    if (props?.className) {
      classes.push(props.className);
    }

    return clsx(classes);
  };
}

export type VariantProps<T extends (...args: any) => any> =
  T extends (props?: infer P) => any ? Omit<NonNullable<P>, 'className'> : never;
