import { ComponentType } from 'react';
import { config } from '@vitus-labs/core';
import { context } from '@vitus-labs/core';
import { FC } from 'react';
import type { FocusEventHandler } from 'react';
import type { ForwardedRef } from 'react';
import type { ForwardRefExoticComponent } from 'react';
import type { MouseEventHandler } from 'react';
import type { ReactElement } from 'react';
import { ReactNode } from 'react';
import { render } from '@vitus-labs/core';

declare type ArrayOfKeys<T> = Array<keyof T>;

declare type ArrayOfValues<T> = Array<T[keyof T]>;

export declare type AttrsCb<A, T> = (props: Partial<A>, theme: T, helpers: {
    mode?: ThemeModeKeys;
    isDark?: boolean;
    isLight?: boolean;
    createElement: typeof render;
}) => Partial<A>;

declare type CallBackParam = TObj | TFn;

export declare type ComposeParam = Record<string, GenericHoc | null | undefined | false>;

export declare type ConfigAttrs<C extends ElementType | unknown, D extends Dimensions, DKP extends TDKP, UB extends boolean> = Partial<{
    name: string;
    component: C;
    provider: boolean;
    consumer: ConsumerCb<D, DKP>;
    DEBUG: boolean;
    inversed: boolean;
    passProps: UB extends true ? keyof DimensionBooleanAttrs<DKP>[] : never;
    styled: boolean;
}>;

declare type Configuration<C = ElementType | unknown, D extends Dimensions = Dimensions> = InitConfiguration<C, D> & {
    provider?: boolean;
    consumer?: ConsumerCb<D>;
    DEBUG?: boolean;
    inversed?: boolean;
    passProps?: Array<string>;
    styled?: boolean;
    attrs: OptionFunc[];
    priorityAttrs: OptionFunc[];
    filterAttrs: string[];
    theme: OptionFunc[];
    styles: StylesCbArray;
    compose: Record<string, TFn | null | undefined | false>;
    statics: Record<string, any>;
} & Record<string, any>;

export declare type ConsumerCb<D extends Dimensions, DKP extends TDKP = TDKP> = (ctx: ConsumerCtxCb<D, DKP>) => ReturnType<ConsumerCtxCb<D, DKP>>;

export declare type ConsumerCtxCb<D extends Dimensions, DKP extends TDKP = TDKP> = <T extends RocketComponentType>(attrs: ConsumerCtxCBValue<T, D, DKP>) => ReturnType<ConsumerCtxCBValue<T, D, DKP>>;

export declare type ConsumerCtxCBValue<T extends RocketComponentType, D extends Dimensions, DKP extends TDKP> = (attrs: RocketProviderState<T>) => DKP extends TDKP ? Partial<ExtractDimensions<D, DKP> & {
    pseudo: PseudoState;
}> : TObj;

export { context }

declare type Css = typeof config.css;

declare const DEFAULT_DIMENSIONS: {
    readonly states: "state";
    readonly sizes: "size";
    readonly variants: "variant";
    readonly multiple: {
        readonly propName: "multiple";
        readonly multi: true;
    };
};

declare type DefaultDimensions = typeof DEFAULT_DIMENSIONS;

export declare type DefaultProps = Partial<PseudoProps>;

declare type DimensionBooleanAttrs<DKP extends TDKP> = Partial<Record<ValueOf<DimensionTypesHelper<DKP>>, boolean>>;

export declare type DimensionCallbackParam<T, CT> = DimensionObj<CT> | DimensionCb<T, CT>;

declare type DimensionCb<T, CT> = (theme: T, mode: ThemeModeCallback, css: Css) => DimensionResult<CT>;

declare type DimensionObj<CT> = DimensionResult<CT>;

declare type DimensionObjAttrs<D extends Dimensions, DKP extends TDKP> = {
    [I in keyof DKP]: ExtractDimensionMulti<D[I]> extends true ? Array<keyof DKP[I]> : keyof DKP[I];
};

export declare type DimensionProps<K extends DimensionValue, D extends Dimensions, P extends CallBackParam, DKP extends TDKP> = {
    [I in ExtractDimensionKey<D[keyof D]>]: I extends ExtractDimensionKey<K> ? ExtractNullableDimensionKeys<Spread<[DKP[I], NullableKeys<ReturnCbParam<P>>]>> : DKP[I];
};

declare type DimensionResult<CT> = Record<string, boolean | null | Partial<CT>>;

export declare type Dimensions = Record<string, DimensionValue>;

