import { describe, expect, test } from "vitest";
import { TocModel } from "../src/tocModel";
import type { FileKey, FoldKey, TocBlockModel } from "../src/types";
import { createTocModelPluginMock } from "./mocks/pluginClassMocks";

type CreateModelOptions = {
    sourceFilePath?: FileKey;
    initialFoldState?: Partial<Record<FoldKey, boolean>>;
    settingsOverrides?: {
        tocTitle?: string;
        tocTitleLevel?: 1 | 2 | 3 | 4 | 5 | 6;
        tocTitleCentered?: boolean;
        excludedChars?: string[];
    };
};

function createModel(
    source: string,
    { sourceFilePath = "notes/my-note.md", initialFoldState = {}, settingsOverrides = {} }: CreateModelOptions = {}
): { model: TocBlockModel; pruneSpy: ReturnType<typeof createTocModelPluginMock>["pruneSpy"]; } {
    const { app, pruneSpy, settings, uiStateManager } = createTocModelPluginMock(settingsOverrides, {
        tocFoldState: initialFoldState
    });

    const model = new TocModel(
        uiStateManager,
        app,
        settings,
        { localSettings: source, sourceFilePath },
        (key) => uiStateManager.getTocFoldState(key)
    )
        .model;

    return { model, pruneSpy };
}

describe("TocModel fold state", () => {
    test("assigns foldKey and initialCollapsed=false when no persisted state exists", () => {
        // Arrange
        const source = `- Heading 1\n    - Heading 2`;

        // Act
        const { model, pruneSpy } = createModel(source);
        const [ parent ] = model.items;
        const [ child ] = parent.children;

        // Assert
        expect(parent.foldKey).toBe("notes/my-note.md::1");
        expect(parent.initialCollapsed).toBe(false);
        expect(child.foldKey).toBeNull();
        expect(pruneSpy).toHaveBeenCalledTimes(1);
        expect(pruneSpy).toHaveBeenCalledWith(
            "notes/my-note.md",
            expect.objectContaining({ activeModernFoldKeys: expect.any(Set) })
        );
        expect([ ...(pruneSpy.mock.calls[0]?.[1].activeModernFoldKeys ?? []) ]).toEqual([ "notes/my-note.md::1" ]);
    });

    test("restores collapsed state from persisted modern fold key", () => {
        // Arrange
        const source = `- Heading 1\n    - Heading 2`;

        // Act
        const { model } = createModel(source, { initialFoldState: { "notes/my-note.md::1": true } });

        // Assert
        expect(model.items[0].initialCollapsed).toBe(true);
    });

    test("does not leak fold state across different file paths", () => {
        // Arrange
        const source = `- Heading 1\n    - Heading 2`;

        // Act
        const { model } = createModel(source, {
            sourceFilePath: "notes/file-b.md",
            initialFoldState: { "notes/file-a.md::1": true }
        });

        // Assert
        expect(model.items[0].initialCollapsed).toBe(false);
    });

    test("tracks foldParentIndex across nested items", () => {
        // Arrange
        const source = `- Heading 1\n    - Heading 2\n        - Heading 3`;

        // Act
        const { model, pruneSpy } = createModel(source, { sourceFilePath: "note.md" });
        const [ parent ] = model.items;
        const [ child ] = parent.children;

        // Assert
        expect(parent.foldKey).toBe("note.md::1");
        expect(child.foldKey).toBe("note.md::2");
        expect([ ...(pruneSpy.mock.calls[0]?.[1].activeModernFoldKeys ?? []) ]).toEqual([ "note.md::1", "note.md::2" ]);
    });

    test("resolves the rendered title from local settings while building the model", () => {
        // Arrange
        const source = `---\ntitle:\n  name: Local TOC\n  level: 2\n  center: true\n---\n\n- Heading 1`;

        // Act
        const { model } = createModel(source);

        // Assert
        expect(model.title).toEqual({ text: "Local TOC", level: 2, centered: true, usesGlobalCentering: false });
    });
});
