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

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

import {
  upsertTournamentResult,
  getTournamentResults,
  getTournamentResultByParticipant,
} from "../tournament-result";
import { database } from "~/database/context";

const TOURNAMENT_ID = "tournament-1";
const PARTICIPANT_ID = "participant-1";

const SAMPLE_RESULT = {
  id: "result-1",
  tournamentId: TOURNAMENT_ID,
  participantId: PARTICIPANT_ID,
  placement: 1,
  rawScore: "7500.00",
  createdAt: new Date("2026-01-28T00:00:00Z"),
  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 {
    select: vi.fn().mockReturnValue({
      from: vi.fn().mockReturnValue({
        where: vi.fn().mockReturnValue({
          limit: vi.fn().mockResolvedValue(rows),
          orderBy: vi.fn().mockResolvedValue(rows),
        }),
      }),
    }),
  };
}

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

describe("upsertTournamentResult", () => {
  it("inserts or updates a result", async () => {
    vi.mocked(database).mockReturnValue(makeUpsertDb(SAMPLE_RESULT) as never);

    const result = await upsertTournamentResult({
      tournamentId: TOURNAMENT_ID,
      participantId: PARTICIPANT_ID,
      placement: 1,
      rawScore: "7500.00",
    });

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

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

    await upsertTournamentResult({
      tournamentId: TOURNAMENT_ID,
      participantId: PARTICIPANT_ID,
      placement: 1,
    });

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

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

describe("getTournamentResults", () => {
  it("returns results ordered by placement", async () => {
    const results = [SAMPLE_RESULT];
    vi.mocked(database).mockReturnValue(makeSelectDb(results) as never);

    const result = await getTournamentResults(TOURNAMENT_ID);

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

describe("getTournamentResultByParticipant", () => {
  it("returns the result when found", async () => {
    vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_RESULT]) as never);

    const result = await getTournamentResultByParticipant(TOURNAMENT_ID, PARTICIPANT_ID);

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

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

    const result = await getTournamentResultByParticipant(TOURNAMENT_ID, "nonexistent");

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