import { ViteDevServer, FilterPattern, IndexHtmlTransformContext, IndexHtmlTransformResult, Plugin } from 'vite';
import { Rewrite } from 'connect-history-api-fallback';
import { Options } from 'html-minifier-terser';

type AllowedEvent = 'add' | 'unlink' | 'change' | 'unlinkDir' | 'addDir';
/**
 * Ensure string follow the extension convention
 */
type ExtStr<E extends string, T extends string> = T extends `/${infer P}` ? ExtStr<E, P> : T extends `${infer Q}.${E}` ? ExtStr<E, Q> : `${T}.${E}`;
type FileStr<T extends string> = ExtStr<'html', T>;
type TplStr<T extends string> = ExtStr<'ejs' | 'html', T>;
interface Page<Name extends string = string, Filename extends string = string, Tpl extends string = string> {
    /**
     * Required. Name is used to generate default rewrite rules, it just a common string and please don't include '/'.
     * You can use filename option not name option if you want to customize the path of generated files.
     */
    name: Name extends `${string}/${string}` ? never : Name;
    /**
     * Relative path to the output directory, which should end with .html and not startWith '/'
     * @default `${name}.html`
     */
    filename?: FileStr<Filename>;
    /**
     * **Higher priority template file**, which will overwrite the default template.
     */
    template?: TplStr<Tpl>;
    /**
     * Entry file that will append to body, which you should remove from the EJS template file.
     * It must always start with `'/'` which represents your project root directory.
     */
    entry?: `/${string}`;
    /**
     * Data to inject with ejs.
     */
    data?: Record<string, any>;
}
type WatchHandler<Event extends AllowedEvent = AllowedEvent> = (ctx: {
    server: ViteDevServer;
    file: string;
    type: Event;
    /**
     * You can update the pages configuration by calling this function.
     * @params pages Your MPA core configurations, which will replace default `pages` config
     */
    reloadPages: <PN extends string, PFN extends string, PT extends string>(pages: Page<PN, PFN, PT>[]) => void;
}) => void;
interface WatchOptions<Event extends AllowedEvent = AllowedEvent> {
    /**
     * Specifies the files to **include**, based on `Rollup.createFilter`
     * @see https://vitejs.dev/guide/api-plugin.html#filtering-include-exclude-pattern
     */
    include?: Exclude<FilterPattern, null>;
    /**
     * Specifies the files to **exclude**, based on `Rollup.createFilter`
     * @see https://vitejs.dev/guide/api-plugin.html#filtering-include-exclude-pattern
     */
    excluded?: Exclude<FilterPattern, null>;
    /**
     * File events you wanna deal with.
     * @default ['add', 'unlink', 'change', 'unlinkDir', 'addDir']
     */
    events?: Event[];
    /**
     * Execute your own logic when file events fired.
     */
    handler: WatchHandler<Event>;
}
interface ScanOptions {
    /**
     * Directory path to scan, subdirectory's name will be used as unique page name.
     */
    scanDirs: string | string[];
    /**
     * Customize the **entry file** path relative to the scanned dir.
     * It doesn't effect when the file specified doesn't exist.
     */
    entryFile?: string;
    /**
     * Customize the **virtual file** name(output filename).
     * @param name Subdirectory name
     */
    filename?: (name: string) => string;
    /**
     * Customize the **template file** path relative to the scanned dir.
     * It doesn't effect when the file specified doesn't exist.
     */
    template?: string;
}
type RewriteRule = false | Rewrite[];
interface MpaOptions<PageName extends string, PageFilename extends string, PageTpl extends string, Event extends AllowedEvent, DefTpl extends string> {
    /**
     * Whether to print log.
     * @default true
     */
    verbose?: boolean;
    /**
     * Default template file.
     * @default index.html
     */
    template?: TplStr<DefTpl>;
    /**
     * Configure your dev server's rewrite rules, only proceed fallback html requests.
     * @see https://github.com/bripkens/connect-history-api-fallback
     */
    rewrites?: RewriteRule;
    /**
     * Configure your preview server's rewrite rules.
     * @see https://github.com/bripkens/connect-history-api-fallback
     */
    previewRewrites?: RewriteRule;
    /**
     * Sometimes you might want to reload `pages` config or restart ViteDevServer when
     * there are some files added, removed, changed and so on. You can set `watchOptions` to
     * customize your own logic.
     */
    watchOptions?: WatchHandler<Event> | WatchOptions<Event>;
    /**
     * Your MPA core configurations, you can write directly or use `createPages` function independently outside and then pass it to this field.
     */
    pages?: Page<PageName, PageFilename, PageTpl>[];
    /**
     * Use to scan directories that have similar structure to generate pages.
     * Detected pages will be appended to `pages` option, page with name existed will be ignored.
     */
    scanOptions?: ScanOptions;
    /**
     * Dedicated hook for transforming template HTML, inherit from `transformIndexHtml`.
     * @see https://vitejs.dev/guide/api-plugin#transformindexhtml
     */
    transformHtml?: (html: string, ctx: IndexHtmlTransformContext & {
        page: Page;
    }) => IndexHtmlTransformResult;
    /**
     * Whether to minify html file. Powered by [html-minify-terser](https://github.com/terser/html-minifier-terser).
     * @default false
     */
    htmlMinify?: Options | boolean;
}

/**
 * This function simply converts the arguments to an array and returns them.
 * It helps creating pages configuration with type hints independently outside plugin function.
 */
declare function createPages<Name extends string, Filename extends string, Tpl extends string>(pages: Page<Name, Filename, Tpl> | Page<Name, Filename, Tpl>[]): Page<Name, Filename, Tpl>[];

declare function createMpaPlugin<PN extends string, PFN extends string, PT extends string, Event extends AllowedEvent, TPL extends string>(config: MpaOptions<PN, PFN, PT, Event, TPL>): Plugin[];

export { type AllowedEvent, type ExtStr, type FileStr, type MpaOptions, type Page, type RewriteRule, type ScanOptions, type TplStr, type WatchHandler, type WatchOptions, createMpaPlugin, createPages };
