import * as _harlem_utilities from '@harlem/utilities';
import { Matcher, Matchable, Disposable, UnionToIntersection } from '@harlem/utilities';
import { Plugin, DeepReadonly, App, ComputedRef } from 'vue';

declare global {
    interface Window {
        $harlem: {
            createInstance(): HarlemInstance;
        };
    }
}
type BaseState = Record<PropertyKey, any>;
type StoreProducer<TState extends BaseState> = keyof StoreProducers<TState>;
type ReadState<TState extends BaseState> = DeepReadonly<TState>;
type WriteState<TState extends BaseState> = TState;
type StoreRegistrations = Record<string, Map<string, StoreRegistration>>;
type RegistrationType = 'ref' | 'reactive' | 'computed' | 'other';
type RegistrationValueProducer = () => unknown;
type Getter<TState extends BaseState, TResult> = (state: ReadState<TState>) => TResult;
type Mutator<TState extends BaseState, TPayload, TResult = void> = (state: WriteState<TState>, payload: TPayload) => TResult;
type Mutation<TPayload, TResult = void> = undefined extends TPayload ? (payload?: TPayload) => TResult : (payload: TPayload) => TResult;
type ActionBody<TState extends BaseState, TPayload = undefined, TResult = void> = (payload: TPayload, mutator: (mutate: Mutator<TState, undefined, void>) => void) => Promise<TResult>;
type Action<TPayload, TResult = void> = undefined extends TPayload ? (payload?: TPayload) => Promise<TResult> : (payload: TPayload) => Promise<TResult>;
type EventHandler<TData = any> = (payload?: EventPayload<TData>) => void;
type Trigger = <TPayload = any, TResult = any>(matcher: Matcher | Matchable, handler: TriggerHandler<TPayload, TResult>) => Disposable;
type TriggerHandler<TPayload = any, TResult = any> = (data: TriggerEventData<TPayload, TResult>) => void;
type BranchAccessor<TState extends BaseState, TValue> = (state: ReadState<TState>) => TValue;
type InternalStores = Map<string, InternalStore<BaseState>>;
type HarlemPlugin = (app: App, eventBus: EventBus, stores: InternalStores) => void;
type Extension<TState extends BaseState> = (store: InternalStore<TState>) => Record<string, any>;
type ExtensionAPIs<TExtensions extends Extension<BaseState>[]> = Record<string, any> extends UnionToIntersection<ReturnType<TExtensions[number]>> ? unknown : UnionToIntersection<ReturnType<TExtensions[number]>>;
type PublicStore<TState extends BaseState, TExtensions extends Extension<TState>[]> = Omit<Store<TState>, keyof ExtensionAPIs<TExtensions>> & ExtensionAPIs<TExtensions>;
interface StoreRegistration {
    type: RegistrationType;
    producer: RegistrationValueProducer;
}
interface EventBus {
    /**
     * Subscribe to an event.
     *
     * @param event - The name of the event to subscribe to
     * @param handler - A handler called when the event is fired
     */
    on(event: string, handler: EventHandler): Disposable;
    /**
     * Subscribe to an event. Once the event is fired once, this listener is automatically detached.
     *
     * @param event - The name of the event to subscribe to
     * @param handler - A handler called when the event is fired
     */
    once(event: string, handler: EventHandler): Disposable;
    /**
     * Unsubscribe from an event.
     *
     * @param event - The name of the event to unsubscribe from
     * @param handler - The handler the was registered to the event
     */
    off(event: string, handler: EventHandler): void;
    /**
     * Publish an event.
     *
     * @param event - The name of the event to publish
     * @param payload - An optional payload to publish with the event
     */
    emit(event: string, payload?: EventPayload): void;
}
interface EventPayload<TData = any> {
    /**
     * The name of the event sender. This could be the core library, any registered extension/plugin, or user-emitted events.
     */
    sender: string;
    /**
     * The store on which this event took place.
     */
    store: string;
    /**
     * A payload sent along with the event.
     */
    data: TData;
}
interface TriggerEventData<TPayload = any, TResult = any> {
    /**
     * The name of the mutation/action that fired this trigger.
     */
    name: string;
    /**
     * The payload provided to the mutation/action that fired this trigger.
     */
    payload: TPayload;
    /**
     * The result returned from the mutation/action that fired this trigger. This is only populated for after and success triggers.
     */
    result?: TResult;
}
interface StoreSnapshot<TState extends BaseState> {
    /**
     * A readonly copy of state in the snapshot
    */
    get state(): TState;
    /**
     * Apply the current snapshot's state to the store. This will essentially overwrite any changes to state since this snapshot was taken.
     * @param branchAccessor - An optional branch accessor to apply a partial part of this snapshot to the store.
     * @param mutationName - An optional mutation name to use when applying the snapshot. This is useful for identifying snapshot applications in the Harlem devtools.
     */
    apply<TValue>(branchAccessor?: BranchAccessor<TState, TValue>, mutationName?: string): void;
}
interface StoreBase<TState extends BaseState> {
    /**
     * Register a getter on this store
     *
     * @param name - The name of this getter
     * @param getter - A function returning the computed value from state
     */
    getter<TResult>(name: string, getter: Getter<TState, TResult>): ComputedRef<TResult>;
    /**
     * Register a mutation on this store
     *
     * @param name - The name of this mutation
     * @param mutator - A function used to mutate state. This function receives state and a payload as it's parameters.
     */
    mutation<TPayload, TResult = void>(name: string, mutator: Mutator<TState, TPayload, TResult>): Mutation<TPayload, TResult>;
    /**
     * Register an action on this store
     *
     * @param name - The name of this action
     * @param body - The function to execute as part of this action. This function receives a payload and mutator function as it's parameters.
     */
    action<TPayload, TResult = void>(name: string, body: ActionBody<TState, TPayload, TResult>): Action<TPayload, TResult>;
    /**
     * Listen to an event on this store. This is useful for creating triggers.
     *
     * @param event - The name of the event to listen to
     * @param handler - The handler that will be called when the event is triggered
     */
    on(event: string, handler: EventHandler): Disposable;
    /**
     * Listen to an event on this store (only executed once)
     *
     * @param event - The name of the event to listen to
     * @param handler - The handler that will be called when the event is triggered
     */
    once(event: string, handler: EventHandler): Disposable;
    /**
     * Suppress events emitted from this store for the duration of the function callback
     *
     * @param callback - A function during which all events will be suppressed on this store
     */
    suppress<TResult = void>(callback: () => TResult): TResult;
    /**
     * Take a snapshot of this store's current state
     */
    snapshot(): StoreSnapshot<TState>;
    /**
     * Reset this store back to it's intial state
     *
     * @param branchAccessor - An optional function that returns a sub-branch of state to reset
     */
    reset<TValue>(branchAccessor?: BranchAccessor<TState, TValue>): void;
    /**
     * Destroy this store
     */
    destroy(): void;
}
interface StoreProducers<TState extends BaseState> {
    /**
     * The provider used when exposing writable state
     *
     * @param state - The writable state object
     */
    read(state: ReadState<TState>): ReadState<TState>;
    /**
     * The provider used when exposing writable state
     *
     * @param state - The writable state object
     */
    write(state: WriteState<TState>): WriteState<TState>;
    /**
     * The provider used when exposing payloads to mutators
     *
     * @param payload - The payload supplied to the mutation (or requester)
     */
    payload<TPayload>(payload: TPayload): TPayload;
}
interface InternalStore<TState extends BaseState = BaseState> extends StoreBase<TState> {
    /**
     * The name of this store
     */
    readonly name: string;
    /**
     * A boolean indicating whether this store allows overwriting duplicate registrations
     */
    readonly allowsOverwrite: boolean;
    /**
     * The current (readonly) state object
     */
    readonly state: ReadState<TState>;
    /**
     * Flags defined on this store
     */
    readonly flags: Map<string, unknown>;
    /**
     * The producers for this store
     */
    readonly producers: StoreProducers<TState>;
    /**
     * The items registered with this store
     */
    readonly registrations: StoreRegistrations;
    /**
     * Checks whether an item with the specified name is registered under the specified group on this store
     *
     * @param group - The group this item is registered under
     * @param name - The name of the registration
     */
    hasRegistration(group: string, name: string): boolean;
    /**
     * Gets a registered item with the specified name
     *
     * @param group - The group this item is registered under
     * @param name - The name of the registration
     */
    getRegistration(group: string, name: string): StoreRegistration | undefined;
    /**
     * Register a new item on this store
     *
     * @param group - The group this item will be registered under
     * @param name - The name of this registration
     * @param valueProducer - A function returning the value that represents this registration
     * @param type - The type of registration this is
     */
    register(group: string, name: string, valueProducer: RegistrationValueProducer, type?: RegistrationType): void;
    /**
     * Remove a registration from this store
     *
     * @param group - The group this item is registered under
     * @param name - The name of the registration
     */
    unregister(group: string, name: string): void;
    /**
     * Emit an event from this store
     *
     * @param event - The name of the event to emit
     * @param sender - The name of the sender
     * @param data - Any data to be emitted with this event
     */
    emit(event: string, sender: string, data: any): void;
    /**
     * Register reactive effects with this store to be disposed when the store is destroyed
     *
     * @param callback - A function during which reactive effects will be tracked
     */
    track<TResult>(callback: () => TResult): TResult;
    /**
     * Perform a write operation on this store
     *
     * @param name - The name that will be used for this mutation operation
     * @param sender - The sender of the mutation
     * @param mutator - A function which will be used to mutate state
     * @param suppress - A boolean indication whether to suppress events for this mutation
     */
    write<TResult = void>(name: string, sender: string, mutator: Mutator<TState, undefined, TResult>, suppress?: boolean): TResult;
}
interface InternalStoreOptions<TState extends BaseState> {
    /**
     * A boolean indicating whether this store allows overwriting duplicate registrations
     */
    allowsOverwrite: boolean;
    /**
     * A set of providers used by this store
     */
    producers: Partial<StoreProducers<TState>>;
}
interface StoreOptions<TState extends BaseState, TExtensions extends Extension<TState>[]> extends InternalStoreOptions<TState> {
    /**
     * An optional array of extensions to extend this store with
     */
    extensions?: TExtensions;
}
interface Store<TState extends BaseState> extends StoreBase<TState> {
    /**
     * The current (readonly) state object
     */
    state: ReadState<TState>;
    /**
     * The trigger called before a mutation runs
     */
    onBeforeMutation: Trigger;
    /**
     * The trigger called after a mutation runs, regardless of it was successful or not
     */
    onAfterMutation: Trigger;
    /**
     * The trigger called upon successful completion of a mutation
     */
    onMutationSuccess: Trigger;
    /**
     * The trigger called when a mutation fails
     */
    onMutationError: Trigger;
    /**
     * The trigger called before an action runs
     */
    onBeforeAction: Trigger;
    /**
     * The trigger called after an action runs, regardless of it was successful or not
     */
    onAfterAction: Trigger;
    /**
     * The trigger called upon successful completion of an action
     */
    onActionSuccess: Trigger;
    /**
     * The trigger called when an action fails
     */
    onActionError: Trigger;
}
interface HarlemOptions {
    /**
     * An optional array of plugins to register with Harlem
     */
    plugins?: HarlemPlugin[];
}
interface HarlemInstance extends Omit<EventBus, 'emit'> {
    /**
     * Attach Harlem to a Vue application. This is required for Harlem plugins to be usable.
     *
     * @param app - The Vue application instance to attach to
     * @param options - Harlem options
     */
    createVuePlugin(options?: HarlemOptions): Plugin;
    /**
     * Create a new Harlem store.
     *
     * @param name - The name of this store.
     * @param state - The initial state of this store.
     * @param options - Additional options used to configure this store.
     *
     * @example
     * // Define the initial state of this store
     * const STATE = {
     *     firstName: 'John',
     *     lastName: 'Smith'
     * };
     *
     * // Create the store with the initial state and any options/extensions
     * const {
     *     state,
     *     getter,
     *     mutation,
     *     action
     * } = createStore('app', STATE, {
     *     extensions: [
     *         actionExtension()
     *     ]
     * })
     */
    createStore<TState extends BaseState, TExtensions extends Extension<TState>[]>(name: string, state: TState, options?: Partial<StoreOptions<TState, TExtensions>>): PublicStore<TState, TExtensions>;
}