declare type DimensionTypesHelper<DKP extends TDKP> = {
    [I in keyof DKP]: keyof DKP[I];
};

export declare type DimensionValue = DimensionValuePrimitive | DimensionValueObj;

declare type DimensionValueObj = {
    propName: string;
    multi?: boolean;
};

declare type DimensionValuePrimitive = string;

export declare type ElementType<T extends TObj | unknown = any> = (ComponentType<T> & Partial<{
    [key: string]: any;
}>) | (ForwardRefExoticComponent<T> & {
    [key: string]: any;
});

declare interface ExoticComponent<P = {}> {
    (props: P): ReactElement<P & InnerComponentProps> | null;
    readonly $$typeof: symbol;
}

declare type ExtractDimensionKey<T extends DimensionValue> = T extends DimensionValueObj ? T['propName'] : T;

declare type ExtractDimensionMulti<T extends DimensionValue> = T extends DimensionValueObj ? true : false;

export declare type ExtractDimensionProps<D extends Dimensions, DKP extends TDKP, UB extends boolean> = UB extends true ? Partial<ExtractNullableDimensionKeys<DimensionObjAttrs<D, DKP> & DimensionBooleanAttrs<DKP>>> : Partial<ExtractNullableDimensionKeys<DimensionObjAttrs<D, DKP>>>;

export declare type ExtractDimensions<D extends Dimensions, DKP extends TDKP> = ExtractNullableDimensionKeys<DimensionObjAttrs<D, DKP>>;

declare type ExtractNullableDimensionKeys<T> = {
    [P in keyof T as T[P] extends false ? never : P]: T[P];
};

declare type ExtractNullableKeys<T> = {
    [P in keyof T as T[P] extends null | never | undefined ? never : P]: T[P];
};

export declare type ExtractProps<TComponentOrTProps> = TComponentOrTProps extends ElementType<infer TProps> ? TProps : TComponentOrTProps;

export declare type GenericHoc = (component: ElementType) => ElementType;

declare type Id<T> = T extends infer U ? {
    [K in keyof U]: U[K];
} : never;

declare type InitConfiguration<C, D> = {
    name?: string;
    component: C;
    useBooleans: boolean;
    dimensions: D;
    dimensionKeys: ArrayOfKeys<D>;
    dimensionValues: ArrayOfValues<D>;
    multiKeys: MultiKeys;
};

declare type InnerComponentProps = {
    $rocketstyleRef?: ForwardedRef<unknown>;
    'data-rocketstyle': string;
};

/**
 * @param OA   Origin component props params.
 * @param EA   Extended prop types
 * @param T    Theme passed via context.
 * @param CSS  Custom theme accepted by styles.
 * @param S    Defined statics
 * @param D    Dimensions to be used for defining component states.
 * @param UB   Use booleans value
 * @param DKP  Dimensions key props.
 * @param DFP  Calculated final component props
 */
