import { isPlainObject } from 'es-toolkit';
import { InjectionKey, isRef, Ref } from 'vue';

/**
 * Create a union from an object's values
 *
 * using a plain object:
 * const SHAPE = { CIRCLE: 'circle', SQUARE: 'square' } as const;
 * ValueOf<typeof SHAPE> -> square | circle
 *
 * using a type:
 * type Shape = { SQUARE: string; CIRCLE: string; }
 * ValueOf<Shape>
 */
export type ValueOf<T> = T[keyof T];

export function isListOfStrings(list: unknown): list is string[] {
  return Array.isArray(list) && typeof list[0] === 'string';
}

export function isListOfObjects<T = unknown>(list: unknown): list is T[] {
  return Array.isArray(list) && isPlainObject(list[0]);
}

export interface Injection<T> {
  key: InjectionKey<T>;
}

export interface InjectionWithDefaults<T> extends Injection<T> {
  defaults: T;
}

/**
 * Like the MaybeRefOrGetter type from Vue, but without the Maybe. Can be used to ensure the most up-to-date value is available.
 */
export type RefOrGetter<T> = Ref<T> | (() => T);

/**
 * A type guard for the RefOrGetter type. Acceptable values include:
 *  - a value created from `ref()`
 *  - a value created from `shallowRef()`
 *  - a value created from `computed()`
 *  - a function that returns a value
 */
export function isRefOrGetter<T>(value: RefOrGetter<T> | unknown): value is RefOrGetter<T> {
  return isRef(value) || typeof value === 'function';
}
