import { describe, expect, it, vi, beforeEach } from "vitest";

const authApiMock = vi.hoisted(() => ({
  get: vi.fn(async () => ({ data: { guacd: { status: "disconnected" } } })),
  post: vi.fn(async () => ({ data: { token: "local-token" } })),
}));
const remoteApiMock = vi.hoisted(() => ({
  get: vi.fn(async () => ({ data: { guacd: { status: "connected" } } })),
  post: vi.fn(async () => ({ data: { token: "remote-token" } })),
}));
const isElectronMock = vi.hoisted(() => vi.fn(() => false));
const resolveRemoteHostIdMock = vi.hoisted(() => vi.fn());

vi.mock("@/main-axios", () => ({
  authApi: authApiMock,
  getRemoteGuacamoleApi: () => remoteApiMock,
  isElectron: isElectronMock,
  handleApiError: (error: unknown) => error,
}));
vi.mock("@/lib/remote-server-api", () => ({
  resolveRemoteHostId: resolveRemoteHostIdMock,
}));

import {
  getGuacdStatus,
  getGuacamoleTokenFromHost,
} from "../../api/guacamole-api";

beforeEach(() => {
  authApiMock.get.mockClear();
  authApiMock.post.mockClear();
  remoteApiMock.get.mockClear();
  remoteApiMock.post.mockClear();
  resolveRemoteHostIdMock.mockReset();
});

describe("guacamole API origin", () => {
  it("uses the shared instance in the browser", async () => {
    isElectronMock.mockReturnValue(false);

    await getGuacdStatus();
    await getGuacamoleTokenFromHost(9, "vnc");

    expect(authApiMock.get).toHaveBeenCalledWith("/guacamole/status");
    expect(authApiMock.post).toHaveBeenCalledOnce();
    expect(remoteApiMock.get).not.toHaveBeenCalled();
    expect(remoteApiMock.post).not.toHaveBeenCalled();
  });

  it("uses the connected remote server in the desktop app", async () => {
    isElectronMock.mockReturnValue(true);

    // The embedded backend has no guacd, so asking it reports "disconnected"
    // even when the connected server can serve the session.
    const status = await getGuacdStatus();
    const token = await getGuacamoleTokenFromHost(9, "vnc");

    expect(status.guacd.status).toBe("connected");
    expect(token.token).toBe("remote-token");
    expect(remoteApiMock.get).toHaveBeenCalledWith("/guacamole/status");
    expect(remoteApiMock.post).toHaveBeenCalledOnce();
    expect(authApiMock.get).not.toHaveBeenCalled();
    expect(authApiMock.post).not.toHaveBeenCalled();
  });

  it("sends the connect-host payload unchanged to the remote server", async () => {
    isElectronMock.mockReturnValue(true);
    resolveRemoteHostIdMock.mockResolvedValue(41);

    await getGuacamoleTokenFromHost(
      9,
      "rdp",
      {
        username: "admin",
        password: "secret",
        domain: "EXAMPLE",
      },
      "host-sync-id",
    );

    expect(remoteApiMock.post).toHaveBeenCalledWith(
      "/guacamole/connect-host/41",
      {
        protocol: "rdp",
        promptedUsername: "admin",
        promptedPassword: "secret",
        promptedDomain: "EXAMPLE",
      },
    );
    expect(resolveRemoteHostIdMock).toHaveBeenCalledWith("host-sync-id");
  });

  it("sends an empty prompted domain for local RDP accounts", async () => {
    isElectronMock.mockReturnValue(false);

    await getGuacamoleTokenFromHost(9, "rdp", {
      username: "local-admin",
      password: "secret",
      domain: "",
    });

    expect(authApiMock.post).toHaveBeenCalledWith("/guacamole/connect-host/9", {
      protocol: "rdp",
      promptedUsername: "local-admin",
      promptedPassword: "secret",
      promptedDomain: "",
    });
  });

  it("does not fall back to a colliding local id when sync resolution fails", async () => {
    isElectronMock.mockReturnValue(true);
    resolveRemoteHostIdMock.mockResolvedValue(null);

    await expect(
      getGuacamoleTokenFromHost(9, "rdp", undefined, "missing-sync-id"),
    ).rejects.toThrow("The synced host does not exist on the remote server");
    expect(remoteApiMock.post).not.toHaveBeenCalled();
  });
});