export declare interface IRocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T extends TObj = {}, CSS extends TObj = {}, S extends TObj = {}, HOC extends TObj = {}, D extends Dimensions = Dimensions, UB extends boolean = boolean, DKP extends TDKP = TDKP, DFP = MergeTypes<[OA, EA, DefaultProps, ExtractDimensionProps<D, DKP, UB>]>> extends ExoticComponent<DFP> {
    /**
     * A chaining method to define default component theme
     * @param param  _object_
     *
     * ### Examples
     *
     * #### Component name / displayName
     * ```tsx
     * const base = rocketstyleComponent
     *  .config({
     *    name: 'Component name'
     *  })
     * ```
     *
     * #### Replace component by a new one
     * ```tsx
     * const base = rocketstyleComponent
     *  .config({
     *    component: (props) => <div {...props} />
     *  })
     * ```
     *
     * #### Component as provider
     * ```tsx
     * const parent = rocketstyleComponent
     *  .config({
     *    provider: true
     *  })
     * ```
     *
     * #### Component as consumer
     * ```tsx
     * const base = rocketstyleComponent
     *  .config({
     *    consumer: ctx => ctx<typeof parent>(({ pseudo, state, ...rest }) => ({
     *      pseudo,
     *      state
     *    }))
     *  })
     * ```
     *
     * #### Inversed theme
     * A possibility to set individualy for each component to have `inversed` styles
     * when using dark / light theme modes
     * ```tsx
     * const base = rocketstyleComponent
     *  .config({
     *    inversed: true
     *  })
     * ```
     *
     * #### Pass props to original component
     * A possibility to set individualy for each component props names to be passed
     * to `origin` component
     *
     * ```tsx
     * const base = rocketstyleComponent
     *  .config({
     *    passProps: ['disabled', 'readOnly']
     *  })
     * ```
     */
    config: <NC extends ElementType | unknown = unknown>({ name, component: NC, provider, consumer, DEBUG, inversed, passProps, }: ConfigAttrs<NC, D, DKP, UB>) => NC extends ElementType ? RocketStyleComponent<ExtractProps<NC>, EA, T, CSS, S, HOC, D, UB, DKP> : RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP>;
    /**
     * A chaining method to define default component props
     * @param param  Can be either _object_ or a _callback_
     *
     * #### Examples
     *
     * ##### Object as a parameter
     * ```tsx
     * const base = rocketstyleComponent
     * const newElement = base.attrs({
     *  propA: 'value',
     *  propB: 'value,
     * })
     * ```
     *
     * ##### Callback as a parameter
     * ```tsx
     * const base = rocketstyleComponent
     * const newElement = base.attrs((props, theme, helpers) => ({
     * propA: props.disabled ? 'valueA' : 'valueB',
     * propB: 'value,
     *  }))
     *  ```
     */
    attrs: <P extends TObj | unknown = unknown>(param: P extends TObj ? Partial<MergeTypes<[DFP, P]>> | AttrsCb<MergeTypes<[DFP, P]>, Theme<T>> : Partial<DFP> | AttrsCb<DFP, Theme<T>>, config?: Partial<{
        /**
         * priority props will be resolved first and overwritten by normal `attrs`
         * callbacks and `props` afterwards
         */
        priority: boolean;
        /**
         * filter props will be omitted when passing to final component
         */
        filter: P extends TObj ? Partial<keyof MergeTypes<[EA, P]>>[] : Partial<keyof EA>[];
    }>) => P extends TObj ? RocketStyleComponent<OA, MergeTypes<[EA, P]>, T, CSS, S, HOC, D, UB, DKP> : RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP>;
    /**
     * A chaining method to define default component theme
     * @param param  Can be either _object_ or a _callback_
     *
     * ### Examples
     *
     * #### Object as a parameter
     * ```tsx
     * const base = rocketstyleComponent
     * const newElement = base.attrs({
     *  backgroundColor: 'black',
     * })
     * ```
     *
     * #### Callback as a parameter
     * ```tsx
     * const base = rocketstyleComponent
     * const newElement = base.theme((theme, css) => ({
     * backgroundColor: t.color.black, // value from context
     *  }))
     *```
     *
     * #### Dark / light theme callback
     * ```tsx
     * const base = rocketstyleComponent
     *
     * const newElement = base.theme((theme, mode, css) => ({
     * backgroundColor: mode(t.color.black, t.color.white), // theme from context
     * }))
     * ```
     */
    theme: <P extends TObj | unknown = unknown>(param: P extends TObj ? Partial<MergeTypes<[Styles<CSS>, P]>> | ThemeCb<MergeTypes<[Styles<CSS>, P]>, Theme<T>> : Partial<Styles<CSS>> | ThemeCb<Styles<CSS>, Theme<T>>) => P extends TObj ? RocketStyleComponent<OA, EA, T, MergeTypes<[CSS, P]>, S, HOC, D, UB, DKP> : RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP>;
    /**
     * A chaining method to define default rendered styles
     * @param param  Callback of styled-components `css` function
     *
     * #### Examples
     *
     * ```tsx
     * const base = rocketstyleComponent
     *
     * const newElement = base.styles(css => css``)
     * ```
     */
    styles: (param: StylesCb) => RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP>;
    /**
     * A chaining method to define high-order components to wrap
     * the defined component
     * @param param object of key hoc function
     *
     * ### Examples
     *
     * #### Define new hoc component
     * Using the `{ key: hoc }` annotation allows to override or remove
     * the defined hoc(s) later on. See the examples below.
     * ```tsx
     * const hoc = (WrappedComponent) => (props) => <WrappedComponent {...props} />
     * const base = rocketstyleComponent
     *
     * const newElement = base.compose({ hocName: hoc })
     * ```
     *
     * #### Remove previously defined hoc component
     * (1) Set value to be `false`
     * ```tsx
     * const newElement = base.compose({ hocName: false })
     * ```
     * (2) Set value to be `null`
     * ```tsx
     * const newElement = base.compose({ hocName: null })
     * ```
     * (3) Set value to be `undefined`
     * ```tsx
     * const newElement = base.compose({ hocName: undefined })
     * ```
     */
    compose: <P extends ComposeParam>(param: P) => P extends TObj ? RocketStyleComponent<OA, EA, T, CSS, S, MergeTypes<[HOC, P]>, D, UB, DKP> : RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP>;
    /**
     * A chaining method to define statics on the rocketstyle component.
     * All statics are accessible via `is` static key on the component.
     * @param param object of statics
     *
     * ### Examples
     *
     * #### Define new static
     * Using the `{ key: value }` annotation allows to override or remove
     * the defined static(s) later on. See the examples below.
     * ```tsx
     * const base = rocketstyleComponent
     *
     * const newElement = base.statics({
     * isNewStatic: true,
     * arrayStatic: ['a', 'b'],
     * functionStatic: (param) => { ...do something }
     * })
     * ```
     *
     * #### Override previously defined hoc component
     * (1) Set value to be `false`
     * ```tsx
     * const newElement = base.statics({ isNewStatic: false })
     * ```
     * (2) Set value to be `null`
     * ```tsx
     * const newElement = base.statics({ isNewStatic: null })
     * ```
     * (3) Set value to be `undefined`
     * ```tsx
     * const newElement = base.statics({ isNewStatic: undefined })
     * ```
     */
    statics: <P extends TObj | unknown = unknown>(param: P) => P extends TObj ? RocketStyleComponent<OA, EA, T, CSS, MergeTypes<[S, P]>, HOC, D, UB, DKP> : RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP>;
    /**
     * An access to all defined statics on the component.
     *
     * ### Examples
     *
     * #### Access a static property
     * ```tsx
     * const element = rocketcomponent.statics({
     * isNewStatic: true,
     * arrayStatic: ['a', 'b'],
     * functionStatic: (param) => { ...do something }
     * })
     *
     * // staticValue = true
     * const staticValue = element.meta.isNewStatic
     * ```
     */
    meta: S;
    getStaticDimensions: (theme: TObj) => {
        dimensions: DKP;
        useBooleans: UB;
        multiKeys: MultiKeys<D>;
    };
    getDefaultAttrs: (props: TObj, theme: TObj, mode: ThemeModeKeys) => TObj;
    /**
     * Accessible via types only!
     *
     * Provides defined rocketstyle dimensions and their options
     *
     * ### Examples
     * ```tsx
     * const element = rocketcomponent
     *
     * type Props = typeof element['$$rocketstyle']
     *
     * ```
     */
    readonly $$rocketstyle: ExtractDimensions<D, DKP>;
    /**
     * Accessible via types only!
     *
     * Provides defined original props types (props of origin
     * component passed to _rocketstyle_)
     *
     * ### Examples
     * ```tsx
     * const element = rocketcomponent
     *
     * type Props = typeof element['$$originProps']
     *
     * ```
     */
    readonly $$originTypes: OA;
    /**
     * Accessible via types only!
     *
     * Provides defined extended props types (props types defined
     * on `.attrs()` chaining method
     * component passed to _rocketstyle_)
     *
     * ### Examples
     * ```tsx
     * const element = rocketcomponent
     *  .attrs<{ propName: string }>({})
     *
     * type Props = typeof element['$$extendedProps']
     *
     * ```
     */
    readonly $$extendedTypes: EA;
    /**
     * Accessible via types only!
     *
     * Provides all defined props types (including **origin**
     * props types, **extended** props types & **rocketstyle**
     * props types all together)
     *
     * ### Examples
     * ```tsx
     * const element = rocketcomponent
     *
     * type Props = typeof element['$$allProps']
     *
     * ```
     */
    readonly $$types: DFP;
    /**
     * Static Rocketstyle component identificator
     */
    IS_ROCKETSTYLE: true;
    /**
     * Component displayName defined in `.config()` chaining
     * method
     *
     * ```tsx
     * const element = rockestyleComponent
     *  .config({
     *    name: 'ComponentName'
     *  })
     * ```
     */
    displayName: string;
}

