import { MountContext, PipelineStage } from "../mount-observer/types";
import { ConvertOptions, Scope } from "./lib/types";
import { EMC} from './be/types';


export type PropAttrQueryType = 
    | '|' //microdata itemprop
    | '@' //form element name
    | '#' //id
    | '%' //part
    | '.' //class
    //| '-' //marker
    | '$' //microdata itemprop + itemscope attributes (nested)
    | '-o'

export type PropAttrPair<TProps> = `${PropAttrQueryType} ${keyof TProps & string}`;

export type PropQueryExpression<TProps, TElement = {}> =
    
    | `* ${CSSQuery}`
    | `:root`
    | `${keyof HTMLElementTagNameMap}`
    | `${PropAttrPair<TProps>}`
    | `${PropAttrPair<TProps>} -s ${keyof TElement & string}`
    | `${PropAttrPair<TProps>} ${PropAttrPair<TProps>}`
    | `${PropAttrPair<TProps>} ${PropAttrPair<TProps>} -s ${keyof TElement & string}`
    // | `${PropAttrPair<TProps>} ${PropAttrPair<TProps>} ${PropAttrPair<TProps>}`
    // | `${PropAttrPair<TProps>} ${PropAttrPair<TProps>} ${PropAttrPair<TProps>} -s ${keyof TElement & string}`
    //
    
;

//#region derived expressions
export type Expr0 = [string, number];
export type Expr1 = [...Expr0, string];
export type Expr2 = [...Expr1, number];
export type Expr3 = [...Expr2, string];
export type Expr4 = [...Expr3, number];
export type Expr5 = [...Expr4, string];
export type Expr6 = [...Expr5, number];
export type Expr7 = [...Expr6, string];
export type Expr8 = [...Expr7, number];
export type Expr9 = [...Expr8, string];
export type Expr10 = [...Expr9, number];
export type Expr11 = [...Expr10, string];
export type Expr12 = [...Expr11, number];

//export type Action<TProps> = (matchingElement: Element, pique: IMountOrchestrator<TProps>) => Promise<Derivative<TProps>> | Promise<void>;
export type InterpolatingExpression = Expr0 | Expr1 | Expr2 | Expr3 | Expr4 | Expr5 | Expr6 | Expr7 | Expr8 | Expr9 | Expr10 | Expr11 | Expr12;
export type NumberExpression = [number];
export type DerivationCriteria<TProps, TMethods> = {
    //TODO
    path: string,
    from?: number,
    //TODO
    as?: ConvertOptions,
    //TODO - applicable to arrays
    filter?: keyof TModhods & string | ((val: any) => boolean),
    //TODO
    //map?: keyof TModhods & string | ((val: any) => any,
};

export interface TransformOptions{
    propagator?: MarkedUpEventTarget,
    propagatorIsReady?: boolean,
    skipInit?: boolean,
    useViewTransition?: boolean,
}

export type Derivative<TProps, TMethods, TElement = {}> = 
    | number 
    | InterpolatingExpression 
    | ((model: TProps & TMethods, transform: ITransformer<TProps, TMethods, TElement>, uow: UnitOfWork<TProps, TMethods, TElement>, matchingElement: Element) => any)
    | NumberExpression 
    | DerivationCriteria<TProps, TMethods>
    // only works if lhs has field/property
    | keyof TMethods & string 
    // combined observe an 0
    | keyof TProps & string
    | boolean
;
//#endregion

export interface Engagement<TMethods>{
    /** Invoked when the element is encountered. */
    do?: keyof TMethods & string,
    /** Invoked when a previously matching element is no longer matching. */
    undo?: keyof TMethods & string,
    /** Invoked when a previously matching element is disconnected. */
    forget?: keyof TMethods & string,
    /**
     * Can be used for any kind of label, but most common use is for specifying a behavior/enhancement
     * to attach.
     */
    be?: EMC,
    with?: any,
    waitForResolved?: boolean,
    dep?: () => void;


}


export type onMountStatusChange = 'onMount' | 'onDismount' | 'onDisconnect';

export interface EngagementCtx<TModel> {
    be?: string,
    with?: any,
    type: onMountStatusChange,
    mountContext: MountContext
}

export type EngagementOrKeyOrEMC<TMethods, TProps = TMethods> = 
    | (keyof TMethods & string) 
    | Engagement<TMethods>
    | EMC<any, TProps>
;

export type EngagementOrEMC<TMethods, TProps = TMethods> = 
    | Engagement<TMethods>
    | EMC<any, TProps>

export type Engagements<TMethods, TProps = TMethods> = 
    | EngagementOrKeyOrEMC<TMethods>
    | Array<EngagementOrKeyOrEMC<TMethods>>
