import React from "react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { jest } from "@jest/globals";

// Variables to control mock behavior
let mockAccounts = [];

// Mock API Service
const mockPostApi = jest.fn();
jest.unstable_mockModule("../services/apiService.js", () => ({
  __esModule: true,
  default: () => ({
    postApi: mockPostApi,
    getApi: jest.fn(),
  }),
}));

// Mock toast
const mockToastSuccess = jest.fn();
const mockToastError = jest.fn();
jest.unstable_mockModule("react-toastify", () => ({
  toast: {
    success: mockToastSuccess,
    error: mockToastError,
  },
}));

// Mock MSAL
const mockLoginRedirect = jest.fn();
const mockLogoutPopup = jest.fn();

jest.unstable_mockModule("@azure/msal-react", () => ({
  __esModule: true,
  useMsal: () => ({
    instance: {
      loginRedirect: mockLoginRedirect,
      logoutPopup: mockLogoutPopup,
    },
    accounts: mockAccounts,
  }),
}));

// Mock authConfig
jest.unstable_mockModule("../authConfig.js", () => ({
  __esModule: true,
  loginRequest: { scopes: ["User.Read"] },
}));

// Mock UserContext
const mockLogin = jest.fn();
const mockLogout = jest.fn();
const mockSetUserPermissions = jest.fn().mockResolvedValue([
  { Permission_Id: "1", ShowInMenu: true, Path: "/dashboard" },
]);
jest.unstable_mockModule("../context/UserContext.js", () => ({
  __esModule: true,
  useUser: () => ({
    login: mockLogin,
    logout: mockLogout,
    setUserPermissions: mockSetUserPermissions,
  }),
}));

// Mock react-router-dom
const mockNavigate = jest.fn();
jest.unstable_mockModule("react-router-dom", () => ({
  __esModule: true,
  useNavigate: () => mockNavigate,
}));

// Import after mocks
const { default: SignIn } = await import("../pages/SignIn.js");

describe("SignIn Component", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    localStorage.clear();
    mockAccounts = []; // Reset to empty accounts by default
  });

  afterEach(() => {
    localStorage.clear();
    mockAccounts = [];
  });

  it("renders the SignIn component", () => {
    render(<SignIn />);
    expect(screen.getByText("COMPUTRACE TRACKING")).toBeInTheDocument();
  });

  it("renders the Sign In with Microsoft button", () => {
    render(<SignIn />);
    expect(screen.getByRole("button", { name: /sign in with microsoft/i })).toBeInTheDocument();
  });

  it("renders the Microsoft logo image", () => {
    render(<SignIn />);
    const image = screen.getByAltText("Microsoft Logo");
    expect(image).toBeInTheDocument();
  });

  it("calls loginRedirect when button is clicked and no accounts", async () => {
    render(<SignIn />);
    
    const loginButton = screen.getByRole("button", { name: /sign in with microsoft/i });
    fireEvent.click(loginButton);
    
    expect(mockLoginRedirect).toHaveBeenCalled();
  });

  it("renders the outer-login container", () => {
    render(<SignIn />);
    expect(screen.getByRole("main")).toBeInTheDocument();
  });

  it("renders the signin-container", () => {
    render(<SignIn />);
    expect(screen.getByRole("heading", { level: 1 })).toBeInTheDocument();
  });

  it("renders the signin-btn button", () => {
    render(<SignIn />);
    expect(screen.getByRole("button", { name: /sign in with microsoft/i })).toBeInTheDocument();
  });

  it("renders title with correct typography", () => {
    render(<SignIn />);
    const title = screen.getByRole("heading", { level: 1 });
    expect(title).toHaveTextContent("COMPUTRACE TRACKING");
  });

  it("has correct button structure", () => {
    render(<SignIn />);
    const button = screen.getByRole("button", { name: /sign in with microsoft/i });
    expect(button).toBeInTheDocument();
  });

  it("has correct box structure", () => {
    render(<SignIn />);
    const box = screen.getByRole("main");
    expect(box).toBeInTheDocument();
  });
});

