/**
 * Unit tests for vscode-extension/src/keywordCompletionProvider.js
 */

import * as vscode from "../__mocks__/vscode.js";
import { createKeywordCompletionItems } from "../../vscode-extension/src/keywordCompletionProvider.js";
import { isStatementStart, isAfterBlockCloser } from "../../core/src/analysis/completionContext.js";

// Each document gets a unique URI so the AST cache doesn't bleed between tests.
let _seq = 0;
function makeDoc(lines) {
    const uid = ++_seq;
    return {
        lineCount: lines.length,
        lineAt: (i) => ({ text: lines[i] }),
        getText: () => lines.join("\n"),
        uri: { fsPath: `/test/kwcompletion_${uid}.txt`, toString: () => `file:///test/kwcompletion_${uid}.txt` },
        version: 1
    };
}

/** Returns label strings from completion items. */
function labels(items) {
    return items.map((i) => i.label);
}

/** Cursor is at the end of the given line index. */
function atEnd(doc, lineIndex) {
    return new vscode.Position(lineIndex, doc.lineAt(lineIndex).text.length);
}

// ---------------------------------------------------------------------------
// completionContext helpers (pure — no document needed)
// ---------------------------------------------------------------------------

describe("isStatementStart", () => {
    test("blank line → true", () => expect(isStatementStart("")).toBe(true));
    test("line with only whitespace → true", () => expect(isStatementStart("   ")).toBe(true));
    test("line with = → false", () => expect(isStatementStart("SET x = ")).toBe(false));
    test("inside parens → false", () => expect(isStatementStart("IF (")).toBe(false));
    test("SET with closed parens → false (has =)", () => expect(isStatementStart("SET x = (1 + 2) ")).toBe(false));
    test("hash prefix only → true", () => expect(isStatementStart("#")).toBe(true));
    // Expression-opening keywords
    test("IF keyword prefix → false", () => expect(isStatementStart("IF GET_TOK")).toBe(false));
    test("CASE keyword prefix → false", () => expect(isStatementStart("CASE DB_STA")).toBe(false));
    test("UNTIL keyword prefix → false", () => expect(isStatementStart("UNTIL x")).toBe(false));
    test("DO WHILE keyword prefix → false", () => expect(isStatementStart("DO WHILE x")).toBe(false));
    test("RETURN keyword prefix → false", () => expect(isStatementStart("RETURN x")).toBe(false));
    test("#IF keyword prefix → false", () => expect(isStatementStart("#IF DB_STA")).toBe(false));
});

describe("isAfterBlockCloser", () => {
    test("ENDIF in prefix → true", () => expect(isAfterBlockCloser("ENDIF")).toBe(true));
    test("ENDDO in prefix → true",  () => expect(isAfterBlockCloser("ENDDO")).toBe(true));
    test("ENDCASE → true",          () => expect(isAfterBlockCloser("ENDCASE")).toBe(true));
    test("ENDFUNCTION → true",      () => expect(isAfterBlockCloser("ENDFUNCTION")).toBe(true));
    test("lowercase enddo → true",  () => expect(isAfterBlockCloser("enddo")).toBe(true));
    test("regular line → false",    () => expect(isAfterBlockCloser("SET x")).toBe(false));
    test("empty → false",           () => expect(isAfterBlockCloser("")).toBe(false));
});

// ---------------------------------------------------------------------------
// Line-type suppression
// ---------------------------------------------------------------------------

describe("createKeywordCompletionItems — suppression", () => {
    test("comment line → empty", () => {
        const doc = makeDoc(["// some comment"]);
        expect(createKeywordCompletionItems(doc, atEnd(doc, 0))).toHaveLength(0);
    });

    test("output line >> → empty", () => {
        const doc = makeDoc([">> some output"]);
        expect(createKeywordCompletionItems(doc, atEnd(doc, 0))).toHaveLength(0);
    });

    test("line with = (expression context) → empty", () => {
        const doc = makeDoc(["#HASH-OFF", "SET x = "]);
        expect(createKeywordCompletionItems(doc, atEnd(doc, 1))).toHaveLength(0);
    });

    test("line inside parens → empty", () => {
        const doc = makeDoc(["#HASH-OFF", "IF ("]);
        expect(createKeywordCompletionItems(doc, atEnd(doc, 1))).toHaveLength(0);
    });

    test("ENDIF on line already → empty", () => {
        const doc = makeDoc(["#HASH-OFF", "ENDIF"]);
        expect(createKeywordCompletionItems(doc, atEnd(doc, 1))).toHaveLength(0);
    });

    test("ENDDO on line already → empty", () => {
        const doc = makeDoc(["#HASH-OFF", "ENDDO"]);
        expect(createKeywordCompletionItems(doc, atEnd(doc, 1))).toHaveLength(0);
    });
});

// ---------------------------------------------------------------------------
// Top-level (no enclosing block)
// ---------------------------------------------------------------------------

