import { Awaitable } from '@antfu/utils';
import { FSWatcher } from 'chokidar';

declare class PageContext {
    private _pageRouteMap;
    rawOptions: UserOptions;
    root: string;
    options: ResolvedOptions;
    watcher?: FSWatcher;
    constructor(userOptions: UserOptions, projectRoot?: string);
    setupWatcher(watcher: FSWatcher): void;
    addPage(path: string | string[], pageDir: PageOptions): Promise<void>;
    removePage(path: string): Promise<void>;
    onUpdate(): void;
    writeFile(): Promise<void>;
    resolveRoutes(): Promise<any>;
    searchGlob(): Promise<void>;
    get debug(): any;
    get pageRouteMap(): any;
}

interface ReactRouteBase {
    caseSensitive?: boolean;
    children?: ReactRouteBase[];
    element?: string;
    index?: boolean;
    path?: string;
    rawRoute: string;
}
interface ReactRoute extends Omit<Optional<ReactRouteBase, 'rawRoute' | 'path'>, 'children'> {
    children?: ReactRoute[];
}

interface SolidRouteBase {
    rawRoute: string;
    path: string;
    children?: SolidRouteBase[];
    component?: string;
    element?: string;
}
interface SolidRoute extends Omit<Optional<SolidRouteBase, 'rawRoute' | 'path'>, 'children'> {
    children?: SolidRoute[];
}

interface VueRouteBase {
    name: string;
    path: string;
    props?: boolean;
    component: string;
    children?: VueRouteBase[];
    customBlock?: CustomBlock;
    rawRoute: string;
}
interface VueRoute extends Omit<Optional<VueRouteBase, 'rawRoute' | 'name'>, 'children'> {
    children?: VueRoute[];
}

type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
type ImportMode = 'sync' | 'async';
type ImportModeResolver = (filepath: string, pluginOptions: ResolvedOptions) => ImportMode;
type CustomBlock = Record<string, any>;
type InternalPageResolvers = 'vue' | 'react' | 'solid';
interface PageOptions {
    /**
     * Page base directory.
     * @default 'src/pages'
     */
    dir: string;
    /**
     * Page base route.
     */
    baseRoute: string;
    /**
     * @deprecated use `filePattern` instead
     */
    filePatern?: string;
    /**
     * Page file pattern.
     * @example `**\/*.page.vue`
     */
    filePattern?: string;
}
interface PageResolver {
    resolveModuleIds: () => string[];
    resolveExtensions: () => string[];
    resolveRoutes: (ctx: PageContext) => Awaitable<string>;
    getComputedRoutes: (ctx: PageContext) => Awaitable<VueRoute[] | ReactRoute[] | SolidRoute[]>;
    stringify?: {
        dynamicImport?: (importPath: string) => string;
        component?: (importName: string) => string;
        final?: (code: string) => string;
    };
}
/**
 * Plugin options.
 */
interface Options {
    /**
     * Paths to the directory to search for page components.
     * @default 'src/pages'
     */
    dirs?: string | (string | PageOptions)[];
    /**
     * Valid file extensions for page components.
     * @default ['vue', 'js']
     */
    extensions?: string[];
    /**
     * List of path globs to exclude when resolving pages.
     */
    exclude?: string[];
    /**
     * Import routes directly or as async components
     * @default 'root index file => "sync", others => "async"'
     */
    importMode?: ImportMode | ImportModeResolver;
    /**
     * Import page components from absolute or relative paths.
     * @default 'relative'
     */
    importPath?: 'absolute' | 'relative';
    /**
     * Sync load top level index file
     * @default true
     * @deprecated use `importMode` instead
     */
    syncIndex?: boolean;
    /**
     * Use Nuxt.js style route naming
     * @default false
     * @deprecated use `routeStyle` instead
     */
    nuxtStyle?: boolean;
    /**
     * Routing style
     * @default false
     */
    routeStyle?: 'next' | 'nuxt' | 'remix';
    /**
     * Separator for generated route names.
     * @default -
     */
    routeNameSeparator?: string;
    /**
     * Case for route paths
     * @default false
     */
    caseSensitive?: boolean;
    /**
     * Set the default route block parser, or use `<route lang=xxx>` in SFC route block
     * @default 'json5'
     */
    routeBlockLang?: 'json5' | 'json' | 'yaml' | 'yml';
    /**
     * Module id for routes import
     * @default '~pages'
     */
    moduleId?: string;
    /**
     * Generate React Route
     * @default 'auto detect'
     */
    resolver: InternalPageResolvers | PageResolver;
    /**
     * Extend route records
     */
    extendRoute?: (route: any, parent: any | undefined) => any | void;
    /**
     * Custom generated routes
     */
    onRoutesGenerated?: (routes: any[]) => Awaitable<any[] | void>;
    /**
     * Custom generated client code
     */
    onClientGenerated?: (clientCode: string) => Awaitable<string | void>;
    /**
     * Paths to the directory to search for page components.
     * @deprecated use `dirs` instead
     */
    pagesDir?: string | (string | PageOptions)[];
    /**
     * Replace '[]' to '_' in bundle filename
     * @deprecated issue #122
     */
    replaceSquareBrackets?: never;
    /**
     * @name watcher
     */
    watcher?: boolean;
}
type UserOptions = Options;
interface ResolvedOptions extends Omit<Options, 'pagesDir' | 'replaceSquareBrackets' | 'nuxtStyle' | 'syncIndex' | 'moduleId'> {
    /**
     * Resolves to the `root` value from config.
     * @default config.root
     */
    root: string;
    /**
     * Resolved page dirs
     */
    dirs: PageOptions[];
    /**
     * Resolved page resolver
     */
    resolver: PageResolver;
    /**
     * RegExp to match extensions
     */
    extensionsRE: RegExp;
    /**
     * Module IDs for routes import
     */
    moduleIds: string[];
}

export type { Options as O };
