import { IMountObserver } from '../../mount-observer/types';
import { Scope} from '../lib/types';
import { WrapperConfig } from '../XV/types';
import {CSSQuery, XForm} from '../types';

export interface IEventConfig<MCProps = any, MCActions = MCProps, TAction = Action>{
    on?: string,
    of?: 'tbd' | EventTarget,
    doInit?: boolean,
    options?: AddEventListenerOptions,
    abort?: {
        origMethName: string & keyof MCActions,
        //destMethName: string & keyof MCActions,
        of: 'tbd' | EventTarget,
        on: string, 
        
    },
    composedPathMatches?: string,
}

//Is anything using this anymore?
export type ActionOnEventConfigs<MCProps = any, MCActions = MCProps, TAction = Action> = Partial<{[key in keyof MCActions]: IEventConfig<MCProps, MCActions, TAction>}>

export interface IPropagator extends EventTarget{
    get(key: string): any;
    set(key: string, val: any): void;
    /**
     * Delta Keys
     */
    dk: Set<string>;

    /**
     * Mature keys
     */
    mk: Set<string>;

    /**
     * timeout handles - key is name of prop
     * used for simple debouncing of echo notifications in XtalElement
     */
    eth: Map<string, string | number | NodeJS.Timeout> | undefined; 

    /**
     * timeout handles - key is name of prop
     * used for simple debouncing of toggle echo notifications in XtalElement
     */
    tth:  Map<string, string | number | NodeJS.Timeout> | undefined;
}

export interface IResolvableService extends EventTarget{
    resolved: boolean;
    resolve(): Promise<void>;
    
}

export interface IInstanceResolvableService<T extends object = object> extends IResolvableService{
    instanceResolve(instance: T): Promise<void>;
}

export interface IMix extends IResolvableService{
    ext: {new(): HTMLElement}
}

export interface IPropRegistrar extends IResolvableService{
    propInfos: {[key: string]: PropInfo},
    allPropNames: string[],
    getAttrNames(ext: any): Promise<string[]>,
    getPropsFromAction(action: string | Action): Set<string>,
    nonDryProps: Set<string>,
}

// export interface IDefine extends IResolvableService{
//     custElClass: {new(): HTMLElement};
//     resolveInstanceSvcs(args: CEArgs, instance: any): Promise<void>;
// }

export interface IPropSvc extends IResolvableService{
    createPropBag(instance: Element): void;
}

export interface IHookup extends IInstanceResolvableService{

}

export interface IAttrChgCB{
    instance: HTMLElement,
    // name: string,
    // oldVal: string,
    // newVal: string,
    newAttrs: {[key: string]: {oldVal: string | null, newVal: string | null}},
    filteredAttrs: {[key: string]: string}
}

export interface IConnectedCB{
    instance: HTMLElement,
}

export interface IPropChg{
    key: string,
    oldVal: any,
    newVal: any,
    
}

export interface IDisconnectedCB {
    instance: HTMLElement
}

export interface INewPropagator {
    instance: HTMLElement,
    propagator: IPropagator,
}





// export interface CEArgs<TProps = any, TActions = TProps, TPropInfo = PropInfo, TAction extends Action<TProps> = Action<TProps>> extends DefineArgs<TProps, TActions, TPropInfo, TAction>{
//     definer?: IDefine,
//     servers?: CEServiceClasses
//     services?: CEServices,
//     asides?: any
// }

export interface DynamicTransform {
    scope?: Scope,
    noCache?: boolean,
}

export interface IPE {
    do(instance: EventTarget, originMethodName: string, vals: [any, ActionOnEventConfigs] ): Promise<void>,
}

export interface IPET extends IPE{
    re(instance: EventTarget, originMethodName: string, vals: [any, ActionOnEventConfigs, DynamicTransform] ): Promise<void>,
}

