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

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

import {
  createTournament,
  getTournamentById,
  findTournamentsBySport,
  findTournamentBySportNameYear,
  upsertTournament,
  updateTournamentStatus,
} from "../tournament";
import { database } from "~/database/context";

const SPORT_ID = "sport-1";
const TOURNAMENT_ID = "tournament-1";

const SAMPLE_TOURNAMENT = {
  id: TOURNAMENT_ID,
  sportId: SPORT_ID,
  name: "Australian Open",
  year: 2026,
  startsAt: new Date("2026-01-15T00:00:00Z"),
  endsAt: new Date("2026-01-28T00:00:00Z"),
  surface: "hard",
  location: "Melbourne",
  status: "scheduled" as const,
  externalKey: "ao-2026",
  createdAt: new Date("2026-01-01T00:00:00Z"),
  updatedAt: new Date("2026-01-01T00:00:00Z"),
};

function makeInsertDb(returnValue: object) {
  return {
    insert: vi.fn().mockReturnValue({
      values: 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),
        }),
      }),
    }),
  };
}

function makeUpdateDb(returnValue: object) {
  return {
    update: vi.fn().mockReturnValue({
      set: vi.fn().mockReturnValue({
        where: vi.fn().mockReturnValue({
          returning: vi.fn().mockResolvedValue([returnValue]),
        }),
      }),
    }),
  };
}

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

describe("createTournament", () => {
  it("inserts a tournament and returns it", async () => {
    vi.mocked(database).mockReturnValue(makeInsertDb(SAMPLE_TOURNAMENT) as never);

    const result = await createTournament({
      sportId: SPORT_ID,
      name: "Australian Open",
      year: 2026,
    });

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

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

    const result = await getTournamentById(TOURNAMENT_ID);

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

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

    const result = await getTournamentById("nonexistent");

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

describe("findTournamentsBySport", () => {
  it("returns tournaments ordered by year and date", async () => {
    const tournaments = [SAMPLE_TOURNAMENT];
    vi.mocked(database).mockReturnValue(makeSelectDb(tournaments) as never);

    const result = await findTournamentsBySport(SPORT_ID);

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

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

    const result = await findTournamentBySportNameYear(SPORT_ID, "Australian Open", 2026);

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

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

    const result = await findTournamentBySportNameYear(SPORT_ID, "Wimbledon", 2026);

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

describe("upsertTournament", () => {
  it("returns existing tournament if found", async () => {
    vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_TOURNAMENT]) as never);

    const result = await upsertTournament({
      sportId: SPORT_ID,
      name: "Australian Open",
      year: 2026,
    });

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

  it("creates new tournament if not found", async () => {
    let callCount = 0;
    vi.mocked(database).mockImplementation(() => {
      callCount++;
      return (callCount === 1 ? makeSelectDb([]) : makeInsertDb(SAMPLE_TOURNAMENT)) as never;
    });

    const result = await upsertTournament({
      sportId: SPORT_ID,
      name: "Australian Open",
      year: 2026,
    });

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

describe("updateTournamentStatus", () => {
  it("updates status and returns the tournament", async () => {
    const updated = { ...SAMPLE_TOURNAMENT, status: "in_progress" as const };
    vi.mocked(database).mockReturnValue(makeUpdateDb(updated) as never);

    const result = await updateTournamentStatus(TOURNAMENT_ID, "in_progress");

    expect(result.status).toBe("in_progress");
  });
});