declare type IsFalseOrNullable<T> = T extends null | undefined | false ? never : true;

export declare type IsRocketComponent = <T>(component: T) => boolean;

export declare const isRocketComponent: IsRocketComponent;

export declare type MergeTypes<A extends readonly [...any]> = ExtractNullableKeys<Spread<A>>;

declare type MultiKeys<T extends Dimensions = Dimensions> = Partial<Record<ExtractDimensionKey<T[keyof T]>, true>>;

declare type NullableKeys<T> = {
    [K in keyof T]: IsFalseOrNullable<T[K]>;
};

declare type OptionFunc = (...arg: unknown[]) => Record<string, unknown>;

export declare const Provider: FC<TProvider>;

declare type PseudoActions = {
    onMouseEnter: MouseEventHandler;
    onMouseLeave: MouseEventHandler;
    onMouseDown: MouseEventHandler;
    onMouseUp: MouseEventHandler;
    onFocus: FocusEventHandler;
    onBlur: FocusEventHandler;
};

declare type PseudoProps = Partial<PseudoState & PseudoActions>;

declare type PseudoState = {
    active: boolean;
    hover: boolean;
    focus: boolean;
    pressed: boolean;
    disabled: boolean;
    readOnly: boolean;
};

declare type ReturnCbParam<P extends TFn | TObj> = P extends TFn ? ReturnType<P> : P;

