import { describe, it, expect, beforeEach, vi } from "vitest";
import { easterGameRouter } from "./routers/easterGame.ts";
import { createCallerFactory } from "./_core/trpc.ts";

describe("Easter Leaderboard", () => {
  let caller: ReturnType<typeof createCallerFactory>;

  beforeEach(() => {
    // Create a mock caller for testing
    caller = createCallerFactory(easterGameRouter)({
      user: null,
      req: {} as any,
    });
  });

  describe("getLeaderboard", () => {
    it("should return top 10 players by default", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 10 });

      expect(result).toBeDefined();
      expect(result.leaderboard).toBeDefined();
      expect(result.leaderboard.length).toBeLessThanOrEqual(10);
      expect(result.totalPlayers).toBeGreaterThan(0);
      expect(result.lastUpdated).toBeInstanceOf(Date);
    });

    it("should return leaderboard entries with required fields", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 5 });

      expect(result.leaderboard.length).toBeGreaterThan(0);

      const entry = result.leaderboard[0];
      expect(entry).toHaveProperty("rank");
      expect(entry).toHaveProperty("userId");
      expect(entry).toHaveProperty("username");
      expect(entry).toHaveProperty("avatarUrl");
      expect(entry).toHaveProperty("eggsCollected");
      expect(entry).toHaveProperty("spins");
      expect(entry).toHaveProperty("totalWinnings");
    });

    it("should have correct ranking order (descending by eggs)", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 10 });

      for (let i = 0; i < result.leaderboard.length - 1; i++) {
        const current = result.leaderboard[i];
        const next = result.leaderboard[i + 1];

        expect(current.eggsCollected).toBeGreaterThanOrEqual(next.eggsCollected);
        expect(current.rank).toBeLessThan(next.rank);
      }
    });

    it("should assign badges to top 3 players", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 10 });

      const top3 = result.leaderboard.slice(0, 3);
      const badges = top3.map((p) => p.badge).filter(Boolean);

      expect(badges.length).toBeGreaterThanOrEqual(1);
      expect(badges).toContain("gold");
      expect(badges).toContain("silver");
      expect(badges).toContain("bronze");
    });

    it("should respect limit parameter", async () => {
      const result5 = await caller.easterGame.getLeaderboard({ limit: 5 });
      const result10 = await caller.easterGame.getLeaderboard({ limit: 10 });

      expect(result5.leaderboard.length).toBeLessThanOrEqual(5);
      expect(result10.leaderboard.length).toBeLessThanOrEqual(10);
    });

    it("should have valid avatar URLs", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 5 });

      result.leaderboard.forEach((entry) => {
        expect(entry.avatarUrl).toBeTruthy();
        expect(entry.avatarUrl).toMatch(/^https?:\/\//);
      });
    });

    it("should have consistent stats", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 10 });

      result.leaderboard.forEach((entry) => {
        expect(entry.eggsCollected).toBeGreaterThanOrEqual(0);
        expect(entry.spins).toBeGreaterThanOrEqual(0);
        expect(entry.totalWinnings).toBeGreaterThanOrEqual(0);
        expect(entry.rank).toBeGreaterThan(0);
      });
    });
  });

  describe("getUserRank", () => {
    it("should return user rank when authenticated", async () => {
      const userCaller = createCallerFactory(easterGameRouter)({
        user: { id: 1, username: "TestPlayer", email: "test@example.com" },
        req: {} as any,
      });

      const result = await userCaller.easterGame.getUserRank();

      expect(result).toBeDefined();
      expect(result.rank).toBeGreaterThan(0);
      expect(result.userId).toBe(1);
      expect(result.eggsCollected).toBeGreaterThanOrEqual(0);
      expect(result.spins).toBeGreaterThanOrEqual(0);
      expect(result.totalWinnings).toBeGreaterThanOrEqual(0);
      expect(result.percentileRank).toBeGreaterThanOrEqual(0);
      expect(result.percentileRank).toBeLessThanOrEqual(100);
    });

    it("should throw error when not authenticated", async () => {
      const unauthenticatedCaller = createCallerFactory(easterGameRouter)({
        user: null,
        req: {} as any,
      });

      await expect(
        unauthenticatedCaller.easterGame.getUserRank()
      ).rejects.toThrow();
    });

    it("should have valid percentile rank", async () => {
      const userCaller = createCallerFactory(easterGameRouter)({
        user: { id: 1, username: "TestPlayer", email: "test@example.com" },
        req: {} as any,
      });

      const result = await userCaller.easterGame.getUserRank();

      expect(result.percentileRank).toBeGreaterThanOrEqual(0);
      expect(result.percentileRank).toBeLessThanOrEqual(100);
    });
  });

  describe("Leaderboard Integration", () => {
    it("should have consistent data between getLeaderboard and getUserRank", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 100 });

      // Verify that leaderboard entries have consistent structure
      result.leaderboard.forEach((entry) => {
        expect(entry.rank).toBe(entry.rank); // Rank should match position
        expect(entry.userId).toBeGreaterThan(0);
        expect(entry.username).toBeTruthy();
      });
    });

    it("should handle empty leaderboard gracefully", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 10 });

      expect(result).toBeDefined();
      expect(Array.isArray(result.leaderboard)).toBe(true);
    });

    it("should update leaderboard timestamp", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 10 });

      expect(result.lastUpdated).toBeInstanceOf(Date);
      expect(result.lastUpdated.getTime()).toBeLessThanOrEqual(Date.now());
    });
  });

  describe("Leaderboard Performance", () => {
    it("should fetch leaderboard within reasonable time", async () => {
      const startTime = Date.now();
      await caller.easterGame.getLeaderboard({ limit: 10 });
      const endTime = Date.now();

      // Should complete within 1 second
      expect(endTime - startTime).toBeLessThan(1000);
    });

    it("should handle large limit values gracefully", async () => {
      const result = await caller.easterGame.getLeaderboard({ limit: 100 });

      expect(result.leaderboard).toBeDefined();
      expect(Array.isArray(result.leaderboard)).toBe(true);
    });
  });
});
