import { Plugin } from 'vite';

interface RawCommit {
    /**
     * The file path for this commit.
     *
     * When the file is located in `srcDir`, the path is relative to `srcDir`.
     * Otherwise, the path is relative to cwd. Paths without `. /`.
     */
    path: string;
    /**
     * The hash of the commit.
     */
    hash: string;
    /**
     * The date of the commit.
     */
    date: string;
    /**
     * The message of the commit.
     */
    message: string;
    /**
     * The refs of the commit.
     */
    refs?: string;
    /**
     * The body of the commit.
     */
    body?: string;
    /**
     * The author name of the commit.
     */
    author_name: string;
    /**
     * The author email of the commit.
     */
    author_email: string;
}
interface MergedRawCommit extends Omit<RawCommit, 'path'> {
    paths: string[];
}
interface Commit extends Omit<MergedRawCommit, 'date' | 'body' | 'refs' | 'author_name' | 'author_email'> {
    /**
     * The file path for this commit.
     *
     * When the file is located in `srcDir`, the path is relative to `srcDir`.
     * Otherwise, the path is relative to cwd. Paths without `. /`.
     */
    paths: string[];
    /**
     * The matched first tag of the commit.
     */
    tag?: string;
    /**
     * The matched tags of the commit.
     */
    tags?: string[];
    /**
     * The URL of the release tag.
     */
    release_tag_url?: string;
    /**
     * The URLs of the release tags.
     */
    release_tags_url?: string[];
    /**
     * The hash of the commit.
     */
    hash: string;
    /**
     * The URL of the commit.
     */
    hash_url?: string;
    /**
     * The UNIX timestamp of the commit.
     */
    date_timestamp: number;
    /**
     * The message of the commit.
     */
    message: string;
    authors: string[];
    /**
     * The repository URL.
     */
    repo_url?: string;
}
interface SocialEntry {
    type: 'github' | 'twitter' | 'email' | string;
    link: string;
}
interface Contributor {
    /**
     * The overriding display name of the contributor in default locale
     */
    name?: string;
    /**
     * The overriding display name of the contributor in other locales if needed
     */
    i18n?: Record<string, string>;
    /**
     * The overriding GitHub, GitLab, Gitea username of the contributor
     */
    username?: string;
    /**
     * The overriding avatar of the contributor
     */
    avatar?: string;
    /**
     * Whether to add a link to the contributor's profile
     */
    links?: string | SocialEntry[];
    /**
     * More names to be recognized as the same contributor.
     *
     * Useful when you changed your name or email address in the past.
     *
     * @deprecated Use `mapByNameAliases` instead
     * @see mapByNameAliases
     */
    nameAliases?: string[];
    /**
     * More names to be recognized as the same contributor.
     *
     * Useful when you changed your name or email address in the past.
     */
    mapByNameAliases?: string[];
    /**
     * More emails to be recognized as the same contributor.
     *
     * Useful when you changed your email address in the past.
     *
     * @deprecated Use `mapByEmailAliases` instead
     * @see mapByEmailAliases
     */
    emailAliases?: string[];
    /**
     * More emails to be recognized as the same contributor.
     *
     * Useful when you changed your email address in the past.
     */
    mapByEmailAliases?: string[];
}

interface Helpers {
    /**
     * A helper function to help to determine whether the passed string parameter equals the
     * current transforming module ID with normalization of paths capabilities and
     * cross platform / OS compatibilities.
     *
     * @param equalsWith - String to equal with
     * @returns boolean
     */
    idEquals: (equalsWith: string) => boolean;
    /**
     * A helper function to help to determine whether the passed string parameter startsWith the
     * current transforming module ID with normalization of paths capabilities and
     * cross platform / OS compatibilities.
     *
     * @param startsWith - String to start with
     * @returns boolean
     */
    idStartsWith: (startsWith: string) => boolean;
    /**
     * A helper function to help to determine whether the passed string parameter endsWith the
     * current transforming module ID with normalization of paths capabilities and
     * cross platform / OS compatibilities.
     *
     * @param endsWith - String to end with
     * @returns boolean
     */
    idEndsWith: (endsWith: string) => boolean;
    /**
     * A helper function to help to determine whether the passed first path parameter
     * equals the second passed string with normalization of paths capabilities and
     * cross platform / OS compatibilities.
     *
     * @param path - Path to be compared with
     * @param equalsWith - String to equal with
     * @returns boolean
     */
    pathEquals: (path: string, equalsWith: string) => boolean;
    /**
     * A helper function to help to determine whether the passed first path parameter
     * startsWith the second passed string with normalization of paths capabilities and
     * cross platform / OS compatibilities.
     *
     * @param path - Path to be compared with
     * @param startsWith - String to start with
     * @returns boolean
     */
    pathStartsWith: (path: string, startsWith: string) => boolean;
    /**
     * A helper function to help to determine whether the passed first path parameter
     * endsWith the second passed string with normalization of paths capabilities and
     * cross platform / OS compatibilities.
     *
     * @param path - Path to be compared with
     * @param endsWith - String to end with
     * @returns boolean
     */
    pathEndsWith: (path: string, endsWith: string) => boolean;
}
type CommitToStringHandler = (commit: Commit) => string | Promise<string> | null | undefined;
type CommitToStringsHandler = (commit: Commit) => string[] | Promise<string[]> | null | undefined;
type CommitAndPathToStringHandler = (commit: Commit, path: string) => string | Promise<string> | null | undefined;
interface RewritePathsBy {
    handler?: CommitAndPathToStringHandler;
}
/**
 * A rewritePathsBy.handler handler that rewrites paths by rewriting the extension.
 *
 * @example
 *
 * ```typescript
 * import { GitChangelog, rewritePathsByRewritingExtension } from '@nolebase/vitepress-plugin-git-changelog/vite'
 *
 * GitChangelog({
 *   rewritePathsBy: {
 *     // to rewrite `example.md` to `example.html`
 *     handler: rewritePathsByRewritingExtension('.md', '.html')
 *   }
 * })
 * ```
 *
 * @param from - The extension to rewrite from.
 * @param to - The extension to rewrite to.
 * @returns A handler that rewrites paths by rewriting the extension.
 */
