import { expect, test } from "bun:test";
import { MemoryAuthStore } from "../src/stores/memory.ts";
import type { AuthIdentity, OAuthAccount, PasskeyCredential } from "../src/types.ts";

function identity(overrides: Partial<AuthIdentity> = {}): AuthIdentity {
  return {
    id: "identity-1",
    userId: "user-1",
    type: "email",
    value: "first@example.com",
    normalizedValue: "first@example.com",
    primary: true,
    createdAt: 1,
    updatedAt: 1,
    ...overrides,
  };
}

function passkey(overrides: Partial<PasskeyCredential> = {}): PasskeyCredential {
  return {
    id: "passkey-1",
    userId: "user-1",
    credentialId: "credential-1",
    publicKey: "public-key",
    counter: 0,
    transports: ["internal"],
    name: "Passkey",
    createdAt: 1,
    ...overrides,
  };
}

function oauth(overrides: Partial<OAuthAccount> = {}): OAuthAccount {
  return {
    id: "oauth-1",
    userId: "user-1",
    provider: "example",
    providerAccountId: "provider-account-1",
    createdAt: 1,
    updatedAt: 1,
    ...overrides,
  };
}

test("memory identity updates re-key lookups and reject collisions", async () => {
  const store = new MemoryAuthStore();
  const first = identity();
  const second = identity({
    id: "identity-2",
    userId: "user-2",
    value: "second@example.com",
    normalizedValue: "second@example.com",
  });
  await store.createIdentity(first);
  await store.createIdentity(second);

  first.value = "renamed@example.com";
  first.normalizedValue = "renamed@example.com";
  first.updatedAt = 2;
  await store.updateIdentity(first);

  expect(await store.findIdentity("email", "first@example.com")).toBeUndefined();
  expect(await store.findIdentity("email", "renamed@example.com")).toMatchObject({
    id: "identity-1",
  });

  first.value = second.value;
  first.normalizedValue = second.normalizedValue;
  await expect(store.updateIdentity(first)).rejects.toThrow("WRN-AUTH-IDENTITY-EXISTS");
});

test("memory store enforces passkey credential uniqueness", async () => {
  const store = new MemoryAuthStore();
  await store.createPasskey(passkey());
  await expect(
    store.createPasskey(
      passkey({ id: "passkey-2", userId: "user-2", credentialId: "credential-1" }),
    ),
  ).rejects.toThrow("WRN-AUTH-PASSKEY-EXISTS");

  await store.createPasskey(
    passkey({ id: "passkey-2", userId: "user-2", credentialId: "credential-2" }),
  );
  await expect(
    store.updatePasskey(
      passkey({ id: "passkey-2", userId: "user-2", credentialId: "credential-1" }),
    ),
  ).rejects.toThrow("WRN-AUTH-PASSKEY-IMMUTABLE");
});

test("memory store enforces OAuth provider-account uniqueness", async () => {
  const store = new MemoryAuthStore();
  await store.createOAuthAccount(oauth());
  await expect(
    store.createOAuthAccount(
      oauth({ id: "oauth-2", userId: "user-2", providerAccountId: "provider-account-1" }),
    ),
  ).rejects.toThrow("WRN-AUTH-OAUTH-ACCOUNT-EXISTS");

  await store.createOAuthAccount(
    oauth({ id: "oauth-2", userId: "user-2", providerAccountId: "provider-account-2" }),
  );
  await expect(
    store.updateOAuthAccount(
      oauth({ id: "oauth-2", userId: "user-2", providerAccountId: "provider-account-1" }),
    ),
  ).rejects.toThrow("WRN-AUTH-OAUTH-ACCOUNT-IMMUTABLE");
});
