import { describe, it, expect, beforeEach, vi } from "vitest";
import { gameConfigSlotsRouter } from "./gameConfigSlots.ts";

// Mock the db functions
vi.mock("../db", () => ({
  getOrCreateWallet: vi.fn(),
  creditWallet: vi.fn(),
  debitWallet: vi.fn(),
  writeAuditLog: vi.fn(),
  getDb: vi.fn(() => Promise.resolve({})), // Mock getDb to return a db object
}));

// Mock notifications
vi.mock("./notifications", () => ({
  createNotification: vi.fn(),
}));

const { getOrCreateWallet, creditWallet, debitWallet, writeAuditLog, getDb } = await import("../db");
const { createNotification } = await import("./notifications");

describe("gameConfigSlots router", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    // Mock getDb to return a truthy db object
    vi.mocked(getDb).mockResolvedValue({} as any);
  });

  describe("spin mutation", () => {
    it("should deduct bet and credit win to user wallet", async () => {
      const userId = 123;
      const gameId = "golden-pharaoh";
      const betAmount = 10;
      const gameMode = "fun";

      // Mock wallet
      const mockWallet = {
        userId,
        gcBalance: "1000.00",
        scBalance: "50.00",
        lifetimeGcEarned: "5000.00",
        lifetimeScEarned: "100.00",
      };

      vi.mocked(getOrCreateWallet).mockResolvedValue(mockWallet as any);
      vi.mocked(debitWallet).mockResolvedValue(990);
      vi.mocked(creditWallet).mockResolvedValue(1005);
      vi.mocked(getDb).mockResolvedValue({} as any);

      // Call spin
      const spinProcedure = gameConfigSlotsRouter.createCaller({
        user: { id: userId, role: "user" },
        req: {} as any,
        res: {} as any,
      }).spin;

      const result = await spinProcedure({
        gameId,
        betAmount,
        gameMode,
      });

      // Verify result
      expect(result).toBeDefined();
      expect(result.success).toBe(true);
      expect(result.betAmount).toBe(betAmount);
      expect(result.gameId).toBe(gameId);
      expect(result.currency).toBe("GC");

      // Verify wallet functions were called
      expect(getOrCreateWallet).toHaveBeenCalledWith(userId);
      expect(debitWallet).toHaveBeenCalledWith(
        userId,
        "GC",
        betAmount,
        "game_bet",
        expect.any(String),
        gameId,
        "game_config"
      );

      // Verify audit log was written
      expect(writeAuditLog).toHaveBeenCalledWith(
        expect.objectContaining({
          actorId: userId,
          action: "game_spin",
          category: "game",
        })
      );
    });

    it("should throw error if insufficient balance", async () => {
      const userId = 123;
      const gameId = "golden-pharaoh";
      const betAmount = 100;
      const gameMode = "fun";

      // Mock wallet with low balance
      const mockWallet = {
        userId,
        gcBalance: "10.00",
        scBalance: "50.00",
        lifetimeGcEarned: "5000.00",
        lifetimeScEarned: "100.00",
      };

      vi.mocked(getOrCreateWallet).mockResolvedValue(mockWallet as any);
      vi.mocked(getDb).mockResolvedValue({} as any);

      // Call spin
      const spinProcedure = gameConfigSlotsRouter.createCaller({
        user: { id: userId, role: "user" },
        req: {} as any,
        res: {} as any,
      }).spin;

      // Should throw error
      await expect(
        spinProcedure({
          gameId,
          betAmount,
          gameMode,
        })
      ).rejects.toThrow("Insufficient GC balance");
    });

    it("should handle real mode (SC currency)", async () => {
      const userId = 123;
      const gameId = "cosmic-quest";
      const betAmount = 5;
      const gameMode = "real";

      // Mock wallet
      const mockWallet = {
        userId,
        gcBalance: "1000.00",
        scBalance: "100.00",
        lifetimeGcEarned: "5000.00",
        lifetimeScEarned: "100.00",
      };

      vi.mocked(getOrCreateWallet).mockResolvedValue(mockWallet as any);
      vi.mocked(debitWallet).mockResolvedValue(95);
      vi.mocked(creditWallet).mockResolvedValue(110);
      vi.mocked(getDb).mockResolvedValue({} as any);

      // Call spin
      const spinProcedure = gameConfigSlotsRouter.createCaller({
        user: { id: userId, role: "user" },
        req: {} as any,
        res: {} as any,
      }).spin;

      const result = await spinProcedure({
        gameId,
        betAmount,
        gameMode,
      });

      // Verify result uses SC currency
      expect(result.currency).toBe("SC");
      expect(result.betAmount).toBe(betAmount);

      // Verify debitWallet was called with SC
      expect(debitWallet).toHaveBeenCalledWith(
        userId,
        "SC",
        betAmount,
        "game_bet",
        expect.any(String),
        gameId,
        "game_config"
      );
    });
  });
});