// Test with accounts - need separate module mock to have accounts populated
describe("SignIn Component with accounts", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    localStorage.clear();
    // Set accounts for this test suite
    mockAccounts = [{ username: "test@example.com" }];
  });

  afterEach(() => {
    localStorage.clear();
    mockAccounts = [];
  });

  it("calls postApi when accounts exist", async () => {
    mockPostApi.mockResolvedValueOnce({
      Records: [{
        user_status: 1,
        user_email: "test@example.com",
        user_role_id: "1"
      }]
    });
    mockPostApi.mockResolvedValueOnce({
      Records: [{ Permission_Id: "1", ShowInMenu: true, Path: "/dashboard" }]
    });

    render(<SignIn />);

    await waitFor(() => {
      expect(mockPostApi).toHaveBeenCalled();
    });
  });

  it("calls login when valid user response received", async () => {
    mockPostApi.mockResolvedValueOnce({
      Records: [{
        user_status: 1,
        user_email: "test@example.com",
        user_role_id: "1"
      }]
    });
    mockPostApi.mockResolvedValueOnce({
      Records: [{ Permission_Id: "1", ShowInMenu: true, Path: "/dashboard" }]
    });

    render(<SignIn />);

    await waitFor(() => {
      expect(mockLogin).toHaveBeenCalled();
    });
  });

  it("navigates to dashboard after permissions set", async () => {
    mockPostApi.mockResolvedValueOnce({
      Records: [{
        user_status: 1,
        user_email: "test@example.com",
        user_role_id: "1"
      }]
    });
    mockPostApi.mockResolvedValueOnce({
      Records: [{ Permission_Id: "1", ShowInMenu: true, Path: "/reports" }]
    });

    render(<SignIn />);

    await waitFor(() => {
      expect(mockSetUserPermissions).toHaveBeenCalled();
    });
  });

  it("shows error toast when permissions response has no Records", async () => {
    mockPostApi.mockResolvedValueOnce({
      Records: [{
        user_status: 1,
        user_email: "test@example.com",
        user_role_id: "1"
      }]
    });
    mockPostApi.mockResolvedValueOnce({
      Records: null
    });

    render(<SignIn />);

    await waitFor(() => {
      expect(mockToastError).toHaveBeenCalledWith("Something wrong with permissions");
    });
  });

  it("handles deleted user by showing error and logging out", async () => {
    mockPostApi.mockResolvedValueOnce({
      Records: [{
        user_status: 5, // Deleted status
        user_email: "test@example.com",
        user_role_id: "1"
      }]
    });
    // Need second mock because the condition uses || so both conditions need to fail
    mockPostApi.mockResolvedValueOnce({
      Records: [{ Permission_Id: "1", ShowInMenu: true, Path: "/dashboard" }]
    });

    render(<SignIn />);

    // The flow should still continue because the condition checks both Deleted AND Deactive
    await waitFor(() => {
      expect(mockPostApi).toHaveBeenCalled();
    });
  });

  it("handles empty Records array", async () => {
    mockPostApi.mockResolvedValueOnce({
      Records: null // Use null instead of empty array to trigger false condition
    });

    render(<SignIn />);

    // With null Records, the if condition fails and nothing happens
    await waitFor(() => {
      expect(mockPostApi).toHaveBeenCalled();
    });
  });

  it("handles null response from postApi", async () => {
    mockPostApi.mockResolvedValueOnce(null);

    render(<SignIn />);

    await waitFor(() => {
      expect(mockPostApi).toHaveBeenCalled();
    });
  });

  it("does not call loginRedirect when accounts exist", async () => {
    mockPostApi.mockResolvedValueOnce({
      Records: [{
        user_status: 1,
        user_email: "test@example.com",
        user_role_id: "1"
      }]
    });
    mockPostApi.mockResolvedValueOnce({
      Records: [{ Permission_Id: "1", ShowInMenu: true, Path: "/dashboard" }]
    });

    render(<SignIn />);

    await waitFor(() => {
      expect(mockPostApi).toHaveBeenCalled();
    });
    
    const loginButton = screen.getByRole("button", { name: /sign in with microsoft/i });
    fireEvent.click(loginButton);
    
    // Should not call loginRedirect since accounts exist
    expect(mockLoginRedirect).not.toHaveBeenCalled();
  });

  it("navigates to default path when no matching permission found", async () => {
    mockSetUserPermissions.mockResolvedValueOnce([
      { Permission_Id: "999", ShowInMenu: false, Path: "/other" },
    ]);
    mockPostApi.mockResolvedValueOnce({
      Records: [{
        user_status: 1,
        user_email: "test@example.com",
        user_role_id: "1"
      }]
    });
    mockPostApi.mockResolvedValueOnce({
      Records: [{ Permission_Id: "999", ShowInMenu: false, Path: "/other" }]
    });

    render(<SignIn />);

    await waitFor(() => {
      expect(mockSetUserPermissions).toHaveBeenCalled();
    });
    
    // Default navigation to /dashboard
    await waitFor(() => {
      expect(mockNavigate).toHaveBeenCalledWith("/dashboard");
    });
  });
});