declare function rewritePathsByRewritingExtension(from: string, to: string): (_: Commit, path: string) => string;

interface Context {
    helpers: Helpers;
}
interface GitChangelogOptions {
    /**
     * The current working directory in which to search files.
     *
     * @default process.cwd()
     */
    cwd?: string;
    /**
     * When fetching git logs, what files should be included?
     *
     * @default ['**\/*.md', '!node_modules']
     */
    include?: string[];
    /**
     * Map authors
     */
    mapAuthors?: Contributor[];
    /**
     * Your repository URL.
     * Yes, you can dynamically generate it.
     *
     * @default 'https://github.com/example/example'
     */
    repoURL?: string | CommitToStringHandler;
    /**
     * A function to get the release tag URL.
     *
     * @default (commit) => `${commit.repo_url}/releases/tag/${commit.tag}`
     */
    getReleaseTagURL?: CommitToStringHandler;
    /**
     * A function to get the release tags URL.
     *
     * @default (commit) => commit.tags?.map(tag => `${commit.repo_url}/releases/tag/${tag}`)
     */
    getReleaseTagsURL?: CommitToStringsHandler;
    /**
     * A function to get the commit URL.
     *
     * @default (commit) => `${commit.repo_url}/commit/${commit.hash}`
     */
    getCommitURL?: CommitToStringHandler;
    /**
     * Rules to rewrite paths by patterns.
     *
     * This can be quite useful when your pages are in different directories,
     * or when they are generated at runtime according to path.ts.
     *
     * Since the plugin matches the git information for each page by comparing the local path,
     * you can override the local file path to `vitepress.useData.page.value.filePath` with this option.
     *
     * @example
     *
     * ```typescript
     * GitChangelog({
     *   rewritePathsBy: {
     *     handler: (_commit, path) => {
     *       if (path) {
     *         // path: packages/characters/src/lib1.ts
     *         if (path.startsWith('packages/characters/src/') && !path.includes('index.ts'))
     *           return `${path.replace('packages/characters/src/', '').slice(0, -3)}.md`
     *       }
     *       return path
     *     },
     *   },
     * })
     * ```
     *
     * Besides that, we offer some built-in handlers to rewrite paths by patterns:
     *
     *  - `rewritePathsByRewritingExtension(from: string, to: string)`: to rewrite paths by rewriting the extension.
     *
     * @example
     *
     * ```typescript
     * import { GitChangelog, rewritePathsByRewritingExtension } from '@nolebase/vitepress-plugin-git-changelog/vite'
     *
     * GitChangelog({
     *  rewritePathsBy: {
     *   // to rewrite `example.md` to `example.html`
     *  handler: rewritePathsByRewritingExtension('.md', '.html')
     * }
     * })
     * ```
     *
     * @see rewritePathsByRewritingExtension
     */
    rewritePathsBy?: RewritePathsBy;
    /**
     * The maximum number of git logs to fetch.
     */
    maxGitLogCount?: number;
}
interface GitChangelogMarkdownSectionOptions {
    /**
     * The list of file names to exclude from the transformation
     *
     * @default ['index.md']
     */
    excludes?: string[];
    /**
     * The function to exclude the file from the transformation
     *
     * @param id - the current transforming module ID (comes from vite when transform hook is called)
     * @param context - the context object, contains several helper functions
     * @returns boolean
     * @default () => false
     */
    exclude?: (id: string, context: Context) => boolean;
    /**
     * The sections options
     */
    sections?: {
        /**
         * Whether to disable the changelog section
         */
        disableChangelog?: boolean;
        /**
         * Whether to disable the contributors section
         */
        disableContributors?: boolean;
    };
}

declare function GitChangelog(options?: GitChangelogOptions): Plugin;

declare function GitChangelogMarkdownSection(options?: GitChangelogMarkdownSectionOptions): Plugin;

export { type Contributor as Author, type Commit, type CommitToStringHandler, type CommitToStringsHandler, type Context, GitChangelog, GitChangelogMarkdownSection, type GitChangelogMarkdownSectionOptions, type RewritePathsBy, rewritePathsByRewritingExtension };