export interface DefineArgs<MixinCompositeProps = any, MixinCompositeActions = MixinCompositeProps, TPropInfo = PropInfo, TAction extends Action = Action<MixinCompositeProps>>{
    superclass?: {new(): HTMLElement} | string,
    mixins?: any[],
    mainTemplate?: HTMLTemplateElement;
    /** use this only for defaults that can't be JSON serialized in config */
    complexPropDefaults?: Partial<MixinCompositeProps>;
    /** Config should be 100% JSON serializable, or a JSON import, or an id of an be-exportable script tag */
    config: WCConfig<MixinCompositeProps, MixinCompositeActions, TPropInfo, TAction> | (() => Promise<{default: WCConfig<MixinCompositeProps, MixinCompositeActions, TPropInfo, TAction>}>) | string;
    // /**
    //  * Side effects tied to actions, mostly used to load enhancement dependencies tied to 
    //  * enhancements
    //  */
    // asides?: Partial<{[key in keyof MixinCompositeActions & string]: (instance: EventTarget, methodName: string, key: string) => Promise<void> }>
}

export interface WCConfig<TProps = any, TActions = TProps, TPropInfo = PropInfo, TAction = Action>{
    tagName?: string;
    isEnh?: boolean;
    propDefaults?: Partial<{[key in keyof TProps]: TProps[key]}>;
    propInfo?: Partial<{[key in keyof TProps]: TPropInfo}>;
    wrappers?: Partial<{[key in keyof TProps]: WrapperConfig<TProps>}>;
    derivedProps?: (keyof TProps & string)[];
    // actions?: 
    //     Partial<{[key in keyof MCActions & string]: TAction | keyof MCProps}> 
    actions?: Actions<TProps, TActions>;
    propChangeMethod?: keyof TActions;
    style?: Partial<CSSStyleDeclaration>;
    /**
     * Used for providing hints to server side processing what css queries should be observed if using HTMLRewriter.
     */
    keyQueries?: string[];
    formAss?: boolean;
    compacts?: Compacts<TProps>;
    
}

export type PropLookup<TProps = any, TActions = any> = Partial<{[key in keyof TProps]: PropInfo<TProps, TActions>}>;
export type IshPropLookup<TProps = any, TActions = any> = Partial<{[key in keyof TProps]: IshPropInfo<TProps, TActions>}>;
export interface IshConfig<TProps = any, TActions = TProps, ETProps = TProps>{
    propDefaults?: Partial<{[key in keyof TProps]: TProps[key]}>;
    propInfo?: Partial<{[key in keyof TProps]: PropInfo}>;
    wrappers?: Partial<{[key in keyof TProps]: WrapperConfig<TProps>}>;
    actions?: Actions<TProps, TActions>;
    /**
     * inferred actions
     */
    infractions?: Infractions<TProps>,
    compacts?: Compacts<TProps, TActions>;
    hitch?: Hitches<TProps, TActions>;
    handlers?: Handlers<ETProps, TActions>;
    positractions?: Positractions<TProps, TActions>;
    
    isSleepless?: boolean;
    xform?: XForm<TProps, TActions>;
    inScopeXForms?: {[key: CSSQuery]: XForm<TProps, TActions>};
    ishListCountProp?: keyof TProps & string;
    defaultIshList?: any[];
}
export interface OConfig<TProps = any, TActions = TProps, ETProps = TProps> extends IshConfig<TProps, TActions, ETProps>{
    mainTemplate?: string | HTMLTemplateElement;
}

export type Positractions<TProps = any, TActions = TProps> = 
    | Array<Positraction<TProps, TActions>>;

export interface Positraction<TProps = any, TActions = TProps> extends LogicOp<TProps> {
    do: Function | (keyof TActions & string),
    ifKeyIn?: Array<keyof TProps & string>,
    ifAllOf?: Array<keyof TProps & string>,
    //ifNoneOf: Array<keyof TProps & string>,
    
    pass?: Array<(keyof TProps & string) | number | boolean | '$0' | '$0+' | `\`${string}\``>,
    assignTo?: Array<null | (keyof TProps & string)>
}