declare type RocketComponent<C extends ElementType = ElementType, T extends TObj = {}, CSS extends TObj = {}, D extends Dimensions = DefaultDimensions, UB extends boolean = boolean> = (props: Configuration<C, D>) => RocketStyleComponent<ExtractProps<C>, {}, T, CSS, {}, {}, D, UB, {}>;

export declare type RocketComponentType = ElementType & {
    IS_ROCKETSTYLE: true;
    $$rocketstyle: Record<string, unknown>;
};

export declare type RocketProviderState<T extends RocketComponentType | TObj | unknown = unknown> = T extends RocketComponentType ? Partial<T['$$rocketstyle']> & {
    pseudo: PseudoState;
} : T;

export declare type Rocketstyle = <D extends Dimensions = DefaultDimensions, UB extends boolean = true>({ dimensions, useBooleans, }?: {
    dimensions?: D;
    useBooleans?: UB;
}) => <C extends ElementType>({ name, component, }: {
    name: string;
    component: C;
}) => ReturnType<RocketComponent<C, {}, {}, D, UB>>;

declare const rocketstyle: Rocketstyle;
export default rocketstyle;
export { rocketstyle }

export declare type RocketStyleComponent<OA extends TObj = {}, EA extends TObj = {}, T extends TObj = {}, CSS extends TObj = {}, S extends TObj = {}, HOC extends TObj = {}, D extends Dimensions = Dimensions, UB extends boolean = boolean, DKP extends TDKP = TDKP> = IRocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP> & {
    [I in keyof D]: <K extends DimensionValue = D[I], P extends DimensionCallbackParam<Theme<T>, Styles<CSS>> = DimensionCallbackParam<Theme<T>, Styles<CSS>>>(param: P) => P extends DimensionCallbackParam<Theme<T>, Styles<CSS>> ? RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DimensionProps<K, D, P, DKP>> : RocketStyleComponent<OA, EA, T, CSS, S, HOC, D, UB, DKP>;
};

declare type Spread<A extends readonly [...any]> = A extends [
infer L,
...infer R
] ? SpreadTwo<L, Spread<R>> : unknown;

declare type SpreadTwo<L, R> = Id<Pick<L, Exclude<keyof L, keyof R>> & R>;

declare type Styles<S> = S extends unknown ? StylesDefault : MergeTypes<[StylesDefault, S]>;

export declare type StylesCb = (css: Css) => ReturnType<Css>;

declare type StylesCbArray = StylesCb[];

export declare interface StylesDefault {
}

export declare type TDKP = Record<ExtractDimensionKey<Dimensions[keyof Dimensions]>, Record<string, boolean | never | Record<string, boolean>> | unknown>;

declare type TFn = (...args: any) => any;

declare type Theme<T> = T extends unknown ? ThemeDefault : MergeTypes<[ThemeDefault, T]>;

declare type Theme_2 = {
    rootSize: number;
    breakpoints?: Record<string, number>;
    __VITUS_LABS__?: never;
} & Record<string, unknown>;

declare const THEME_MODES: {
    readonly light: true;
    readonly dark: true;
};

export declare type ThemeCb<CSS, T> = (theme: T, mode: ThemeModeCallback, css: Css) => Partial<CSS>;

export declare interface ThemeDefault {
}

export declare type ThemeMode = <A = any, B = any>(light: A, dark: B) => A | B;

export declare interface ThemeModeCallback {
    <A = any, B = any>(light: A, dark: B): (mode: 'light' | 'dark') => A | B;
}

export declare type ThemeModeKeys = keyof typeof THEME_MODES;

export declare type TObj = Record<string, unknown>;

export declare type TProvider = {
    children: ReactNode;
    theme?: Theme_2;
    mode?: 'light' | 'dark';
    inversed?: boolean;
    provider?: ComponentType<any>;
};

declare type ValueOf<T> = T[keyof T];

export { }