;

export interface IMountOrchestrator<TProps, TMethods = TProps>{
    //TODO add all the methods
}

// export type OneOrMore<TProps> = 
//     | keyof TProps & string
//     | `${keyof TProps & string} ${keyof TProps & string}`




export type LHS<TProps, TElement={}> = PropQueryExpression<TProps, TElement>;

export type CSSQuery = string;

export interface ConditionGate<TProps, TMethods, TElement = {}>{
    ifAllOf?: number[],
    ifNoneOf?: number[],
    ifEqual?: [number, number | [number] | string],
    d?: Derivative<TProps, TMethods, TElement>,

}

export interface ScopingConfig<TProps=any, TMethods = TProps> {
    name: string;
    config?: IshConfig<TProps, TMethods>;
}

export type ScopeInstructions<TProps=any, TMethods=TProps> = 
    | ScopingConfig
;

export interface ScopedLoop<TProps = any, TMethods = TProps>{
    config?: IshConfig<TProps, TMethods>;
    options: Partial<Clone$Options>
}

export type WhereConditions = 
    | string //css matches
    | {
        matches: string,
        mediaMatches: string,
        containerQuery: string,
    }

export type IfInstructions<TProps, TMethods, TElement = {}> = string | boolean | number | [number] | ConditionGate<TProps, TMethods, TElement> ;

export interface ObservePropParams {
    derivePropFrom?: string,
}
export type PropOrComputedProp<TProps, TMethods = TProps> = 
    | keyof TProps & string
    | [keyof TProps & string, (val: any) => any]
    | [keyof TProps & string, keyof TMethods & string]
    | ObservePropParams
    | `:${string}`

export interface CrossProduct<TProps, TMethods> {
    x: string | Array<string>,
    y: (keyof TProps & TMethods & string) | Array<keyof TProps & TMethods & string>
}

export interface ForEach<TProps, TMethods, TElement = {}>{
    each?: 0,
    /** css query to locate the template */
    clone?: string,
    /** within scope for locating the template */
    wi?: 'node' | 'rootNode' | 'upShadowSearch'
    indexProp?: string,
    xform: XForm<TProps, TMethods, TElement> & Info,
    appendTo?: string,
    timestampProp?: string,
    outOfRangeAction?: string,
    outOfRangeProp?: string,
}

// export interface MapInstructions<TProps, TMethods, TElement = {}>{
//     // itemCss: CSSQuery,
//     // each: string | [string, IshConfig<TProps, TMethods, TElement>],
//     // in: string | [string, IshConfig<TProps, TMethods, TElement>],
// }

export interface ForEachInterface{
    init(): Promise<void>;
    update(model: any[]): Promise<void>;
}
export interface UnitOfWork<TProps, TMethods = TProps, TElement = {}>{
    /**
     * add event listener
     */   
    addEventListener?: AddEventListenerType<TProps, TMethods> | Array<AddEventListenerType<TProps, TMethods>>,
    /**
     * abbrev. for addEventListener
     */
    a?:  AddEventListenerType<TProps, TMethods> | Array<AddEventListenerType<TProps, TMethods>>,

    /**
     * Specify how the value we want to apply to the target element should be derived from the observed props.
     * derived value from observed props
     */
    derivedValFromModel?: Derivative<TProps, TMethods, TElement>,
    /**
     * Specify how the value we want to apply to the target element should be derived from the observed props.
     * abbrev. for derivedValSpecs
     */
    d?: Derivative<TProps, TMethods, TElement>,

    /**
     * Specify what to do when the element is encountered, and/or when it goes out of scope.
     * Register the found element in some way.
     * Actions not tied to observed props or user actions.
     */
    engage?: Engagements<TMethods>
    /**
     * Specify what to do when the element is encountered, and/or when it goes out of scope.
     * Register the found element in some way.
     * Actions not tied to observed props or user actions.
     * Abbrev. for engagementActions
     */
    e?:  Engagements<TMethods>,

    forEachBinding?: ForEach<any, any, any>
    /**
     * for each -- deprecated?
     */
    f?: ForEach<any, any, any>,

    /**
     * ifs ands or buts -- conditions on the model
     */
    ifs?: IfInstructions<TProps, TMethods, TElement>,
    /**
     * ifs ands or buts -- conditions on the model
     * abbrev for ifs
     */
    i?: IfInstructions<TProps, TMethods, TElement>,

    /**
     * method of matching element to pass derived value into
     * [TODO]
     */
    invoke?: string,


