import { beforeEach, describe, expect, it, vi } from "vitest";
import {
    DEFAULT_SETTINGS,
    REMOTE_COUCHDB,
    REMOTE_P2P,
    type ObsidianLiveSyncSettings,
} from "@vrtmrz/livesync-commonlib/compat/common/types";
import { SettingService } from "@vrtmrz/livesync-commonlib/compat/services/base/SettingService";
import { ServiceContext } from "@vrtmrz/livesync-commonlib/context";
import { createNewVaultSettings } from "@vrtmrz/livesync-commonlib/settings";
import type {
    P2PConnectionProbeAdmission,
    P2PConnectionProbeAdmissionResult,
    P2PConnectionProbeSettings,
} from "@vrtmrz/livesync-commonlib/p2p";

vi.mock("./SetupWizard/dialogs/Intro.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/SelectMethodNewUser.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/SelectMethodExisting.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/ScanQRCode.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/UseSetupURI.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/OutroNewUser.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/OutroExistingUser.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/OutroAskUserMode.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/ApplySettingsInitialisation.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/SetupRemote.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/SetupRemoteCouchDB.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/SetupRemoteBucket.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/SetupRemoteP2P.svelte", () => ({ default: {} }));
vi.mock("./SetupWizard/dialogs/SetupRemoteE2EE.svelte", () => ({ default: {} }));

vi.mock("@vrtmrz/livesync-commonlib/compat/API/processSetting", () => ({
    decodeSettingsFromQRCodeData: vi.fn(),
}));

import { decodeSettingsFromQRCodeData } from "@vrtmrz/livesync-commonlib/compat/API/processSetting";
import { SetupManager, UserMode } from "./SetupManager";

class TestSettingService extends SettingService<ServiceContext> {
    protected setItem(_key: string, _value: string): void {}
    protected getItem(_key: string): string {
        return "";
    }
    protected deleteItem(_key: string): void {}
    protected saveData(_setting: ObsidianLiveSyncSettings): Promise<void> {
        return Promise.resolve();
    }
    protected loadData(): Promise<ObsidianLiveSyncSettings | undefined> {
        return Promise.resolve(undefined);
    }
}

function createLegacyRemoteSetting(): ObsidianLiveSyncSettings {
    return {
        ...DEFAULT_SETTINGS,
        remoteConfigurations: {},
        activeConfigurationId: "",
        remoteType: REMOTE_COUCHDB,
        couchDB_URI: "http://localhost:5984",
        couchDB_USER: "user",
        couchDB_PASSWORD: "password",
        couchDB_DBNAME: "vault",
    };
}

function createSetupManager() {
    const setting = new TestSettingService(new ServiceContext(), {
        APIService: {
            getSystemVaultName: vi.fn(() => "vault"),
            getAppID: vi.fn(() => "app"),
            confirm: {
                askString: vi.fn(() => Promise.resolve("")),
            },
            addLog: vi.fn(),
            addCommand: vi.fn(),
            registerWindow: vi.fn(),
            addRibbonIcon: vi.fn(),
            registerProtocolHandler: vi.fn(),
        } as any,
    });
    setting.settings = {
        ...DEFAULT_SETTINGS,
        remoteConfigurations: {},
        activeConfigurationId: "",
    };
    vi.spyOn(setting, "saveSettingData").mockResolvedValue();

    const dialogManager = {
        openWithExplicitCancel: vi.fn(),
        open: vi.fn(),
    };
    const services = {
        API: {
            addLog: vi.fn(),
            addCommand: vi.fn(),
            registerWindow: vi.fn(),
            addRibbonIcon: vi.fn(),
            registerProtocolHandler: vi.fn(),
        },
        UI: {
            dialogManager,
        },
        setting,
    } as any;
    const core: any = {
        _services: services,
        rebuilder: {
            scheduleRebuild: vi.fn(async (prepareBeforeRestart?: () => Promise<void>) => {
                await prepareBeforeRestart?.();
                return true;
            }),
            scheduleFetch: vi.fn(async (prepareBeforeRestart?: () => Promise<void>) => {
                await prepareBeforeRestart?.();
                return true;
            }),
        },
    };
    Object.defineProperty(core, "services", {
        get() {
            return services;
        },
    });
    Object.defineProperty(core, "settings", {
        get() {
            return setting.settings;
        },
        set(value: ObsidianLiveSyncSettings) {
            setting.settings = value;
        },
    });

    const p2pSetupConnectionProbe: P2PConnectionProbeAdmission = {
        async run<T>(
            _settings: P2PConnectionProbeSettings,
            runOwnedTrial: () => Promise<T>
        ): Promise<P2PConnectionProbeAdmissionResult<T>> {
            return { status: "trial", result: await runOwnedTrial() };
        },
    };
    const manager = new SetupManager(core);
    manager.registerP2PSetupConnectionProbe(p2pSetupConnectionProbe);

    return {
        manager,
        setting,
        dialogManager,
        core,
        p2pSetupConnectionProbe,
    };
}

