import React from "react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { BrowserRouter } from "react-router-dom";
import axios from "axios";
import { toast } from "react-toastify";
import Leaderboard from "../components/Leaderboard";
import { AuthContext } from "../context/AuthContext";

// Mock axios
jest.mock("axios");

// Mock react-toastify
jest.mock("react-toastify", () => ({
  toast: {
    success: jest.fn(),
    error: jest.fn(),
    warning: jest.fn(),
  },
}));

// Mock leaderboard data
const mockLeaderboardData = [
  {
    userId: "user1",
    username: "TopUser",
    email: "topuser@example.com",
    points: 1000,
    streetsAdopted: 5,
    tasksCompleted: 20,
    badges: [
      { name: "Beginner", icon: "🏅" },
      { name: "Intermediate", icon: "🏆" },
    ],
  },
  {
    userId: "user2",
    username: "SecondUser",
    email: "second@example.com",
    points: 800,
    streetsAdopted: 4,
    tasksCompleted: 15,
    badges: [{ name: "Beginner", icon: "🏅" }],
  },
  {
    userId: "user3",
    username: "ThirdUser",
    email: "third@example.com",
    points: 600,
    streetsAdopted: 3,
    tasksCompleted: 10,
    badges: [],
  },
];

const mockStats = {
  totalUsers: 100,
  totalPoints: 50000,
  averagePoints: 500,
  maxPoints: 1000,
  minPoints: 0,
};