export type Compacts<TProps = any, TActions = TProps> = 
    //| Partial<{[key in `${keyof TProps & string}_to_${keyof TProps & string}` & string]: Operation<TProps> }>
    | Partial<{[key in `negate_${keyof TProps & string}_to_${keyof TProps & string}`]: number}>
    | Partial<{[key in `pass_length_of_${keyof TProps & string}_to_${keyof TProps & string}`]: number}>
    | Partial<{[key in `echo_${keyof TProps & string}_to_${keyof TProps & string}`]: number}>
    | Partial<{[key in `echo_${keyof TProps & string}_to_${keyof TProps & string}_after`]: keyof TProps}>
    | Partial<{[key in `when_${keyof TProps & string}_changes_call_${keyof TActions & string}`]: number}>
    | Partial<{[key in `when_${keyof TProps & string}_changes_toggle_${keyof TProps & string}`]: number}>
    | Partial<{[key in `when_${keyof TProps & string}_changes_inc_${keyof TProps & string}_by`]: number}>
    | Partial<{[key in `when_${keyof TProps & string}_changes_dispatch`]: string}> //TODO
;

export type Hitches<TProps = any, TActions = TProps> = 
    | Partial<{[key in `when_${keyof TProps & string}_emits_${keyof TProps & string}_inc_${keyof TProps & string}_by`]: number}>
    
;

export type Handlers<ETProps = any, TActions = ETProps> = 
    | Partial<{[key in `${keyof ETProps & string}_to_${keyof TActions & string}_on` & string]: string }>;



export type ListOfLogicalExpressions<MCProps = any> = (keyof MCProps | LogicOp<MCProps>)[];

export type LogicOpProp<MCProps = any> = 
    |LogicOp<MCProps> | (keyof MCProps & string)[];

export interface LogicOp<Props = any>{
    /**
     * Supported by trans-render
     */
    ifAllOf?: Keysh<Props>,

    ifKeyIn?: Keysh<Props>,  

    ifNoneOf?: Keysh<Props>,

    ifEquals?: Array<Key<Props>>,

    ifAtLeastOneOf?: Keysh<Props>,

    ifNotAllOf?: Keysh<Props>,

    debug?: boolean,

    delay?: number,

    do?: (x: Props) => (Promise<Partial<Props>> | Partial<Props>)

}

export interface SetLogicOps<Props = any>{

    a?: boolean,

    ifAllOf?: Set<Key<Props>>,

    ifKeyIn?: Set<Key<Props>>,  

    ifNoneOf?: Set<Key<Props>>,

    ifEquals?: Set<Key<Props>>,

    ifAtLeastOneOf?: Set<Key<Props>>,

    ifNotAllOf?: Set<Key<Props>>,

    debug?: boolean,

    delay?: number,

    do?: (x: Props) => (Promise<Partial<Props>> | Partial<Props>),
}

export interface Action<MCProps = any, MCActions = MCProps> extends LogicOp<MCProps>{
    target?: keyof MCProps; 
    debug?: boolean;
    secondArg?: any;
    //setFree?: (keyof MCProps & string)[],
}

export interface IActionProcessor{
    doActions(self: IActionProcessor, actions: {[methodName: string]: Action}, target: any, proxy?: any): void;
    postHoc(self: this, action: Action, target: any, returnVal: any, proxy?: any): void;
}

type PropInfoTypes = "String" | "Number" | "Boolean" | "Object" | "RegExp";

export interface IshPropInfo<TProps = any, TActions = any>{
    type?: PropInfoTypes;
    dry?: boolean;
    ro?: boolean;
    propName?: string;
    /**
     * Allow for discarding what is passed in favor of a modified value such as a formatted value
     * or filtered list
     */
    adjuster?: 
        |keyof TActions & string 
        |((nv: any) => any)
}

export interface PropInfo<TProps=any, TActions=any> extends IshPropInfo<TProps, TActions>{

    parse?: boolean;
    def?: any;
    attrName?: string;
    /**
     * form associated read only property
     * https://web.dev/articles/more-capable-form-controls#:~:text=Form-associated%20custom%20elements%20aim%20to%20bridge%20the%20gap,associated%20with%20the%20form%2C%20like%20a%20browser-provided%20control.
     * examples: form, validity, validityMessage, willValidate
     */
    farop?: boolean;
    /**
     * form associated read only method
     * examples: checkValidity, reportValidity
     */
    farom?: 'checkValidity' | 'reportValidity';
    
    /**
     * form associated write method
     */
    fawm?: 'setValidity' | 'setFormValue'

    /**
     * internals pass through property
     * examples: role, ariaRole
     */
    ip?: boolean;


}

