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

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

import {
  upsertCanonicalGolfSkills,
  getCanonicalGolfSkills,
} from "../canonical-golf-skills";
import { database } from "~/database/context";

const PARTICIPANT_ID = "participant-1";

const SAMPLE_SKILLS = {
  id: "skill-1",
  participantId: PARTICIPANT_ID,
  sgTotal: "2.50",
  datagolfRank: 3,
  mastersOdds: 800,
  usOpenOdds: 1200,
  openChampionshipOdds: 1000,
  pgaChampionshipOdds: 900,
  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: {
      participantGolfSkills: {
        findFirst: vi.fn().mockResolvedValue(rows[0] ?? null),
      },
    },
  };
}

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

describe("upsertCanonicalGolfSkills", () => {
  it("inserts or updates golf skill data", async () => {
    vi.mocked(database).mockReturnValue(makeUpsertDb(SAMPLE_SKILLS) as never);

    const result = await upsertCanonicalGolfSkills({
      participantId: PARTICIPANT_ID,
      sgTotal: "2.50",
      datagolfRank: 3,
      mastersOdds: 800,
    });

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

  it("throws if participantId is missing", async () => {
    await expect(
      upsertCanonicalGolfSkills({ participantId: undefined as unknown as string }),
    ).rejects.toThrow("participantId is required");
  });

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

    await upsertCanonicalGolfSkills({
      participantId: PARTICIPANT_ID,
      sgTotal: "2.50",
    });

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

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

describe("getCanonicalGolfSkills", () => {
  it("returns skills when found", async () => {
    vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_SKILLS]) as never);

    const result = await getCanonicalGolfSkills(PARTICIPANT_ID);

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

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

    const result = await getCanonicalGolfSkills("nonexistent");

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