import { describe, expect, it, vi } from "vitest";
import { createServiceContext } from "@vrtmrz/livesync-commonlib/context";
import { eventHub, EVENT_REQUEST_OPEN_P2P } from "@/common/events";

vi.mock("@/features/P2PSync/P2PReplicator/P2PServerStatusPaneView", () => ({
    P2PServerStatusPaneView: class {
        getViewType() {
            return "p2p-status";
        }
    },
    VIEW_TYPE_P2P_SERVER_STATUS: "p2p-status",
}));

import { useP2PReplicatorUI } from "./useP2PReplicatorUI";

const noopOpenInteractiveReplication = () => Promise.resolve(false);

describe("useP2PReplicatorUI commands", () => {
    it("waits for settings to load before deciding whether to show the P2P ribbon", async () => {
        let initialise: (() => Promise<unknown>) | undefined;
        let settingLoaded: (() => Promise<unknown>) | undefined;
        let settings: Record<string, unknown> | undefined;
        const currentSettings = vi.fn(() => settings);
        const host = {
            services: {
                context: createServiceContext(),
                API: {
                    showWindow: vi.fn(async () => undefined),
                    registerWindow: vi.fn(),
                    addCommand: vi.fn(),
                    addRibbonIcon: vi.fn(),
                },
                appLifecycle: {
                    onInitialise: {
                        addHandler: vi.fn((handler) => {
                            initialise = handler;
                        }),
                    },
                    onSettingLoaded: {
                        addHandler: vi.fn((handler) => {
                            settingLoaded = handler;
                        }),
                    },
                    onLayoutReady: { addHandler: vi.fn() },
                },
                setting: {
                    currentSettings,
                    onSettingSaved: { addHandler: vi.fn() },
                },
                replicator: { runFiniteReplicationActivity: vi.fn() },
            },
        } as any;

        useP2PReplicatorUI(host, {} as any, {} as any, noopOpenInteractiveReplication);

        await expect(initialise?.()).resolves.toBe(true);
        expect(currentSettings).not.toHaveBeenCalled();
        settings = {
            remoteType: "COUCHDB",
            remoteConfigurations: {},
        };
        await expect(settingLoaded?.()).resolves.toBe(true);
        expect(currentSettings).toHaveBeenCalledOnce();
    });

    it("exposes a direct modal P2P replication command as finite replication activity", async () => {
        const commands: Array<{ id: string; checkCallback?: (isChecking: boolean) => unknown }> = [];
        let initialise: (() => Promise<unknown>) | undefined;
        const openInteractiveReplication = vi.fn(async () => true);
        const runFiniteReplicationActivity = vi.fn(async (task: () => unknown) => await task());
        const host = {
            services: {
                context: createServiceContext(),
                API: {
                    showWindow: vi.fn(async () => undefined),
                    registerWindow: vi.fn(),
                    addCommand: vi.fn((command) => commands.push(command)),
                    addRibbonIcon: vi.fn(),
                    getPlatform: vi.fn(() => "obsidian"),
                },
                appLifecycle: {
                    onInitialise: {
                        addHandler: vi.fn((handler) => {
                            initialise = handler;
                        }),
                    },
                    onSettingLoaded: { addHandler: vi.fn() },
                    onLayoutReady: { addHandler: vi.fn() },
                },
                setting: {
                    currentSettings: vi.fn(() => ({
                        remoteType: "COUCHDB",
                        P2P_Enabled: true,
                    })),
                },
                replicator: { runFiniteReplicationActivity },
            },
        } as any;
        const p2p = {
            transportLifecycle: { isConnected: true },
        } as any;

        useP2PReplicatorUI(host, {} as any, p2p, openInteractiveReplication);
        await initialise?.();
        commands.find((command) => command.id === "replicate-now-by-p2p")?.checkCallback?.(false);

        await vi.waitFor(() => expect(openInteractiveReplication).toHaveBeenCalledWith(true));
        expect(runFiniteReplicationActivity).toHaveBeenCalledWith(expect.any(Function), {
            label: "replication",
        });
    });

    it("retains only the current P2P status command and routes existing open requests to it", async () => {
        const commands: Array<{
            id: string;
            callback?: () => void;
            checkCallback?: (checking: boolean) => boolean | void;
        }> = [];
        let initialise: (() => Promise<unknown>) | undefined;
        const showWindow = vi.fn(async () => undefined);
        const showWindowOnRight = vi.fn(async () => undefined);
        const host = {
            services: {
                context: createServiceContext(),
                API: {
                    showWindow,
                    showWindowOnRight,
                    registerWindow: vi.fn(),
                    addCommand: vi.fn((command) => commands.push(command)),
                    addRibbonIcon: vi.fn(),
                    getPlatform: vi.fn(() => "desktop"),
                },
                appLifecycle: {
                    onInitialise: {
                        addHandler: vi.fn((handler) => {
                            initialise = handler;
                        }),
                    },
                    onSettingLoaded: { addHandler: vi.fn() },
                    onLayoutReady: { addHandler: vi.fn() },
                },
                setting: {
                    currentSettings: vi.fn(() => ({
                        remoteType: "COUCHDB",
                        remoteConfigurations: {},
                    })),
                },
                replicator: { runFiniteReplicationActivity: vi.fn() },
            },
        } as any;
        const p2p = {} as any;

        useP2PReplicatorUI(host, {} as any, p2p, noopOpenInteractiveReplication);
        await initialise?.();

        expect(commands.map((command) => command.id)).not.toContain("open-p2p-replicator");
        expect(commands.map((command) => command.id)).toContain("open-p2p-server-status");
        expect(commands.find((command) => command.id === "open-p2p-server-status")?.checkCallback?.(true)).toBe(false);

        eventHub.emitEvent(EVENT_REQUEST_OPEN_P2P);
        await vi.waitFor(() => expect(showWindowOnRight).toHaveBeenCalledWith("p2p-status"));
        expect(showWindow).not.toHaveBeenCalledWith("p2p");
    });

    it("shows P2P commands only when a P2P configuration exists and their runtime prerequisites are met", async () => {
        const commands: Array<{
            id: string;
            checkCallback?: (checking: boolean) => boolean | void;
        }> = [];
        let initialise: (() => Promise<unknown>) | undefined;
        let settings: Record<string, unknown> = {
            remoteType: "COUCHDB",
            remoteConfigurations: {},
        };
        const runFiniteReplicationActivity = vi.fn(async (task: () => unknown) => await task());
        const host = {
            services: {
                context: createServiceContext(),
                API: {
                    showWindow: vi.fn(async () => undefined),
                    showWindowOnRight: vi.fn(async () => undefined),
                    registerWindow: vi.fn(),
                    addCommand: vi.fn((command) => commands.push(command)),
                    addRibbonIcon: vi.fn(),
                },
                appLifecycle: {
                    onInitialise: {
                        addHandler: vi.fn((handler) => {
                            initialise = handler;
                        }),
                    },
                    onSettingLoaded: { addHandler: vi.fn() },
                    onLayoutReady: { addHandler: vi.fn() },
                },
                setting: {
                    currentSettings: vi.fn(() => settings),
                    onSettingSaved: { addHandler: vi.fn() },
                },
                replicator: { runFiniteReplicationActivity },
            },
        } as any;
        const synchroniseConfiguredTargets = vi.fn(async () => ({ status: "completed" }));
        const p2p = {
            transportLifecycle: { isConnected: true },
            targetedTransfer: { synchroniseConfiguredTargets },
        } as any;

        useP2PReplicatorUI(host, {} as any, p2p, noopOpenInteractiveReplication);
        await initialise?.();

        for (const commandId of [
            "open-p2p-server-status",
            "replicate-now-by-p2p-default-peer",
            "replicate-now-by-p2p",
            "p2p-sync-targets",
        ]) {
            expect(commands.find(({ id }) => id === commandId)?.checkCallback?.(true)).toBe(false);
        }

        settings = {
            ...settings,
            remoteConfigurations: {
                peer: {
                    id: "peer",
                    name: "Peer",
                    uri: "sls+p2p://room?passphrase=secret",
                    isEncrypted: false,
                },
            },
        };
        for (const commandId of [
            "open-p2p-server-status",
            "replicate-now-by-p2p-default-peer",
            "replicate-now-by-p2p",
            "p2p-sync-targets",
        ]) {
            expect(commands.find(({ id }) => id === commandId)?.checkCallback?.(true)).toBe(true);
        }

        commands.find(({ id }) => id === "p2p-sync-targets")?.checkCallback?.(false);
        await vi.waitFor(() => expect(synchroniseConfiguredTargets).toHaveBeenCalledOnce());
        expect(runFiniteReplicationActivity).toHaveBeenCalledWith(expect.any(Function), {
            label: "replication",
        });
    });

    it("does not open the P2P status pane automatically when the workspace becomes ready", async () => {
        let layoutReady: (() => Promise<unknown>) | undefined;
        const showWindow = vi.fn(async () => undefined);
        const showWindowOnRight = vi.fn(async () => undefined);
        const host = {
            services: {
                context: createServiceContext(),
                API: {
                    showWindow,
                    showWindowOnRight,
                    registerWindow: vi.fn(),
                    addCommand: vi.fn(),
                    addRibbonIcon: vi.fn(),
                    getPlatform: vi.fn(() => "obsidian"),
                },
                appLifecycle: {
                    onInitialise: { addHandler: vi.fn() },
                    onSettingLoaded: { addHandler: vi.fn() },
                    onLayoutReady: {
                        addHandler: vi.fn((handler) => {
                            layoutReady = handler;
                        }),
                    },
                },
                setting: {
                    currentSettings: vi.fn(() => ({
                        remoteType: "COUCHDB",
                        remoteConfigurations: {},
                    })),
                },
                replicator: { runFiniteReplicationActivity: vi.fn() },
            },
        } as any;

        useP2PReplicatorUI(host, {} as any, {} as any, noopOpenInteractiveReplication);
        await layoutReady?.();

        expect(showWindow).not.toHaveBeenCalled();
        expect(showWindowOnRight).not.toHaveBeenCalled();
    });

    it("shows the ribbon only whilst a P2P configuration exists", async () => {
        let initialise: (() => Promise<unknown>) | undefined;
        let settingLoaded: (() => Promise<unknown>) | undefined;
        let onSettingSaved: ((settings: unknown) => Promise<unknown>) | undefined;
        let currentSettings: any = {
            remoteType: "COUCHDB",
            remoteConfigurations: {},
            P2P_Enabled: false,
            P2P_roomID: "",
            P2P_passphrase: "",
        };
        const ribbon = { addClass: vi.fn(), remove: vi.fn() };
        const addRibbonIcon = vi.fn(() => ribbon);
        const host = {
            services: {
                context: createServiceContext(),
                API: {
                    showWindow: vi.fn(async () => undefined),
                    showWindowOnRight: vi.fn(async () => undefined),
                    registerWindow: vi.fn(),
                    addCommand: vi.fn(),
                    addRibbonIcon,
                    getPlatform: vi.fn(() => "desktop"),
                },
                appLifecycle: {
                    onInitialise: {
                        addHandler: vi.fn((handler) => {
                            initialise = handler;
                        }),
                    },
                    onSettingLoaded: {
                        addHandler: vi.fn((handler) => {
                            settingLoaded = handler;
                        }),
                    },
                    onLayoutReady: { addHandler: vi.fn() },
                },
                setting: {
                    currentSettings: vi.fn(() => currentSettings),
                    onSettingSaved: {
                        addHandler: vi.fn((handler) => {
                            onSettingSaved = handler;
                        }),
                    },
                },
                replicator: { runFiniteReplicationActivity: vi.fn() },
            },
        } as any;

        useP2PReplicatorUI(host, {} as any, {} as any, noopOpenInteractiveReplication);
        await initialise?.();
        await settingLoaded?.();
        expect(addRibbonIcon).not.toHaveBeenCalled();

        currentSettings = {
            ...currentSettings,
            remoteConfigurations: {
                peer: {
                    id: "peer",
                    name: "Peer",
                    uri: "sls+p2p://room?passphrase=secret",
                    isEncrypted: false,
                },
            },
        };
        await onSettingSaved?.(currentSettings);
        expect(addRibbonIcon).toHaveBeenCalledOnce();

        await onSettingSaved?.(currentSettings);
        expect(addRibbonIcon).toHaveBeenCalledOnce();

        currentSettings = {
            ...currentSettings,
            remoteConfigurations: {},
        };
        await onSettingSaved?.(currentSettings);
        expect(ribbon.remove).toHaveBeenCalledOnce();
    });

    it("compatibility: migrates a restored P2P leaf to the current status view without opening another leaf", async () => {
        let layoutReady: (() => Promise<unknown>) | undefined;
        const legacyLeaf = {
            setViewState: vi.fn(async () => undefined),
        };
        const workspace = {
            getLeavesOfType: vi.fn((type: string) => (type === "p2p-replicator" ? [legacyLeaf] : [])),
        };
        const context = createServiceContext() as ReturnType<typeof createServiceContext> & {
            app: { workspace: typeof workspace };
        };
        context.app = { workspace };
        const showWindow = vi.fn(async () => undefined);
        const showWindowOnRight = vi.fn(async () => undefined);
        const host = {
            services: {
                context,
                API: {
                    showWindow,
                    showWindowOnRight,
                    registerWindow: vi.fn(),
                    addCommand: vi.fn(),
                    addRibbonIcon: vi.fn(),
                    getPlatform: vi.fn(() => "desktop"),
                },
                appLifecycle: {
                    onInitialise: { addHandler: vi.fn() },
                    onSettingLoaded: { addHandler: vi.fn() },
                    onLayoutReady: {
                        addHandler: vi.fn((handler) => {
                            layoutReady = handler;
                        }),
                    },
                },
                setting: {
                    currentSettings: vi.fn(() => ({
                        remoteType: "COUCHDB",
                        remoteConfigurations: {},
                    })),
                },
                replicator: { runFiniteReplicationActivity: vi.fn() },
            },
        } as any;

        useP2PReplicatorUI(host, {} as any, {} as any, noopOpenInteractiveReplication);
        await layoutReady?.();

        expect(legacyLeaf.setViewState).toHaveBeenCalledWith({
            type: "p2p-status",
            active: false,
        });
        expect(showWindow).not.toHaveBeenCalled();
        expect(showWindowOnRight).not.toHaveBeenCalled();
    });
});