export type ConstString = string;

export type NameOfProp = string;

export type StringOrProp = ConstString | [NameOfProp, PropInfo];

export type Parts = Array<StringOrProp>;

export interface PropChangeInfo<TPropInfo = PropInfo> {
    key: string,
    ov: any,
    nv: any,
    prop: TPropInfo,
    pcm: PropChangeMethod | undefined;
}

//are these still really used?
export type PropChangeMoment = 'v' | '-a' | '+a' | '+qr' | '+qm';

export type PropChangeMethod = (self: EventTarget, pci: PropChangeInfo, moment: PropChangeMoment) => boolean;

export type Actions<TProps = any, TActions = TProps> = 
    Partial<{[key in keyof TActions & string]: LogicOp<TProps>}>
    //& Partial<{[key in `do_${keyof TActions & string}_on`]: Key<TActions> | Array<Key<TActions>> }> 
;

export type Checks<TProps = any, TActions = TProps> = 
    Partial<{[key in keyof TActions & string]: SetLogicOps<TProps>}>

export type roundaboutOptions<TProps = any, TActions = TProps, ETProps = TProps> = {
    vm?: TProps & TActions & RoundaboutReady,
    //for enhanced elements, pass in the container, referenced via $0.
    container?: EventTarget,
    propagate?: keyof TProps & string | Array<keyof TProps & string>,
    actions?: Actions<TProps,TActions>,
    compacts?: Compacts<TProps, TActions>,
    //onsets?: Onsets<TProps, TActions>,
    handlers?: Handlers<ETProps, TActions>,
    hitch?: Hitches<TProps, TActions>,
    positractions?: Positractions<TProps>,
    mountObservers?: Set<IMountObserver>
}

export type PropsToPartialProps<TProps = any> = 
 | ((self: TProps, a: any, b: any) => Promise<Partial<TProps>>) 
 | ((self: TProps) => Partial<TProps>);

export type Infractions<TProps = any> = 
    //| PropsToPartialProps<TProps> 
    | Array<PropsToPartialProps<TProps>> 



export type Busses = {[key: string]: Set<string>};

export type Routers = {[key: string]: Array<{
    on: string,
    do: string,
    full: string,
}>}

export type Key<T = any> = keyof T & string;

export type Keysh<T = any> = Key<T> | Array<Key<T>>;

export interface RoundaboutReady{
    /**
     * Allow for assigning to read only props via the "backdoor"
     * Bypasses getters / setters, sets directly to (private) memory slots
     * Doesn't do any notification
     * Allows for nested property setting
    */
    covertAssignment(obj: any): Promise<void>;

    /**
     * fires event with name matching the name of the property when the value changes (but not via covertAssignment)
     * when property is set via public interface, not (immediately) via an action method's return object
     */
    readonly propagator : EventTarget | undefined;

    /**
     * 
     * https://github.com/whatwg/dom/issues/1296
     */
    //readonly disconnectedSignal: AbortSignal

    RAController: AbortController;

    /**
     * During this time, queues/buses continue to perform "bookkeeping"
     * but doesn't process the queue until sleep property becomes falsy.
     * If truthy, can call await awake() before processing should resume
     * [TODO]
     */  
    readonly sleep?: number,

    awake(): Promise<void>;

    nudge(): void;

    rock(): void;
}



export interface BaseProps{
    proppedUp: boolean,
    sleep?: number;
}

export interface ICompact{
    compacts: Compacts,
}

interface CompactStatement {
    srcKey: string,
    destKey: string,
    op: 'toggle' | 'negate' | 'call' | 'pass_length' | 'echo' | 'inc' | 'dispatch',
    rhsIsDynamic: boolean
}

interface HitchStatement {
    leftKey: string,
    middleKey: string,
    rightKey: string,
    lOp: 'when'
    lmOp: 'emits',
    mrOp: 'inc',
    rOp: 'by'
}

export type CommandMethod<T extends EventTarget = EventTarget> = (self: T, evt: Event) => Partial<T> | Promise<Partial<T>>

export type HookupConfig<T extends EventTarget = EventTarget> = {[key: string]: CommandMethod | [string, CommandMethod]};



