/**
 * @jest-environment node
 */
import { NextRequest } from "next/server";
import { GET } from "../route";

// Mock dependencies
jest.mock("@/lib/database/index");
jest.mock("@clerk/nextjs/server");
jest.mock("@fitai/database", () => ({
  db: {
    all: jest.fn(),
  },
  sql: jest.fn(),
}));
jest.mock("@/lib/logger");

describe("GET /api/users", () => {
  const mockGetDatabase = require("@/lib/database/index").getDatabase;
  const mockDb = require("@fitai/database").db;

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

  describe("Batch Query Optimization", () => {
    it("should use batch methods instead of individual queries", async () => {
      // Mock database methods
      const mockBatchMethods = {
        getAllUsers: jest.fn().mockResolvedValue([
          {
            id: "user1",
            email: "user1@test.com",
            firstName: "User",
            lastName: "One",
            role: "client",
            password: "hashed",
            createdAt: new Date(),
            updatedAt: new Date(),
          },
          {
            id: "user2",
            email: "user2@test.com",
            firstName: "User",
            lastName: "Two",
            role: "client",
            password: "hashed",
            createdAt: new Date(),
            updatedAt: new Date(),
          },
        ]),
        getClientsByUserIds: jest.fn().mockResolvedValue([
          {
            id: "client1",
            userId: "user1",
            membershipType: "basic",
            membershipStatus: "active",
            joinDate: new Date(),
          },
        ]),
        getActiveCheckInsByUserIds: jest.fn().mockResolvedValue([]),
        getAttendanceHistoriesByUserIds: jest.fn().mockResolvedValue([]),
      };

      mockGetDatabase.mockResolvedValue(mockBatchMethods);

      // Mock raw DB queries for gym data
      mockDb.all.mockImplementation(async (query: any) => {
        const queryStr = query.toString();
        if (queryStr.includes("users")) {
          return [
            { id: "user1", gym_id: null },
            { id: "user2", gym_id: null },
          ];
        }
        if (queryStr.includes("gyms")) {
          return [];
        }
        return [];
      });

      // Create mock request
      const request = new NextRequest("http://localhost:3000/api/users");

      const response = await GET(request);
      const data = await response.json();

      // Verify batch methods were called
      expect(mockBatchMethods.getClientsByUserIds).toHaveBeenCalledWith([
        "user1",
        "user2",
      ]);
      expect(mockBatchMethods.getActiveCheckInsByUserIds).toHaveBeenCalledWith([
        "user1",
        "user2",
      ]);
      expect(
        mockBatchMethods.getAttendanceHistoriesByUserIds,
      ).toHaveBeenCalledWith(["user1", "user2"]);

      // Verify each batch method was called only ONCE
      expect(mockBatchMethods.getClientsByUserIds).toHaveBeenCalledTimes(1);
      expect(mockBatchMethods.getActiveCheckInsByUserIds).toHaveBeenCalledTimes(
        1,
      );
      expect(
        mockBatchMethods.getAttendanceHistoriesByUserIds,
      ).toHaveBeenCalledTimes(1);

      // Verify response structure
      expect(data.success).toBe(true);
      expect(data.data.users).toHaveLength(2);
      expect(data.data.users[0]).not.toHaveProperty("password");
    });

    it("should handle empty user list efficiently", async () => {
      const mockBatchMethods = {
        getAllUsers: jest.fn().mockResolvedValue([]),
        getClientsByUserIds: jest.fn().mockResolvedValue([]),
        getActiveCheckInsByUserIds: jest.fn().mockResolvedValue([]),
        getAttendanceHistoriesByUserIds: jest.fn().mockResolvedValue([]),
      };

      mockGetDatabase.mockResolvedValue(mockBatchMethods);
      mockDb.all.mockResolvedValue([]);

      const request = new NextRequest("http://localhost:3000/api/users");
      const response = await GET(request);
      const data = await response.json();

      // Verify batch methods were called with empty arrays
      expect(mockBatchMethods.getClientsByUserIds).toHaveBeenCalledWith([]);
      expect(mockBatchMethods.getActiveCheckInsByUserIds).toHaveBeenCalledWith(
        [],
      );
      expect(
        mockBatchMethods.getAttendanceHistoriesByUserIds,
      ).toHaveBeenCalledWith([]);

      expect(data.data.users).toEqual([]);
    });

    it("should filter by role without breaking batch optimization", async () => {
      const mockBatchMethods = {
        getAllUsers: jest.fn().mockResolvedValue([
          {
            id: "user1",
            email: "user1@test.com",
            firstName: "User",
            lastName: "One",
            role: "client",
            password: "hashed",
            createdAt: new Date(),
            updatedAt: new Date(),
          },
          {
            id: "user2",
            email: "user2@test.com",
            firstName: "User",
            lastName: "Two",
            role: "admin",
            password: "hashed",
            createdAt: new Date(),
            updatedAt: new Date(),
          },
        ]),
        getClientsByUserIds: jest.fn().mockResolvedValue([]),
        getActiveCheckInsByUserIds: jest.fn().mockResolvedValue([]),
        getAttendanceHistoriesByUserIds: jest.fn().mockResolvedValue([]),
      };

      mockGetDatabase.mockResolvedValue(mockBatchMethods);
      mockDb.all.mockImplementation(async (query: any) => {
        const queryStr = query.toString();
        if (queryStr.includes("users")) {
          return [
            { id: "user1", gym_id: null },
            { id: "user2", gym_id: null },
          ];
        }
        return [];
      });

      const request = new NextRequest(
        "http://localhost:3000/api/users?role=client",
      );
      const response = await GET(request);
      const data = await response.json();

      // Should still use batch methods for filtered users
      expect(mockBatchMethods.getClientsByUserIds).toHaveBeenCalledWith([
        "user1",
      ]);

      expect(data.data.users).toHaveLength(1);
      expect(data.data.users[0].role).toBe("client");
    });
  });

  describe("Response Structure", () => {
    it("should include client data in response", async () => {
      const mockBatchMethods = {
        getAllUsers: jest.fn().mockResolvedValue([
          {
            id: "user1",
            email: "user1@test.com",
            firstName: "User",
            lastName: "One",
            role: "client",
            password: "hashed",
            createdAt: new Date(),
            updatedAt: new Date(),
          },
        ]),
        getClientsByUserIds: jest.fn().mockResolvedValue([
          {
            id: "client1",
            userId: "user1",
            membershipType: "premium",
            membershipStatus: "active",
            joinDate: new Date(),
          },
        ]),
        getActiveCheckInsByUserIds: jest.fn().mockResolvedValue([]),
        getAttendanceHistoriesByUserIds: jest.fn().mockResolvedValue([]),
      };

      mockGetDatabase.mockResolvedValue(mockBatchMethods);
      mockDb.all.mockImplementation(async (query: any) => {
        const queryStr = query.toString();
        if (queryStr.includes("users")) {
          return [{ id: "user1", gym_id: null }];
        }
        return [];
      });

      const request = new NextRequest("http://localhost:3000/api/users");
      const response = await GET(request);
      const data = await response.json();

      expect(data.data.users[0].client).toBeDefined();
      expect(data.data.users[0].client.membershipType).toBe("premium");
    });

    it("should include attendance statistics", async () => {
      const weekAgo = new Date(Date.now() - 5 * 24 * 60 * 60 * 1000);
      const twoWeeksAgo = new Date(Date.now() - 15 * 24 * 60 * 60 * 1000);

      const mockBatchMethods = {
        getAllUsers: jest.fn().mockResolvedValue([
          {
            id: "user1",
            email: "user1@test.com",
            firstName: "User",
            lastName: "One",
            role: "client",
            password: "hashed",
            createdAt: new Date(),
            updatedAt: new Date(),
          },
        ]),
        getClientsByUserIds: jest.fn().mockResolvedValue([]),
        getActiveCheckInsByUserIds: jest.fn().mockResolvedValue([]),
        getAttendanceHistoriesByUserIds: jest.fn().mockResolvedValue([
          {
            id: "att1",
            userId: "user1",
            checkInTime: weekAgo,
            checkOutTime: new Date(),
          },
          {
            id: "att2",
            userId: "user1",
            checkInTime: twoWeeksAgo,
            checkOutTime: new Date(),
          },
        ]),
      };

      mockGetDatabase.mockResolvedValue(mockBatchMethods);
      mockDb.all.mockImplementation(async (query: any) => {
        const queryStr = query.toString();
        if (queryStr.includes("users")) {
          return [{ id: "user1", gym_id: null }];
        }
        return [];
      });

      const request = new NextRequest("http://localhost:3000/api/users");
      const response = await GET(request);
      const data = await response.json();

      expect(data.data.users[0].checkInsThisWeek).toBe(1);
      expect(data.data.users[0].checkInsThisMonth).toBe(2);
    });
  });
});