describe("SetupManager", () => {
    beforeEach(() => {
        vi.clearAllMocks();
        vi.restoreAllMocks();
    });

    it("opens P2P Setup with the registered owner admission", async () => {
        const { manager, setting, dialogManager, p2pSetupConnectionProbe } = createSetupManager();
        const settings = setting.currentSettings();
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce("cancelled");

        await expect(manager.openP2PSetup(settings)).resolves.toBe("cancelled");

        expect(dialogManager.openWithExplicitCancel).toHaveBeenCalledWith(
            expect.anything(),
            expect.objectContaining({ settings, connectionProbe: p2pSetupConnectionProbe })
        );
    });

    it("starts manual new-user setup from the recommended new-Vault settings", async () => {
        const { manager, dialogManager } = createSetupManager();
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce("configure-manually");
        const configureManually = vi.spyOn(manager, "onConfigureManually").mockResolvedValue(true);

        await manager.onOnboard(UserMode.NewUser);

        expect(configureManually).toHaveBeenCalledWith(createNewVaultSettings(), UserMode.NewUser);
    });

    it("compatibility: normalises imported flat remote settings from a Setup URI before applying", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce(createLegacyRemoteSetting())
            .mockResolvedValueOnce("compatible-existing-user");

        const result = await manager.onUseSetupURI(UserMode.Unknown, "mock-config://settings");

        expect(result).toBe(true);
        expect(setting.currentSettings().remoteConfigurations["legacy-couchdb"]?.uri).toContain(
            "sls+http://user:password@localhost:5984"
        );
        expect(setting.currentSettings().activeConfigurationId).toBe("legacy-couchdb");
    });

    it("compatibility: normalises imported flat remote settings from QR data before applying", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        vi.mocked(decodeSettingsFromQRCodeData).mockReturnValue(createLegacyRemoteSetting());
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce("compatible-existing-user");

        const result = await manager.decodeQR("qr-data");

        expect(result).toBe(true);
        expect(decodeSettingsFromQRCodeData).toHaveBeenCalledWith("qr-data");
        expect(setting.currentSettings().remoteConfigurations["legacy-couchdb"]?.uri).toContain(
            "sls+http://user:password@localhost:5984"
        );
        expect(setting.currentSettings().activeConfigurationId).toBe("legacy-couchdb");
    });

    it("reserves Rebuild before saving a new-user configuration", async () => {
        const { manager, setting, dialogManager, core } = createSetupManager();
        setting.settings = { ...setting.currentSettings(), isConfigured: false };
        const applyExternalSettings = vi.spyOn(setting, "applyExternalSettings");
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce(true);

        await manager.onConfirmApplySettingsFromWizard(
            { ...createLegacyRemoteSetting(), isConfigured: true },
            UserMode.NewUser
        );

        expect(core.rebuilder.scheduleRebuild).toHaveBeenCalledWith(expect.any(Function));
        expect(core.rebuilder.scheduleRebuild.mock.invocationCallOrder[0]).toBeLessThan(
            applyExternalSettings.mock.invocationCallOrder[0]
        );
        expect(setting.currentSettings().isConfigured).toBe(true);
    });

    it("identifies P2P when opening the new-user initialisation confirmation", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        setting.settings = { ...setting.currentSettings(), isConfigured: false };
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce(true);
        const p2pProfileId = "p2p-profile";

        await manager.onConfirmApplySettingsFromWizard(
            {
                ...setting.currentSettings(),
                isConfigured: true,
                // Imported profile settings can still carry the previous compatibility field
                // until the selected profile is projected by the setting lifecycle.
                remoteType: REMOTE_COUCHDB,
                activeConfigurationId: p2pProfileId,
                remoteConfigurations: {
                    [p2pProfileId]: {
                        id: p2pProfileId,
                        name: "P2P room",
                        uri: "sls+p2p://:secret@team-room?relays=wss%3A%2F%2Frelay.example",
                        isEncrypted: false,
                    },
                },
            },
            UserMode.NewUser
        );

        expect(dialogManager.openWithExplicitCancel).toHaveBeenCalledWith(expect.anything(), {
            isP2P: true,
        });
    });

    it("reserves Fetch when compatible imported settings activate an unconfigured device", async () => {
        const { manager, setting, dialogManager, core } = createSetupManager();
        setting.settings = { ...setting.currentSettings(), isConfigured: false };
        const applyExternalSettings = vi.spyOn(setting, "applyExternalSettings");
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce({ ...createLegacyRemoteSetting(), isConfigured: true })
            .mockResolvedValueOnce("compatible-existing-user");

        await manager.onUseSetupURI(UserMode.Unknown, "mock-config://settings");

        expect(core.rebuilder.scheduleFetch).toHaveBeenCalledWith(expect.any(Function));
        expect(core.rebuilder.scheduleFetch.mock.invocationCallOrder[0]).toBeLessThan(
            applyExternalSettings.mock.invocationCallOrder[0]
        );
        expect(setting.currentSettings().isConfigured).toBe(true);
    });

    it("applies compatible settings to an already configured device without scheduling Fetch", async () => {
        const { manager, setting, dialogManager, core } = createSetupManager();
        setting.settings = { ...setting.currentSettings(), isConfigured: true };
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce({ ...createLegacyRemoteSetting(), isConfigured: true })
            .mockResolvedValueOnce("compatible-existing-user");

        await manager.onUseSetupURI(UserMode.Unknown, "mock-config://settings");

        expect(core.rebuilder.scheduleFetch).not.toHaveBeenCalled();
        expect(setting.currentSettings().isConfigured).toBe(true);
    });

    it("does not enable imported settings when the initialisation flag cannot be reserved", async () => {
        const { manager, setting, dialogManager, core } = createSetupManager();
        setting.settings = { ...setting.currentSettings(), isConfigured: false };
        const applyExternalSettings = vi.spyOn(setting, "applyExternalSettings");
        core.rebuilder.scheduleRebuild.mockResolvedValueOnce(false);
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce(true);

        await manager.onConfirmApplySettingsFromWizard(
            { ...createLegacyRemoteSetting(), isConfigured: true },
            UserMode.NewUser
        );

        expect(core.rebuilder.scheduleRebuild).toHaveBeenCalledWith(expect.any(Function));
        expect(applyExternalSettings).not.toHaveBeenCalled();
        expect(setting.currentSettings().isConfigured).toBe(false);
    });

    it("reports cancellation separately when applying settings which require initialisation", async () => {
        const { manager, dialogManager, core } = createSetupManager();
        const applySettings = vi.fn(async () => undefined);
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce("cancelled");

        const result = await manager.applySettingsWithInitialisationChoice({ applySettings, isP2P: true });

        expect(result).toEqual({ result: "cancelled" });
        expect(dialogManager.openWithExplicitCancel).toHaveBeenCalledWith(expect.anything(), { isP2P: true });
        expect(core.rebuilder.scheduleFetch).not.toHaveBeenCalled();
        expect(core.rebuilder.scheduleRebuild).not.toHaveBeenCalled();
        expect(applySettings).not.toHaveBeenCalled();
    });

    it("reserves the selected initialisation before applying pending settings", async () => {
        const { manager, dialogManager, core } = createSetupManager();
        const applySettings = vi.fn(async () => undefined);
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce("fetch");

        const result = await manager.applySettingsWithInitialisationChoice({ applySettings, isP2P: false });

        expect(result).toEqual({ result: "scheduled", mode: "fetch" });
        expect(core.rebuilder.scheduleFetch).toHaveBeenCalledWith(expect.any(Function));
        expect(core.rebuilder.scheduleFetch.mock.invocationCallOrder[0]).toBeLessThan(
            applySettings.mock.invocationCallOrder[0]
        );
    });

    it("reports a failed reservation without applying pending settings", async () => {
        const { manager, dialogManager, core } = createSetupManager();
        const applySettings = vi.fn(async () => undefined);
        core.rebuilder.scheduleRebuild.mockResolvedValueOnce(false);
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce("rebuild");

        const result = await manager.applySettingsWithInitialisationChoice({ applySettings, isP2P: false });

        expect(result).toEqual({ result: "failed", mode: "rebuild" });
        expect(applySettings).not.toHaveBeenCalled();
    });

    it("does not reserve initialisation when the selected source cannot be validated", async () => {
        const { manager, dialogManager, core } = createSetupManager();
        const applySettings = vi.fn(async () => undefined);
        const validateChoice = vi.fn(async () => false);
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce("fetch");

        const result = await manager.applySettingsWithInitialisationChoice({
            applySettings,
            isP2P: false,
            validateChoice,
        });

        expect(result).toEqual({ result: "failed", mode: "fetch" });
        expect(validateChoice).toHaveBeenCalledWith("fetch");
        expect(core.rebuilder.scheduleFetch).not.toHaveBeenCalled();
        expect(applySettings).not.toHaveBeenCalled();
    });

    it("preserves modern profiles, display names, and the active selection from a Setup URI", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        const imported = {
            ...DEFAULT_SETTINGS,
            remoteConfigurations: {
                couch: {
                    id: "couch",
                    name: "Office CouchDB",
                    uri: "sls+https://alice:secret@couch.example/?db=notes",
                    isEncrypted: false,
                },
                archive: {
                    id: "archive",
                    name: "Archive bucket",
                    uri: "sls+s3://key:secret@storage.example/?endpoint=https%3A%2F%2Fstorage.example&bucket=archive&region=auto",
                    isEncrypted: false,
                },
            },
            activeConfigurationId: "archive",
        } as ObsidianLiveSyncSettings;
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce(imported)
            .mockResolvedValueOnce("compatible-existing-user");

        await manager.onUseSetupURI(UserMode.Unknown, "mock-config://modern-settings");

        const current = setting.currentSettings();
        expect(current.remoteConfigurations).toEqual(imported.remoteConfigurations);
        expect(current.activeConfigurationId).toBe("archive");
        expect(Object.keys(current.remoteConfigurations).some((id) => id.startsWith("legacy-"))).toBe(false);
    });

    it("adds and activates a manually configured CouchDB without replacing existing profiles", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        setting.settings = {
            ...setting.currentSettings(),
            isConfigured: true,
            remoteConfigurations: {
                existing: {
                    id: "existing",
                    name: "Existing remote",
                    uri: "sls+http://old:secret@old.example/?db=old",
                    isEncrypted: false,
                },
            },
            activeConfigurationId: "existing",
        };
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce({
                couchDB_URI: "https://couch.example",
                couchDB_USER: "alice",
                couchDB_PASSWORD: "secret",
                couchDB_DBNAME: "notes",
                couchDB_CustomHeaders: "",
                useJWT: false,
                jwtAlgorithm: "",
                jwtKey: "",
                jwtKid: "",
                jwtSub: "",
                jwtExpDuration: 5,
                useRequestAPI: false,
            })
            .mockResolvedValueOnce(true);

        await manager.onCouchDBManualSetup(UserMode.ExistingUser, setting.currentSettings());

        const current = setting.currentSettings();
        expect(current.remoteConfigurations.existing).toBeDefined();
        expect(Object.keys(current.remoteConfigurations)).toHaveLength(2);
        expect(current.activeConfigurationId).not.toBe("existing");
        const activeProfile = current.remoteConfigurations[current.activeConfigurationId];
        expect(activeProfile?.name).toBe("CouchDB couch.example");
        expect(activeProfile?.uri).toContain("sls+https://alice:secret@couch.example");
    });

    it.each([
        [UserMode.NewUser, "create-or-connect"],
        [UserMode.ExistingUser, "connect-existing"],
        [UserMode.Update, "settings"],
    ] as const)(
        "passes the %s CouchDB database policy to the manual setup dialogue",
        async (userMode, expectedMode) => {
            const { manager, setting, dialogManager } = createSetupManager();
            const couchConf = {
                couchDB_URI: "https://couch.example",
                couchDB_USER: "alice",
                couchDB_PASSWORD: "secret",
                couchDB_DBNAME: "notes",
                couchDB_CustomHeaders: "",
                useJWT: false,
                jwtAlgorithm: "",
                jwtKey: "",
                jwtKid: "",
                jwtSub: "",
                jwtExpDuration: 5,
                useRequestAPI: false,
            };
            dialogManager.openWithExplicitCancel.mockResolvedValueOnce(couchConf).mockResolvedValueOnce("cancelled");

            await manager.onCouchDBManualSetup(userMode, setting.currentSettings());

            expect(dialogManager.openWithExplicitCancel).toHaveBeenNthCalledWith(1, expect.anything(), {
                settings: setting.currentSettings(),
                mode: expectedMode,
            });
        }
    );

    it("adds and activates a manually configured Object Storage profile without replacing existing profiles", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        setting.settings = {
            ...setting.currentSettings(),
            isConfigured: true,
            remoteConfigurations: {
                existing: {
                    id: "existing",
                    name: "Existing remote",
                    uri: "sls+http://old:secret@old.example/?db=old",
                    isEncrypted: false,
                },
            },
            activeConfigurationId: "existing",
        };
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce({
                endpoint: "https://storage.example",
                accessKey: "key",
                secretKey: "secret",
                bucket: "notes",
                region: "auto",
                bucketPrefix: "",
                useCustomRequestHandler: false,
                bucketCustomHeaders: "",
                forcePathStyle: true,
            })
            .mockResolvedValueOnce(true);

        await manager.onBucketManualSetup(UserMode.ExistingUser, setting.currentSettings());

        const current = setting.currentSettings();
        expect(current.remoteConfigurations.existing).toBeDefined();
        expect(Object.keys(current.remoteConfigurations)).toHaveLength(2);
        expect(current.activeConfigurationId).not.toBe("existing");
        const activeProfile = current.remoteConfigurations[current.activeConfigurationId];
        expect(activeProfile?.name).toBe("S3 notes");
        expect(activeProfile?.uri).toContain("sls+s3://key:secret@storage.example");
    });

    it("creates and selects a P2P profile during fresh manual onboarding", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        setting.settings = {
            ...setting.currentSettings(),
            isConfigured: false,
            remoteConfigurations: {},
            activeConfigurationId: "",
            P2P_ActiveRemoteConfigurationId: "",
        };
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce({
                P2P_Enabled: true,
                P2P_roomID: "team-room",
                P2P_passphrase: "secret",
                P2P_relays: "wss://relay.example",
                P2P_AppID: "self-hosted-livesync",
                P2P_AutoStart: true,
                P2P_AutoBroadcast: false,
                P2P_turnServers: "",
                P2P_turnUsername: "",
                P2P_turnCredential: "",
            })
            .mockResolvedValueOnce(true);

        await manager.onP2PManualSetup(UserMode.NewUser, setting.currentSettings());

        const current = setting.currentSettings();
        expect(Object.keys(current.remoteConfigurations)).toHaveLength(1);
        expect(current.activeConfigurationId).not.toBe("");
        expect(current.P2P_ActiveRemoteConfigurationId).toBe(current.activeConfigurationId);
        const activeProfile = current.remoteConfigurations[current.activeConfigurationId];
        expect(activeProfile?.name).toBe("P2P team-room");
        expect(activeProfile?.uri).toContain("sls+p2p://");
    });

    it("selects a configured P2P profile without replacing the active main remote", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        setting.settings = {
            ...setting.currentSettings(),
            isConfigured: true,
            remoteConfigurations: {
                main: {
                    id: "main",
                    name: "Main CouchDB",
                    uri: "sls+http://old:secret@old.example/?db=old",
                    isEncrypted: false,
                },
            },
            activeConfigurationId: "main",
            P2P_ActiveRemoteConfigurationId: "",
        };
        dialogManager.openWithExplicitCancel.mockResolvedValueOnce({
            P2P_Enabled: true,
            P2P_roomID: "team-room",
            P2P_passphrase: "secret",
            P2P_relays: "wss://relay.example",
            P2P_AppID: "self-hosted-livesync",
            P2P_AutoStart: true,
            P2P_AutoBroadcast: false,
            P2P_turnServers: "",
            P2P_turnUsername: "",
            P2P_turnCredential: "",
        });

        await manager.onP2PManualSetup(UserMode.Unknown, setting.currentSettings(), false);

        const current = setting.currentSettings();
        expect(Object.keys(current.remoteConfigurations)).toHaveLength(2);
        expect(current.activeConfigurationId).toBe("main");
        expect(current.P2P_ActiveRemoteConfigurationId).not.toBe("");
        expect(current.P2P_ActiveRemoteConfigurationId).not.toBe("main");
        expect(current.remoteConfigurations[current.P2P_ActiveRemoteConfigurationId]?.name).toBe("P2P team-room");
    });

    it("does not register Object Storage when final confirmation is cancelled", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        setting.settings = {
            ...setting.currentSettings(),
            isConfigured: true,
            remoteConfigurations: {
                existing: {
                    id: "existing",
                    name: "Existing remote",
                    uri: "sls+http://old:secret@old.example/?db=old",
                    isEncrypted: false,
                },
            },
            activeConfigurationId: "existing",
        };
        const before = structuredClone(setting.currentSettings().remoteConfigurations);
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce({
                endpoint: "https://storage.example",
                accessKey: "key",
                secretKey: "secret",
                bucket: "notes",
                region: "auto",
                bucketPrefix: "",
                useCustomRequestHandler: false,
                bucketCustomHeaders: "",
                forcePathStyle: true,
            })
            .mockResolvedValueOnce("cancelled");

        await manager.onBucketManualSetup(UserMode.ExistingUser, setting.currentSettings());

        expect(setting.currentSettings().remoteConfigurations).toEqual(before);
        expect(setting.currentSettings().activeConfigurationId).toBe("existing");
    });

    it("does not mutate an existing P2P profile when final confirmation is cancelled", async () => {
        const { manager, setting, dialogManager } = createSetupManager();
        setting.settings = {
            ...setting.currentSettings(),
            isConfigured: true,
            remoteConfigurations: {
                existing: {
                    id: "existing",
                    name: "Existing P2P remote",
                    uri: "sls+p2p://old-room?passphrase=old-secret",
                    isEncrypted: false,
                },
            },
            activeConfigurationId: "existing",
            P2P_ActiveRemoteConfigurationId: "existing",
        };
        const before = structuredClone(setting.currentSettings().remoteConfigurations);
        dialogManager.openWithExplicitCancel
            .mockResolvedValueOnce({
                P2P_Enabled: true,
                P2P_roomID: "new-room",
                P2P_passphrase: "new-secret",
                P2P_relays: "wss://relay.example",
                P2P_AppID: "self-hosted-livesync",
                P2P_AutoStart: true,
                P2P_AutoBroadcast: false,
                P2P_turnServers: "",
                P2P_turnUsername: "",
                P2P_turnCredential: "",
            })
            .mockResolvedValueOnce("cancelled");

        await manager.onP2PManualSetup(UserMode.ExistingUser, setting.currentSettings());

        expect(setting.currentSettings().remoteConfigurations).toEqual(before);
        expect(setting.currentSettings().activeConfigurationId).toBe("existing");
        expect(setting.currentSettings().P2P_ActiveRemoteConfigurationId).toBe("existing");
    });
});
