import { FunctionComponent, ComponentClass } from 'react';
import { VariableDefine } from '@easy-coder/sdk/variable';
import { StyleDefine } from '@easy-coder/sdk/style';
import { EasyCoderState } from './state';

export type PickFunction<T extends Record<string, any>> = {
    [K in keyof T]: Required<T>[K] extends Function ? K : never;
};
export type PickCSSProperties<T extends Record<string, any>> = {
    [K in keyof T]: Required<T>[K] extends React.CSSProperties ? K : never;
};
export type PickString<T extends Record<string, any>> = {
    [K in keyof T]: Required<T>[K] extends string ? K : never;
};
export type UnionOmit<T, K extends keyof T> = T extends any ? Omit<T, K> : never;
export type UnionMerge<T, M> = T extends any ? T & M : never;
export type ObjectValue<T extends Record<string, any>> = T[keyof T];
export declare namespace EasyCoderElement {
    export type Type = string;
    export type OmitApiNameVariable = UnionOmit<Exclude<VariableDefine.Desc, VariableDefine.Object | VariableDefine.Array>, 'apiName'> | (Omit<VariableDefine.Object, 'apiName' | 'prototype'> & {
        prototype: Record<string, OmitApiNameVariable>;
    }) | (Omit<VariableDefine.Array, 'apiName' | 'item'> & {
        item: UnionOmit<OmitApiNameVariable, 'label'>;
    });
    export type GetAttrTypeInDynamicVariable<P extends Record<string, any>> = (attrKey: Exclude<keyof P, ObjectValue<PickFunction<P>>>) => Promise<OmitApiNameVariable | undefined>;
    export type DynamicVariable<P extends Record<string, any>> = (props: P, getAttrType: GetAttrTypeInDynamicVariable<P>) => Promise<OmitApiNameVariable | undefined>;
    export type VariableWithDynamic<P extends Record<string, any>> = OmitApiNameVariable | DynamicVariable<P>;
    export interface SetterBaseProps<T = any> {
        value?: T;
        onChange: (value?: T) => void;
    }
    export interface UiConfig<P extends SetterBaseProps> {
        disabledFx?: boolean;
        setter?: FunctionComponent<P> | ComponentClass<P>;
        setterProps?: Omit<P, 'value' | 'onChange'>;
    }
    export interface RefVariable {
        type: 'ref';
        label: string;
        required?: boolean;
        canAcceptTypes?: VariableDefine.Desc['type'][];
    }
    export type ElementAttr = UnionMerge<OmitApiNameVariable, UiConfig<any>> | RefVariable;
    export type ElementAttrWithApiName = UnionMerge<VariableDefine.Desc, UiConfig<any>>;
    export interface ClassName {
        label: string;
    }
    export interface Style {
        label: string;
        supportModels?: (keyof StyleDefine)[];
        defaultValue?: StyleDefine;
    }
    export interface Slot<P extends Record<string, any>> {
        payload?: Record<string, VariableWithDynamic<P>>;
        defaultStyle?: StyleDefine;
        label: string;
    }
    export interface Event<P extends Record<string, any>> {
        params?: VariableWithDynamic<P>[];
        return?: VariableWithDynamic<P>;
        label: string;
    }
    export interface Desc<P extends Record<string, any>> {
        type: Type;
        label: string;
        defaultAttr?: Partial<Pick<P, Exclude<keyof P, ObjectValue<PickFunction<P>>>>>;
        className?: Partial<Record<ObjectValue<PickString<P>>, ClassName>>;
        style?: Partial<Record<ObjectValue<PickCSSProperties<P>>, Style>>;
        attr?: Partial<Record<Exclude<keyof P, ObjectValue<PickFunction<P>>>, ElementAttr>>;
        event?: Partial<Record<ObjectValue<PickFunction<P>>, Event<P>>>;
        slot?: Partial<Record<ObjectValue<PickFunction<P>>, Slot<P>>>;
        export?: {
            attr?: Record<string, VariableWithDynamic<P>>;
            event?: Record<string, Event<P>>;
        };
        onCreate?: (options: OnCreateOptions) => Promise<void>;
        Render: FunctionComponent<P> | ComponentClass<P>;
    }
    interface OnCreateOptions {
        currentElement: EasyCoderState.Element;
        getElementById: (id: string) => EasyCoderState.Element | undefined;
        getEditorById: (id: string) => EasyCoderState.Editor | undefined;
        createElement: (type: string, editorId: string, defProps?: Record<string, any>) => Promise<string>;
    }
    export interface DataProps {
        [x: `data-${string}`]: string;
    }
    export {};
}
export interface FetchElementByType {
    (type: string): Promise<EasyCoderElement.Desc<any> | undefined>;
}