describe("createKeywordCompletionItems — top level", () => {
    test("blank line at top level → no block-closing keywords", () => {
        const doc = makeDoc(["#HASH-OFF", ""]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 1)));
        expect(result).not.toContain("ELSE");
        expect(result).not.toContain("ENDIF");
        expect(result).not.toContain("ENDDO");
        expect(result).not.toContain("ENDCASE");
        expect(result).not.toContain("ENDFUNCTION");
    });

    test("blank line at top level → includes block openers", () => {
        const doc = makeDoc(["#HASH-OFF", ""]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 1)));
        expect(result).toContain("IF");
        expect(result).toContain("DO");
        expect(result).toContain("CASE");
        expect(result).toContain("FUNCTION");
    });

    test("blank line at top level → includes type keywords and directives", () => {
        const doc = makeDoc(["#HASH-OFF", ""]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 1)));
        expect(result).toContain("STRING");
        expect(result).toContain("SHORT");
        expect(result).toContain("SET");
        expect(result).toContain("OPTION");
        expect(result).toContain("EXIT");
        expect(result).toContain("END-OF-CODE");
    });

    test("blank line at top level → does not contain ELSEIF", () => {
        const doc = makeDoc(["#HASH-OFF", ""]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 1)));
        expect(result).not.toContain("ELSEIF");
    });
});

// ---------------------------------------------------------------------------
// Inside IF block
// ---------------------------------------------------------------------------

describe("createKeywordCompletionItems — inside IF", () => {
    test("offers ELSE and ENDIF; does not offer ENDDO or ELSEIF", () => {
        const doc = makeDoc([
            "#HASH-OFF",
            "IF (x = 1)",
            "  SET y = 2",
            "  ",          // cursor line
            "ENDIF"
        ]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 3)));
        expect(result).toContain("ELSE");
        expect(result).toContain("ENDIF");
        expect(result).not.toContain("ELSEIF");
        expect(result).not.toContain("ENDDO");
        expect(result).not.toContain("ENDCASE");
    });

    test("prefix 'EN' → ENDIF matches; ENDDO does not", () => {
        const doc = makeDoc([
            "#HASH-OFF",
            "IF (x = 1)",
            "  EN"
        ]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 2)));
        expect(result).toContain("ENDIF");
        expect(result).not.toContain("ENDDO");
    });
});

// ---------------------------------------------------------------------------
// Inside DO block
// ---------------------------------------------------------------------------

describe("createKeywordCompletionItems — inside DO", () => {
    test("offers BREAK, CONTINUE, UNTIL, ENDDO; does not offer ENDIF", () => {
        const doc = makeDoc([
            "#HASH-OFF",
            "DO i = 1 TO 10",
            "  ",           // cursor line
            "ENDDO"
        ]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 2)));
        expect(result).toContain("BREAK");
        expect(result).toContain("CONTINUE");
        expect(result).toContain("UNTIL");
        expect(result).toContain("ENDDO");
        expect(result).not.toContain("ENDIF");
        expect(result).not.toContain("ELSE");
    });

    test("prefix 'EN' inside DO → ENDDO matches; ENDIF does not", () => {
        const doc = makeDoc([
            "#HASH-OFF",
            "DO i = 1 TO 10",
            "  EN"
        ]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 2)));
        expect(result).toContain("ENDDO");
        expect(result).not.toContain("ENDIF");
    });
});

// ---------------------------------------------------------------------------
// Inside CASE block
// ---------------------------------------------------------------------------

describe("createKeywordCompletionItems — inside CASE", () => {
    test("offers VALUE, OTHERWISE, ENDCASE; does not offer ENDIF or ENDDO", () => {
        const doc = makeDoc([
            "#HASH-OFF",
            "CASE x",
            "  ",           // cursor line
            "ENDCASE"
        ]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 2)));
        expect(result).toContain("VALUE");
        expect(result).toContain("OTHERWISE");
        expect(result).toContain("ENDCASE");
        expect(result).not.toContain("ENDIF");
        expect(result).not.toContain("ENDDO");
    });
});

// ---------------------------------------------------------------------------
// Inside FUNCTION block
// ---------------------------------------------------------------------------

describe("createKeywordCompletionItems — inside FUNCTION", () => {
    test("offers RETURN and ENDFUNCTION; does not offer ENDIF or ENDDO", () => {
        const doc = makeDoc([
            "#HASH-OFF",
            "FUNCTION Calc() : SHORT",
            "  ",           // cursor line
            "ENDFUNCTION"
        ]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 2)));
        expect(result).toContain("RETURN");
        expect(result).toContain("ENDFUNCTION");
        expect(result).not.toContain("ENDIF");
        expect(result).not.toContain("ENDDO");
    });
});

// ---------------------------------------------------------------------------
// Nested blocks — innermost wins
// ---------------------------------------------------------------------------

describe("createKeywordCompletionItems — nested (innermost block wins)", () => {
    test("cursor inside DO inside IF → DO closers offered, not IF closers", () => {
        const doc = makeDoc([
            "#HASH-OFF",
            "IF (x = 1)",
            "  DO i = 1 TO 5",
            "    ",          // cursor line
            "  ENDDO",
            "ENDIF"
        ]);
        const result = labels(createKeywordCompletionItems(doc, atEnd(doc, 3)));
        expect(result).toContain("ENDDO");
        expect(result).toContain("BREAK");
        expect(result).not.toContain("ENDIF");
        expect(result).not.toContain("ELSE");
    });
});