declare const EVENTS: {
    readonly core: {
        readonly installed: "core:installed";
    };
    readonly store: {
        readonly created: "store:created";
        readonly ready: "store:ready";
        readonly destroyed: "store:destroyed";
    };
    readonly mutation: {
        readonly before: "mutation:before";
        readonly after: "mutation:after";
        readonly success: "mutation:success";
        readonly error: "mutation:error";
    };
    readonly action: {
        readonly before: "action:before";
        readonly after: "action:after";
        readonly success: "action:success";
        readonly error: "action:error";
    };
    readonly ssr: {
        readonly initServer: "ssr:init:server";
        readonly initClient: "ssr:init:client";
    };
    readonly devtools: {
        readonly update: "devtools:update";
        readonly reset: "devtools:reset";
    };
};
declare const PRODUCERS: StoreProducers<any>;
declare const INTERNAL: {
    readonly prefix: "$harlem:";
    readonly pattern: RegExp;
};

/**
 * Create a new instance of Harlem. This is useful in multi-app scenarios.
 */
declare function createInstance(): HarlemInstance;
declare const on: (event: string, handler: EventHandler<any>) => _harlem_utilities.Disposable;
declare const off: (event: string, handler: EventHandler<any>) => void;
declare const once: (event: string, handler: EventHandler<any>) => _harlem_utilities.Disposable;
declare const createVuePlugin: (options?: HarlemOptions | undefined) => Plugin;
declare const createStore: <TState extends BaseState, TExtensions extends Extension<TState>[]>(name: string, state: TState, options?: Partial<StoreOptions<TState, TExtensions>> | undefined) => PublicStore<TState, TExtensions>;

export { Action, ActionBody, BaseState, BranchAccessor, EVENTS, EventBus, EventHandler, EventPayload, Extension, ExtensionAPIs, Getter, HarlemInstance, HarlemOptions, HarlemPlugin, INTERNAL, InternalStore, InternalStoreOptions, InternalStores, Mutation, Mutator, PRODUCERS, PublicStore, ReadState, RegistrationType, RegistrationValueProducer, Store, StoreBase, StoreOptions, StoreProducer, StoreProducers, StoreRegistration, StoreRegistrations, StoreSnapshot, Trigger, TriggerEventData, TriggerHandler, WriteState, createInstance, createStore, createVuePlugin, off, on, once };
