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

vi.mock("~/database/context", () => ({
  database: vi.fn(),
}));

import {
  upsertCanonicalSurfaceElo,
  getCanonicalSurfaceElo,
} from "../canonical-surface-elo";
import { database } from "~/database/context";

const PARTICIPANT_ID = "participant-1";

const SAMPLE_ELO = {
  id: "elo-1",
  participantId: PARTICIPANT_ID,
  worldRanking: 1,
  eloHard: 2400,
  eloClay: 2350,
  eloGrass: 2300,
  updatedAt: new Date("2026-01-28T00:00:00Z"),
};

function makeUpsertDb(returnValue: object) {
  return {
    insert: vi.fn().mockReturnValue({
      values: vi.fn().mockReturnValue({
        onConflictDoUpdate: vi.fn().mockReturnValue({
          returning: vi.fn().mockResolvedValue([returnValue]),
        }),
      }),
    }),
  };
}

function makeSelectDb(rows: object[]) {
  return {
    query: {
      participantSurfaceElos: {
        findFirst: vi.fn().mockResolvedValue(rows[0] ?? null),
      },
    },
  };
}

beforeEach(() => {
  vi.clearAllMocks();
});

describe("upsertCanonicalSurfaceElo", () => {
  it("inserts or updates surface elo data", async () => {
    vi.mocked(database).mockReturnValue(makeUpsertDb(SAMPLE_ELO) as never);

    const result = await upsertCanonicalSurfaceElo({
      participantId: PARTICIPANT_ID,
      worldRanking: 1,
      eloHard: 2400,
      eloClay: 2350,
      eloGrass: 2300,
    });

    expect(result).toEqual(SAMPLE_ELO);
  });

  it("uses onConflictDoUpdate on participantId", async () => {
    const mockDb = makeUpsertDb(SAMPLE_ELO);
    vi.mocked(database).mockReturnValue(mockDb as never);

    await upsertCanonicalSurfaceElo({
      participantId: PARTICIPANT_ID,
      worldRanking: 1,
    });

    const onConflictCall = (mockDb.insert as ReturnType<typeof vi.fn>)
      .mock.results[0].value.values.mock.results[0].value.onConflictDoUpdate;

    expect(onConflictCall).toHaveBeenCalled();
  });
});

describe("getCanonicalSurfaceElo", () => {
  it("returns elo data when found", async () => {
    vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_ELO]) as never);

    const result = await getCanonicalSurfaceElo(PARTICIPANT_ID);

    expect(result).toEqual(SAMPLE_ELO);
  });

  it("returns null when not found", async () => {
    vi.mocked(database).mockReturnValue(makeSelectDb([]) as never);

    const result = await getCanonicalSurfaceElo("nonexistent");

    expect(result).toBeNull();
  });
});
