import { jest } from '@jest/globals';
import * as vscode from "../__mocks__/vscode.js";
import { createMockDocument } from "../helpers/mockDocument.js";
import { provideAnnotationCompletions, buildCompletionItems } from "../../vscode-extension/src/annotationCompletionProvider.js";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function docAt(line, charOffset) {
    const doc = createMockDocument([line]);
    const position = new vscode.Position(0, charOffset !== undefined ? charOffset : line.length);
    return { doc, position };
}

function mockConfig(enabled) {
    jest.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
        get: (key, defaultVal) => key === "code-completion" ? enabled : defaultVal
    });
}

afterEach(() => jest.restoreAllMocks());

// ---------------------------------------------------------------------------
// Context suppression
// ---------------------------------------------------------------------------

describe("annotationCompletionProvider — context suppression", () => {
    test("returns null on a non-comment line", () => {
        mockConfig(true);
        const { doc, position } = docAt("SET zX = @mode:strict");
        expect(provideAnnotationCompletions(doc, position)).toBeNull();
    });

    test("returns null on an output line", () => {
        mockConfig(true);
        const { doc, position } = docAt(">> @mode:strict");
        expect(provideAnnotationCompletions(doc, position)).toBeNull();
    });

    test("returns null when acurity.code-completion is false", () => {
        mockConfig(false);
        const { doc, position } = docAt("// @");
        expect(provideAnnotationCompletions(doc, position)).toBeNull();
    });

    test("returns null when line has no @ symbol before cursor", () => {
        mockConfig(true);
        const { doc, position } = docAt("// just a comment");
        expect(provideAnnotationCompletions(doc, position)).toBeNull();
    });
});

// ---------------------------------------------------------------------------
// Item availability
// ---------------------------------------------------------------------------

describe("annotationCompletionProvider — item availability", () => {
    test("returns all four annotation items when @ is typed in a comment", () => {
        mockConfig(true);
        const { doc, position } = docAt("// @");
        const items = provideAnnotationCompletions(doc, position);
        expect(items).not.toBeNull();
        const labels = items.map((i) => i.label);
        expect(labels).toContain("@mode:strict");
        expect(labels).toContain("@mode:legacy");
        expect(labels).toContain("@mode:default");
        expect(labels).toContain("@rule-ignore");
    });

    test("filters to @mode items when user has typed @m", () => {
        mockConfig(true);
        const { doc, position } = docAt("// @m");
        const items = provideAnnotationCompletions(doc, position);
        const labels = items.map((i) => i.label);
        expect(labels).toContain("@mode:strict");
        expect(labels).toContain("@mode:legacy");
        expect(labels).toContain("@mode:default");
        expect(labels).not.toContain("@rule-ignore");
    });

    test("filters to @rule-ignore when user has typed @r", () => {
        mockConfig(true);
        const { doc, position } = docAt("// @r");
        const items = provideAnnotationCompletions(doc, position);
        const labels = items.map((i) => i.label);
        expect(labels).toContain("@rule-ignore");
        expect(labels).not.toContain("@mode:strict");
    });

    test("returns empty array (not null) when typed prefix matches nothing", () => {
        mockConfig(true);
        const { doc, position } = docAt("// @z");
        const items = provideAnnotationCompletions(doc, position);
        expect(items).not.toBeNull();
        expect(items).toHaveLength(0);
    });
});

// ---------------------------------------------------------------------------
// Item properties
// ---------------------------------------------------------------------------

describe("annotationCompletionProvider — item properties", () => {
    test("@rule-ignore insert text is a SnippetString", () => {
        const items = buildCompletionItems();
        const ruleIgnore = items.find((i) => i.label === "@rule-ignore");
        expect(ruleIgnore).toBeDefined();
        expect(ruleIgnore.insertText).toBeInstanceOf(vscode.SnippetString);
    });

    test("@rule-ignore snippet includes REASON placeholder", () => {
        const items = buildCompletionItems();
        const ruleIgnore = items.find((i) => i.label === "@rule-ignore");
        expect(ruleIgnore.insertText.value).toContain("REASON:");
    });

    test("@mode:strict insert text is a plain string", () => {
        const items = buildCompletionItems();
        const mode = items.find((i) => i.label === "@mode:strict");
        expect(typeof mode.insertText).toBe("string");
        expect(mode.insertText).toBe("@mode:strict");
    });

    test("all items have detail text", () => {
        const items = buildCompletionItems();
        for (const item of items) {
            expect(typeof item.detail).toBe("string");
            expect(item.detail.length).toBeGreaterThan(0);
        }
    });

    test("all items are CompletionItemKind.Keyword", () => {
        const items = buildCompletionItems();
        for (const item of items) {
            expect(item.kind).toBe(vscode.CompletionItemKind.Keyword);
        }
    });

    test("sortText orders @mode items before @rule-ignore", () => {
        const items = buildCompletionItems();
        const modeSortText = items.find((i) => i.label === "@mode:strict").sortText;
        const ruleSortText = items.find((i) => i.label === "@rule-ignore").sortText;
        expect(modeSortText < ruleSortText).toBe(true);
    });
});
