import { describe, expect, it, vi } from "vitest";
import { AccountRepository } from "./accountRepository";
import { OnlineApiError, type OnlineRepository } from "./onlineRepository";

function onlineStub(overrides: Partial<OnlineRepository> = {}): OnlineRepository {
  return {
    register: vi.fn(async (username: string) => ({ id: 1, username })),
    login: vi.fn(async (username: string) => ({ id: 1, username })),
    session: vi.fn(async () => null),
    logout: vi.fn(async () => undefined),
    ...overrides,
  } as unknown as OnlineRepository;
}

describe("AccountRepository", () => {
  it("requires both username and password before contacting server", async () => {
    const online = onlineStub();
    const repository = new AccountRepository(online);
    await expect(repository.create("", "secret")).resolves.toEqual({ ok: false, reason: "missing-credentials" });
    await expect(repository.authenticate("healer", "")).resolves.toEqual({ ok: false, reason: "missing-credentials" });
    expect(online.register).not.toHaveBeenCalled();
  });

  it("creates and authenticates real server accounts", async () => {
    const repository = new AccountRepository(onlineStub());
    await expect(repository.create("Wayfinder", "long-password")).resolves.toEqual({ ok: true, username: "Wayfinder" });
    await expect(repository.authenticate("Wayfinder", "long-password")).resolves.toEqual({ ok: true, username: "Wayfinder" });
  });

  it("maps server conflicts, invalid credentials, and outages", async () => {
    const conflict = new AccountRepository(onlineStub({ register: vi.fn(async () => { throw new OnlineApiError("exists", 409); }) }));
    await expect(conflict.create("Wayfinder", "long-password")).resolves.toMatchObject({ ok: false, reason: "account-exists" });
    const invalid = new AccountRepository(onlineStub({ login: vi.fn(async () => { throw new OnlineApiError("bad login", 401); }) }));
    await expect(invalid.authenticate("Wayfinder", "wrong-password")).resolves.toMatchObject({ ok: false, reason: "invalid-password" });
    const outage = new AccountRepository(onlineStub({ login: vi.fn(async () => { throw new OnlineApiError("offline", 0); }) }));
    await expect(outage.authenticate("Wayfinder", "long-password")).resolves.toMatchObject({ ok: false, reason: "server-unavailable" });
  });
});
