import { describe, it, expect, vi, beforeEach } from "vitest";
import { get } from "svelte/store";
import type { Session } from "$lib/api/types";

const mockInvoke = vi.fn();
vi.mock("$lib/api/bindings", () => ({
  commands: {
    lmsGetSyncGroups: () => mockInvoke("lms_get_sync_groups"),
    lmsCreateSyncGroup: (masterMac: string, slaveMacs: string[]) =>
      mockInvoke("lms_create_sync_group", { masterMac, slaveMacs }),
    lmsUnsyncPlayer: (mac: string) => mockInvoke("lms_unsync_player", { mac }),
    lmsDissolveSyncGroup: (masterMac: string) =>
      mockInvoke("lms_dissolve_sync_group", { masterMac }),
  },
}));

import { lmsSync, isLmsSession, macForSession } from "./lmsSync";

function session(deviceId: string | null): Session {
  return { id: "s", deviceId } as unknown as Session;
}

describe("LMS session detection", () => {
  it("detects LMS zones by the lms- device-id prefix", () => {
    expect(isLmsSession(session("lms-aa:bb:cc:dd:ee:ff"))).toBe(true);
    expect(isLmsSession(session("chromecast-123"))).toBe(false);
    expect(isLmsSession(session(null))).toBe(false);
    expect(isLmsSession(null)).toBe(false);
  });

  it("recovers the MAC from an LMS session, null otherwise", () => {
    expect(macForSession(session("lms-aa:bb:cc:dd:ee:ff"))).toBe("aa:bb:cc:dd:ee:ff");
    expect(macForSession(session("web-xyz"))).toBeNull();
    expect(macForSession(null)).toBeNull();
  });
});

describe("lmsSync store", () => {
  beforeEach(() => {
    mockInvoke.mockReset();
    lmsSync.reset();
  });

  it("fusing preserves existing slaves and adds the new zone", async () => {
    // Initial group: master M with slave A.
    mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A"], slaveNames: [] }]);
    await lmsSync.refresh();

    // create returns void; refresh after returns the updated group.
    mockInvoke.mockResolvedValueOnce(undefined);
    mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A", "B"], slaveNames: [] }]);

    await lmsSync.fuseZone("M", "B");

    expect(mockInvoke).toHaveBeenCalledWith("lms_create_sync_group", {
      masterMac: "M",
      slaveMacs: ["A", "B"],
    });
    expect(get(lmsSync).groups[0].slaveMacs).toEqual(["A", "B"]);
  });

  it("decoupling a slave unsyncs just that player", async () => {
    mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A"], slaveNames: [] }]);
    await lmsSync.refresh();

    mockInvoke.mockResolvedValueOnce(undefined); // unsync
    mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: [], slaveNames: [] }]);

    await lmsSync.decoupleZone("A");

    expect(mockInvoke).toHaveBeenCalledWith("lms_unsync_player", { mac: "A" });
  });

  it("decoupling the master dissolves the whole group", async () => {
    mockInvoke.mockResolvedValueOnce([{ masterMac: "M", masterName: "M", slaveMacs: ["A"], slaveNames: [] }]);
    await lmsSync.refresh();

    mockInvoke.mockResolvedValueOnce(undefined); // dissolve
    mockInvoke.mockResolvedValueOnce([]);

    await lmsSync.decoupleZone("M");

    expect(mockInvoke).toHaveBeenCalledWith("lms_dissolve_sync_group", { masterMac: "M" });
  });

  it("treats a missing plugin (refresh error) as no groups, not fatal", async () => {
    mockInvoke.mockRejectedValueOnce(new Error("404"));
    await lmsSync.refresh();
    expect(get(lmsSync).groups).toEqual([]);
  });
});
