import { createMemo, createSignal, For } from "solid-js";
export const BoolProp = props => (<label>
    <input type="checkbox" name={props.name} checked={props.value()} onChange={ev => props.setValue(ev.currentTarget.checked)}/>{" "}
    <span>{props.name}</span>{" "}
  </label>);
export const NumberProp = props => (<label>
    <span>{props.name}</span>{" "}
    <input type="number" name={props.name} min={props.min} max={props.max} value={props.value()} onChange={ev => props.setValue(ev.currentTarget.valueAsNumber)}/>{" "}
  </label>);
export const StringProp = props => (<label>
    <span>{props.name}</span>{" "}
    <input type="text" name={props.name} value={props.value()} onInput={ev => props.setValue(ev.currentTarget.value)} onChange={ev => props.setValue(ev.currentTarget.value)}/>{" "}
  </label>);
const filterEnum = (options) => {
    const entries = Object.entries(options);
    if (Object.keys(options).every(key => key === options[options[key]].toString())) {
        return entries
            .reduce((items, [key, value]) => {
            if (!items.includes(key)) {
                items.push(value);
            }
            return items;
        }, [])
            .map(item => [item, options[item]]);
    }
    return entries;
};
export const SelectProp = (props) => {
    const options = createMemo(() => Array.isArray(props.options)
        ? props.options.map((option) => [option, option])
        : filterEnum(props.options));
    const initialValue = options().findIndex(([, value]) => value === props.value());
    return (<label>
      <span>{props.name}</span>{" "}
      <select name={props.name} value={initialValue.toString()} onChange={ev => props.setValue(options()[ev.currentTarget.selectedIndex][1])}>
        <For each={options()} fallback={<option>"options missing"</option>}>
          {([key], index) => <option value={index()}>{key}</option>}
        </For>
      </select>{" "}
    </label>);
};
const defaultInitialValues = {
    boolean: false,
    number: 0,
    string: "",
    object: undefined,
};
export function createControlledProp(name, options) {
    const initialValue = options == null
        ? undefined
        : typeof options !== "object"
            ? options
            : (options.initialValue ??
                defaultInitialValues[options.type ?? "object"]);
    if (initialValue == null) {
        throw new Error(`cannot get type for Prop ${name}`);
    }
    const propType = options.options
        ? "object"
        : (options.type ?? typeof initialValue);
    const [value, setValue] = createSignal(initialValue, { name });
    return [
        value,
        setValue,
        propType === "boolean"
            ? () => BoolProp({ name, value: value, setValue: setValue })
            : propType === "number"
                ? () => NumberProp({ name, value: value, setValue: setValue })
                : propType === "string"
                    ? () => StringProp({ name, value: value, setValue: setValue })
                    : () => SelectProp({
                        name,
                        value: value,
                        setValue: setValue,
                        options: options.options ??
                            [initialValue],
                    }),
    ];
}
/**
 * creates reactive props for testing a component
 *
 * @param props {Record<string, TestPropOptions>}
 * @returns ```ts
 * [
 *   props: { [name: string]: Accessor<T>, [setName: string]: Setter<T> }
 *   fields: JSX.Element[]
 * ]
 * ```
 * You get the fields to render the prop controls and getter and setter names derived from the name in `props` based on common practice, i.e. `count` would automatically translate to
 * ```ts
 * { count: Accessor<T>, getCount: Setter<T> }
 * ```
 * @example ```ts
 * const [props, fields] = createControlledProps({
 *   value: { initialValue: '' },
 *   disabled: { initialValue: false },
 *   invalid: { initialValue: false },
 *   type: { initialValue: 'text', options: ['text', 'password', 'email'] }
 * })
 * return <>
 *   <Input {...props} />
 *   {fields}
 * </>
 * ```
 */
export const createControlledProps = props => Object.entries(props).reduce((result, [name, options]) => {
    const [value, setValue, field] = createControlledProp(name, options);
    result[0][name] = value;
    result[0][`set${name.slice(0, 1).toUpperCase()}${name.slice(1)}`] = setValue;
    result[1].push(field({}));
    return result;
}, [{}, []]);