    /**
     * modify the model in a (mostly) declarative  way
     */
    modifyModel?: ModificationUnitOfWork<TProps, TMethods, TElement> | Array<ModificationUnitOfWork<TProps, TMethods, TElement>>,
    /**
     * modify the model in a (mostly) declarative  way
     * abbreviation for modifyModel
     */
    m?: ModificationUnitOfWork<TProps, TMethods, TElement> | Array<ModificationUnitOfWork<TProps, TMethods, TElement>>,

    /**
     * List of props to observe from the model
     */
    observedProps?: keyof TProps & string | PropOrComputedProp<TProps, TMethods> | PropOrComputedProp<TProps, TMethods>[],
    /**
     * List of props to observe from the model
     * abbrev. for observedProps
     */
    o?: keyof TProps & string | PropOrComputedProp<TProps, TMethods> | PropOrComputedProp<TProps, TMethods>[],

    /**
     * set specified property of the matching element to the (derived) value
     */
    setProp?: (keyof TElement & string) | {},
    /**
     * set specified property of the matching element to the (derived) value
     * abbrev of setProp
     */
    s?: (keyof TElement & string) | {},
    /**
     * set specified attribute of the matching element to the (derived) value
     *  
     */
    setAttr?: string,
    /**
     * set specified attribute of the matching element to the (derived) value
     * abbrev of setAttr
     */
    sa?: string,
    /**
     * set specified style of the matching element to the (derived) value
     */
    ss?: string,

    /**
     * negate to
     */
    negTo?: string,

    /**
     * Where condition for selecting the target elements.
     */
    whereConditions?: WhereConditions,
    /**
     * Where conditions for selecting the target elements
     * abbrev. for whereConditions
     */
    w?: WhereConditions,

    y?: number | YieldSettings<TProps>,

    $?:  ScopeInstructions<TProps, TMethods>,

    $$?:  ScopedLoop<TProps, TMethods>,
}

export interface YieldSettings<TProps>{
    to?: keyof TProps,
    as?: 'date' | 'number'
}

export type ValueFromElement<TProps, TMethods, TElement = {}> = 
    (
        matchingElement: Element, 
        transformer: ITransformer<TProps, TMethods, TElement>, 
        mod: ModificationUnitOfWork<TProps, TMethods, TElement>
    ) => any

export interface ModificationUnitOfWork<TProps, TMethods, TElement = {}>{
    on: string,
    /**
     * Increment
     */
    inc?: keyof TProps & string,
    /**
     * Increment by specified number, or by specified property coming from matching element
     */
    byAmt?: number | string,
    /**
     * Set this prop on the host
     */
    s?: keyof TProps & string,
    /**
     * [TODO] -- Set Custom State --only available for xtal-element
     */
    ss?: string,
    /**
     * [TODO] -- Set attribute
     */
    sa?: string,
    /**
     * [TODO] enhance / engage the host, or register the host in some way
     * don't implement this until a good use case is found, make sure it makes sense.
     */
    e?:  Engagements<TMethods>,
    /**
     * [TODO]  Set hardcoded value
     */
    to?: any,
    toValFrom?: string | ValueFromElement<TProps, TMethods, TElement>;
    toggle?: keyof TProps & string,
}

export interface QuenitOfWork<TProps, TMethods, TElement = {}> extends UnitOfWork<TProps, TMethods, TElement>{
    q: string,
    qi?: QueryInfo,
}

export type UnitOfWorkRHS<TProps, TMethods, TElement = {}> = 
    | 0 
    | keyof TMethods & string 
    | keyof TProps & string
    | UnitOfWork<TProps, TMethods, TElement>
    | XForm<any, any, any> & Info //unclear if this is necessary
;

export type RHS<TProps, TMethods, TElements = Element> = UnitOfWorkRHS<TProps, TMethods, TElements> | Array<UnitOfWork<TProps, TMethods, TElements>>;

export interface AttrMap{
    type: PropAttrQueryType, 
    name: string
}

export interface QueryInfo{
    isRootQry?: boolean,
    localPropCamelCase?: string,
    cssQuery?: string,
    o?: string[],
    s?: string[],
    localName?: string,
    //w?: WhereConditions,
    css?: string,
    hostPropToAttrMap?: Array<AttrMap>
}

export type TransformerTarget = Element | DocumentFragment | Element[] | ShadowRoot | Document;

export type Model = {
    [key: string]: any
}

export type EventListenerAction<TProps, TMethods> = (keyof TMethods & string) | ((e: Event, t: ITransformer<TProps, TMethods>, uow: UnitOfWork<TProps, TMethods>) => void);

export type AddEventListenerType<TProps, TMethods> =
    | keyof TMethods & string 
    | AddEventListener<TProps, TMethods>;

export interface AddEventListener<TProps, TMethods>{
    on: string,
    do: EventListenerAction<TProps, TMethods>,
    options?: boolean | EventListenerOptions, 
}

