type Component = any
type Dictionary<T> = { [key: string]: T }
type ErrorHandler = (err: Error) => void

export type RouterMode = "hash" | "history" | "abstract"
export type RawLocation = string | Location
export type RedirectOption = RawLocation | ((to: Route) => RawLocation)
export type NavigationGuardNext = (
    to?: RawLocation | false | void,
) => void

export type NavigationGuard = (
    to: Route,
    from: Route,
    next: NavigationGuardNext,
) => any

interface _History {
    current: Route;
}

export declare class VueRouter {
    constructor(options?: Obj)

    mode: RouterMode;
    currentRoute: Route;

    history: _History;

    beforeEach(guard: NavigationGuard): Function

    beforeResolve(guard: NavigationGuard): Function

    afterEach(hook: (to: Route, from: Route) => any): Function

    push(location: RawLocation): Promise<Route>

    replace(location: RawLocation): Promise<Route>

    push(
        location: RawLocation,
        onComplete?: Function,
        onAbort?: ErrorHandler,
    ): void

    replace(
        location: RawLocation,
        onComplete?: Function,
        onAbort?: ErrorHandler,
    ): void

    go(n: number): void

    back(): void

    forward(): void

    match(raw: RawLocation, current?: Route, redirectedFrom?: Location): Route

    getMatchedComponents(to?: RawLocation | Route): Component[]

    onReady(cb: Function, errorCb?: ErrorHandler): void

    onError(cb: ErrorHandler): void

    addRoutes(routes: RouteConfig[]): void

    addRoute(parent: string, route: RouteConfig): void
    addRoute(route: RouteConfig): void

    getRoutes(): RouteRecordPublic[]

    resolve(
        to: RawLocation,
        current?: Route,
        append?: boolean,
    ): {
        location: Location
        route: Route
        href: string
        // backwards compat
        normalizedTo: Location
        resolved: Route
    }

}

type RoutePropsFunction = (route: Route) => Object

interface _RouteConfigBase {
    path: string;
    name?: string;
    children?: RouteConfig[];
    redirect?: RedirectOption;
    alias?: string | string[];
    meta?: RouteMeta;
    beforeEnter?: NavigationGuard;
    caseSensitive?: boolean;
    pathToRegexpOptions?: PathToRegexpOptions;
}

interface RouteConfigSingleView extends _RouteConfigBase {
    component?: Component;
    props?: boolean | Object | RoutePropsFunction;
}

interface RouteConfigMultipleViews extends _RouteConfigBase {
    components?: Dictionary<Component>;
    props?: Dictionary<boolean | Object | RoutePropsFunction>;
}

type RouteConfig = RouteConfigSingleView | RouteConfigMultipleViews

interface PathToRegexpOptions {
    sensitive?: boolean;
    strict?: boolean;
    end?: boolean;
}

interface _RouteConfigBase {
    path: string;
    name?: string;
    children?: RouteConfig[];
    redirect?: RedirectOption;
    alias?: string | string[];
    meta?: RouteMeta;
    beforeEnter?: NavigationGuard;
    caseSensitive?: boolean;
    pathToRegexpOptions?: PathToRegexpOptions;
}

interface RouteRecord {
    path: string;
    regex: RegExp;
    components: Dictionary<Component>;
    name?: string;
    parent?: RouteRecord;
    redirect?: RedirectOption;
    matchAs?: string;
    meta: RouteMeta;
    beforeEnter?: (
        route: Route,
        redirect: (location: RawLocation) => void,
        next: () => void,
    ) => any;
    props:
        | boolean
        | Object
        | RoutePropsFunction
        | Dictionary<boolean | Object | RoutePropsFunction>;
}

interface RouteRecordPublic {
    path: string;
    components: Dictionary<Component>;
    name?: string;
    redirect?: RedirectOption;
    meta: any;
    beforeEnter?: (
        route: Route,
        redirect: (location: RawLocation) => void,
        next: () => void,
    ) => any;
    props:
        | boolean
        | Object
        | RoutePropsFunction
        | Dictionary<boolean | Object | RoutePropsFunction>;
}


interface Location {
    name?: string;
    path?: string;
    hash?: string;
    query?: Dictionary<string | (string | null)[] | null | undefined>;
    params?: Dictionary<string>;
    append?: boolean;
    replace?: boolean;
}

interface Route {
    path: string;
    name?: string | null;
    hash: string;
    query: Dictionary<string | (string | null)[]>;
    params: Dictionary<string>;
    fullPath: string;
    matched: RouteRecord[];
    redirectedFrom?: string;
    meta?: RouteMeta;
}

interface RouteMeta extends Record<string | number | symbol, any> {
}


export declare class Router {
    constructor(op: { [key: string]: any });

    static $router: VueRouter;

    route(route: string, callback: Function): this;
    route(route: string, name: string, callback?: Function): this;

    execute(callback?: Function, args?: any[]): void;

    navigate(fragment: string, options?: { [key: string]: any } | boolean): this;
}

export declare class History {
    atRoot(): boolean;

    getSearch(): string;

    getHash(window?: Window): string;

    getPath(): string;

    getFragment(fragment?: string): string;

    start(op?: { [key: string]: any }): void;

    stop(): void;

    route(route: string, callback: Function): void;

    checkRoute(route: string): { route: string, callback: Function };

    unRoute(route: string): void;

    checkUrl(e?: Event): void;

    loadUrl(fragment: string): boolean;

    navigate(fragment: string, options?: { [key: string]: any } | boolean): void;
}