describe("Leaderboard Component", () => {
  const mockAuthContext = {
    auth: {
      isAuthenticated: true,
      user: {
        _id: "user1",
        username: "TopUser",
        points: 1000,
      },
    },
  };

  const renderLeaderboard = (authContext = mockAuthContext) => {
    return render(
      <BrowserRouter>
        <AuthContext.Provider value={authContext}>
          <Leaderboard />
        </AuthContext.Provider>
      </BrowserRouter>
    );
  };

  beforeEach(() => {
    jest.clearAllMocks();
    localStorage.setItem("token", "mock-token");
  });

  afterEach(() => {
    localStorage.clear();
  });

  describe("Initial Loading", () => {
    it("should display loading spinner on initial load", () => {
      axios.get.mockImplementation(() => new Promise(() => {})); // Never resolves
      renderLeaderboard();

      expect(screen.getByText(/loading leaderboard/i)).toBeInTheDocument();
      expect(screen.getByRole("status")).toBeInTheDocument();
    });

    it("should load global leaderboard by default", async () => {
      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/global")) {
          return Promise.resolve({ data: mockLeaderboardData });
        }
        if (url.includes("/api/leaderboard/stats")) {
          return Promise.resolve({ data: mockStats });
        }
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      expect(axios.get).toHaveBeenCalledWith(
        expect.stringContaining("/api/leaderboard/global"),
        expect.anything()
      );
    });

    it("should load leaderboard stats", async () => {
      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/global")) {
          return Promise.resolve({ data: mockLeaderboardData });
        }
        if (url.includes("/api/leaderboard/stats")) {
          return Promise.resolve({ data: mockStats });
        }
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText(/total users:/i)).toBeInTheDocument();
      });

      expect(screen.getByText(/100/)).toBeInTheDocument();
      expect(screen.getByText(/50,000/)).toBeInTheDocument();
    });
  });

  describe("Tab Navigation", () => {
    beforeEach(() => {
      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/")) {
          return Promise.resolve({ data: mockLeaderboardData });
        }
        return Promise.resolve({ data: mockStats });
      });
    });

    it("should switch to weekly leaderboard when clicking weekly tab", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      const weeklyTab = screen.getByRole("button", { name: /this week/i });
      fireEvent.click(weeklyTab);

      await waitFor(() => {
        expect(axios.get).toHaveBeenCalledWith(
          expect.stringContaining("/api/leaderboard/weekly"),
          expect.anything()
        );
      });
    });

    it("should switch to monthly leaderboard when clicking monthly tab", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      const monthlyTab = screen.getByRole("button", { name: /this month/i });
      fireEvent.click(monthlyTab);

      await waitFor(() => {
        expect(axios.get).toHaveBeenCalledWith(
          expect.stringContaining("/api/leaderboard/monthly"),
          expect.anything()
        );
      });
    });

    it("should switch to friends leaderboard when authenticated", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      const friendsTab = screen.getByRole("button", { name: /friends/i });
      fireEvent.click(friendsTab);

      await waitFor(() => {
        expect(axios.get).toHaveBeenCalledWith(
          expect.stringContaining("/api/leaderboard/friends"),
          expect.objectContaining({
            headers: { "x-auth-token": "mock-token" },
          })
        );
      });
    });

    it("should show warning when trying to access friends tab without authentication", async () => {
      const unauthContext = {
        auth: {
          isAuthenticated: false,
          user: null,
        },
      };

      axios.get.mockResolvedValue({ data: mockLeaderboardData });
      renderLeaderboard(unauthContext);

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      const friendsTab = screen.getByRole("button", { name: /friends/i });
      fireEvent.click(friendsTab);

      expect(toast.warning).toHaveBeenCalledWith(
        "Please login to view friends leaderboard"
      );
    });
  });

  describe("User Display", () => {
    beforeEach(() => {
      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/")) {
          return Promise.resolve({ data: mockLeaderboardData });
        }
        return Promise.resolve({ data: mockStats });
      });
    });

    it("should display all users in leaderboard", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
        expect(screen.getByText("SecondUser")).toBeInTheDocument();
        expect(screen.getByText("ThirdUser")).toBeInTheDocument();
      });
    });

    it("should highlight current user", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      expect(screen.getByText("You")).toBeInTheDocument();
    });

    it("should display user points", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("1,000")).toBeInTheDocument();
      });
    });

    it("should display user statistics", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      // Check for streets and tasks counts
      const statsElements = screen.getAllByText(/5|20/);
      expect(statsElements.length).toBeGreaterThan(0);
    });

    it("should display current user points in alert", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText(/your points:/i)).toBeInTheDocument();
      });

      expect(screen.getByText("1000")).toBeInTheDocument();
    });
  });

  describe("Pagination", () => {
    beforeEach(() => {
      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/")) {
          return Promise.resolve({ data: mockLeaderboardData });
        }
        return Promise.resolve({ data: mockStats });
      });
    });

    it("should disable previous button on first page", async () => {
      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      const previousButton = screen.getByRole("button", { name: /previous/i });
      expect(previousButton).toBeDisabled();
    });

    it("should enable next button when there are more results", async () => {
      // Mock 50 results to trigger hasMore
      const largeDataset = Array.from({ length: 50 }, (_, i) => ({
        userId: `user${i}`,
        username: `User${i}`,
        points: 1000 - i * 10,
        streetsAdopted: 1,
        tasksCompleted: 1,
        badges: [],
      }));

      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/")) {
          return Promise.resolve({ data: largeDataset });
        }
        return Promise.resolve({ data: mockStats });
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("User0")).toBeInTheDocument();
      });

      const nextButton = screen.getByRole("button", { name: /next/i });
      expect(nextButton).not.toBeDisabled();
    });

    it("should load next page when clicking next button", async () => {
      const largeDataset = Array.from({ length: 50 }, (_, i) => ({
        userId: `user${i}`,
        username: `User${i}`,
        points: 1000 - i * 10,
        streetsAdopted: 1,
        tasksCompleted: 1,
        badges: [],
      }));

      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/")) {
          return Promise.resolve({ data: largeDataset });
        }
        return Promise.resolve({ data: mockStats });
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("User0")).toBeInTheDocument();
      });

      const nextButton = screen.getByRole("button", { name: /next/i });
      fireEvent.click(nextButton);

      await waitFor(() => {
        expect(axios.get).toHaveBeenCalledWith(
          expect.stringContaining("offset=50"),
          expect.anything()
        );
      });
    });
  });

  describe("Error Handling", () => {
    it("should display error message when API fails", async () => {
      axios.get.mockRejectedValue({
        response: { data: { msg: "Server error" } },
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText(/error loading leaderboard/i)).toBeInTheDocument();
      });

      expect(toast.error).toHaveBeenCalledWith("Server error");
    });

    it("should show retry button on error", async () => {
      axios.get.mockRejectedValue({
        response: { data: { msg: "Server error" } },
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByRole("button", { name: /retry/i })).toBeInTheDocument();
      });
    });

    it("should retry loading when clicking retry button", async () => {
      axios.get
        .mockRejectedValueOnce({
          response: { data: { msg: "Server error" } },
        })
        .mockImplementation((url) => {
          if (url.includes("/api/leaderboard/")) {
            return Promise.resolve({ data: mockLeaderboardData });
          }
          return Promise.resolve({ data: mockStats });
        });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByRole("button", { name: /retry/i })).toBeInTheDocument();
      });

      const retryButton = screen.getByRole("button", { name: /retry/i });
      fireEvent.click(retryButton);

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });
    });
  });

  describe("Empty State", () => {
    it("should display message when leaderboard is empty", async () => {
      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/")) {
          return Promise.resolve({ data: [] });
        }
        return Promise.resolve({ data: mockStats });
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(
          screen.getByText(/no users to display yet/i)
        ).toBeInTheDocument();
      });
    });

    it("should display friends-specific message when friends leaderboard is empty", async () => {
      axios.get.mockImplementation((url) => {
        if (url.includes("/api/leaderboard/friends")) {
          return Promise.resolve({ data: [] });
        }
        if (url.includes("/api/leaderboard/")) {
          return Promise.resolve({ data: mockLeaderboardData });
        }
        return Promise.resolve({ data: mockStats });
      });

      renderLeaderboard();

      await waitFor(() => {
        expect(screen.getByText("TopUser")).toBeInTheDocument();
      });

      const friendsTab = screen.getByRole("button", { name: /friends/i });
      fireEvent.click(friendsTab);

      await waitFor(() => {
        expect(
          screen.getByText(/no friends to display/i)
        ).toBeInTheDocument();
      });
    });
  });
});