export type XForm<TProps, TMethods, TElement = {}> = Partial<{
    [key in LHS<TProps, TElement>]: RHS<TProps, TMethods, TElement>;
}>;

export interface Info  {
    411?: {
        w?: string,
        //idxFrom?: string
    }
}



export interface ITransformer<TProps, TMethods, TElement = {}>{
    target: TransformerTarget,
    model: TProps & TMethods,
    xform: XForm<TProps, TMethods, TElement> & Info,
    options: TransformOptions,
    initializedMods: Set<ModificationUnitOfWork<TProps, TMethods, TElement>>
    //propagator?: EventTarget,
}

export type ToTransformer<TProps, TMethods, TElement = {}> = (
    target: TransformerTarget, 
    model: TProps & TMethods,
    xform: XForm<TProps, TMethods, TElement>,
    propagator?: EventTarget
) => ITransformer<TProps, TMethods>;

export interface MarkedUpEventTarget extends EventTarget{
    ___props?: Set<string>;
    ___nestedProps?: Map<string, any>;
}

export interface TransRenderEndUserProps<ModelProps, ModelMethods = ModelProps, TElement = {}>{
    xform: XForm<ModelProps, ModelMethods, TElement>;
    scope: Scope;
    //model?: ModelProps & ModelMethods;
    options?: TransformOptions;
}

export interface TransRenderProps<ModelProps, ModelMethods = ModelProps> extends TransRenderEndUserProps<ModelProps, ModelMethods>{
    
}

export interface TransRenderMethods{
    getTarget(): Promise<Document | ShadowRoot | DocumentFragment | Element>,
    getXForm(): Promise<XForm<any, any>>,
    getModel(): Promise<any>,
    skipInit: boolean,
}

import {IshConfig, OConfig} from './froop/types';
export interface MntCfg<TProps = any, TActions = TProps, ETProps = TProps> extends OConfig<TProps, TActions, ETProps>{
    mainTemplate: string | HTMLTemplateElement,
    /**
     * Only set to true if shadow dom is used and the light children play a critical role as far as 
     * progressive enhancement.
     */
    appendOnClone?: boolean,
    /**
     * transform within ShadowRoot if applicable
     */
    xform?: XForm<TProps, TActions>,
    /**
     * transform applied to light children, if applicable
     * Use ":root" to match on the root element
     */
    lcXform?: XForm<TProps, TActions>,


    /**
     * transforms within ShadowRoot if applicable that uses view transitions 
     */
    xxform?: XForm<TProps, TActions>

    styles?: /*CSSStyleSheet[] |*/ string | string[] | CSSStyleSheet | Array<CSSStyleSheet>;

    shadowRootInit?: ShadowRootInit,

    assumeCSR?: boolean,


}

export interface MountProps<TProps = any, TActions = TProps, ETProps = TProps>{
    readonly clonedTemplate?: DocumentFragment;
    deferHydration?: boolean;
    readonly hydrated?: boolean;
    readonly csr?: boolean;
    readonly xform?: XForm<TProps, TActions>,
    readonly xxform?: XForm<TProps, TActions>,
}
export type PMP<TProps = any, TActions = TProps, ETProps = TProps> = Partial<MountProps<TProps, TActions, ETProps>>;
export type ProPMP<TProps = any, TActions = TProps, ETProps = TProps> = Promise<PMP<TProps, TActions, ETProps>>

export interface MountActions<TProps = any, TActions = TProps, ETProps = TProps>{
    cloneMT(self: this): PMP;
    // inspect(self: this): PMP
    // mount(self: this): ProPMP
    initCSRXform(self: this): ProPMP<TProps, TActions, ETProps>;
    initSSRXform(self: this): ProPMP<TProps, TActions, ETProps>;
    onNoXForm(self: this): ProPMP<TProps, TActions, ETProps>;
    
    mountClone(self: this): Partial<MountProps<TProps, TActions, ETProps>>;
}

export interface IObject$tring{
    strVal: string | undefined;
    objVal: any;
    arrVal: any[] | undefined;
    parse(): Promise<void>;
}

export type ZeroOrMore<T> = T | Array<T> | undefined;

/**
 * https://x.com/mattpocockuk/status/1821926395380986219
 */
export type StringWithAutocompleteOptions<TOptions> = 
    | (string & {})
    | TOptions;

export interface Clone$Options{
    ish: HasIshList,
    ishContainer: Element,
    seedEl: Element,
    idxStart: number,
    itemProp: string,
    mapIdxTo?: string,
    itemTemplate: HTMLTemplateElement;
    baseCrumb: string,
    idleTimeout: number,
    //model?: any,
    listProp?: string,
}