// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { jest, type jest as JestTypes } from "@jest/globals";
import {
    ICompletionDispatcher,
    ISearchMenu,
    PartialCompletionSession,
} from "../../src/renderer/src/partialCompletionSession.js";
import { SearchMenuPosition } from "../../src/preload/electronTypes.js";
import { CompletionGroup } from "@typeagent/agent-sdk";
import { CommandCompletionResult } from "agent-dispatcher";
import { SearchMenuBase } from "../../src/renderer/src/searchMenuBase.js";

export { PartialCompletionSession };
export type { ICompletionDispatcher, ISearchMenu };
export type { CompletionGroup };
export type { CommandCompletionResult };
export type { SearchMenuPosition };

type Mocked<T extends (...args: any[]) => any> = T &
    JestTypes.MockedFunction<T>;

// Real trie-backed ISearchMenu backed by SearchMenuBase.
// Every method is a jest.fn() wrapping the real implementation so tests can
// assert on call counts and arguments.
export class TestSearchMenu extends SearchMenuBase {
    override setChoices: Mocked<SearchMenuBase["setChoices"]> = jest.fn(
        (...args: Parameters<SearchMenuBase["setChoices"]>) =>
            super.setChoices(...args),
    ) as any;

    override updatePrefix: Mocked<ISearchMenu["updatePrefix"]> = jest.fn(
        (prefix: string, position: SearchMenuPosition): boolean =>
            super.updatePrefix(prefix, position),
    ) as any;

    override hasExactMatch: Mocked<ISearchMenu["hasExactMatch"]> = jest.fn(
        (text: string): boolean => super.hasExactMatch(text),
    ) as any;

    override hide: Mocked<ISearchMenu["hide"]> = jest.fn(() =>
        super.hide(),
    ) as any;

    override isActive: Mocked<ISearchMenu["isActive"]> = jest.fn(() =>
        super.isActive(),
    ) as any;
}

export function makeMenu(): TestSearchMenu {
    return new TestSearchMenu();
}

export type MockDispatcher = {
    getCommandCompletion: jest.MockedFunction<
        ICompletionDispatcher["getCommandCompletion"]
    >;
};

export function makeDispatcher(
    result: CommandCompletionResult = {
        startIndex: 0,
        completions: [],
        separatorMode: undefined,
        closedSet: true,
        directionSensitive: false,
        openWildcard: false,
    },
): MockDispatcher {
    return {
        getCommandCompletion: jest
            .fn<ICompletionDispatcher["getCommandCompletion"]>()
            .mockResolvedValue(result),
    };
}

export const anyPosition: SearchMenuPosition = { left: 0, bottom: 0 };
export const getPos = (_prefix: string) => anyPosition;

export function makeCompletionResult(
    completions: string[],
    startIndex: number = 0,
    opts: Partial<CommandCompletionResult> = {},
): CommandCompletionResult {
    const group: CompletionGroup = { name: "test", completions };
    return {
        startIndex,
        completions: [group],
        closedSet: false,
        directionSensitive: false,
        openWildcard: false,
        ...opts,
    };
}
