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

// Mock axios
const mockAxiosCreate = jest.fn(() => ({
  post: jest.fn(),
  get: jest.fn(),
}));

jest.unstable_mockModule("axios", () => ({
  __esModule: true,
  default: {
    create: mockAxiosCreate,
  },
}));

// Mock MSAL
const mockAcquireTokenSilent = jest.fn(() => Promise.resolve({ accessToken: "test-token" }));
const mockLogoutRedirect = jest.fn();

jest.unstable_mockModule("@azure/msal-react", () => ({
  __esModule: true,
  useMsal: () => ({
    instance: {
      acquireTokenSilent: mockAcquireTokenSilent,
      logoutRedirect: mockLogoutRedirect,
    },
    accounts: [{ username: "test@test.com" }],
  }),
}));

// Mock UserContext
const mockLogout = jest.fn();
const mockSetOpenRoleChangedPopup = jest.fn();

jest.unstable_mockModule("../context/UserContext.js", () => ({
  __esModule: true,
  useUser: () => ({
    logout: mockLogout,
    user: { user_role_id: 1 },
    openRoleChangedPopup: false,
    setOpenRoleChangedPopup: mockSetOpenRoleChangedPopup,
  }),
}));

// Mock useTokenExpiryMonitor
jest.unstable_mockModule("../hooks/useTokenExpiryMonitor.js", () => ({
  __esModule: true,
  useTokenExpiryMonitor: () => ({
    setDialog: jest.fn(),
  }),
}));

// Mock RoleChangedPopup
jest.unstable_mockModule("../components/RoleChangedPopup.js", () => ({
  __esModule: true,
  default: ({ open, onLogout, message }) => (
    open ? <div data-testid="role-changed-popup">{message}</div> : null
  ),
}));

// Import after mocks
const { default: useApiService } = await import("../services/apiService.js");

// Test component that uses the hook
function TestComponent({ endpoint, data, action }) {
  const { postApi, getApi, RoleChangedPopupElement } = useApiService();
  const [result, setResult] = React.useState(null);
  const [error, setError] = React.useState(null);

  const handlePost = async () => {
    try {
      const res = await postApi(endpoint, data);
      setResult(res);
    } catch (e) {
      setError(e);
    }
  };

  const handleGet = async () => {
    try {
      const res = await getApi(endpoint);
      setResult(res);
    } catch (e) {
      setError(e);
    }
  };

  return (
    <div>
      <button onClick={handlePost} data-testid="post-btn">Post</button>
      <button onClick={handleGet} data-testid="get-btn">Get</button>
      {result && <div data-testid="result">{JSON.stringify(result)}</div>}
      {error && <div data-testid="error">Error</div>}
      {RoleChangedPopupElement}
    </div>
  );
}

describe("apiService hook", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    localStorage.clear();
  });

  it("renders test component", () => {
    render(<TestComponent endpoint="/test" data={{}} />);
    expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    expect(screen.getByTestId("get-btn")).toBeInTheDocument();
  });

  it("creates api client with correct headers", async () => {
    const mockPost = jest.fn(() => Promise.resolve({ data: { IsSuccess: true, Data: { id: 1 } } }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

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

  it("handles post request", async () => {
    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
  });

  it("handles get request", async () => {
    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

    await waitFor(() => {
      expect(screen.getByTestId("get-btn")).toBeInTheDocument();
    });
  });

  it("renders RoleChangedPopupElement", () => {
    render(<TestComponent endpoint="/test" data={{}} />);
    // The popup element is rendered but hidden when openRoleChangedPopup is false
    expect(screen.getByTestId("post-btn")).toBeInTheDocument();
  });

  it("handles successful post request with data", async () => {
    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
  });

  it("handles successful get request", async () => {
    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

    await waitFor(() => {
      expect(screen.getByTestId("get-btn")).toBeInTheDocument();
    });
  });

  it("renders buttons correctly", () => {
    render(<TestComponent endpoint="/test" data={{}} />);
    expect(screen.getByTestId("post-btn")).toHaveTextContent("Post");
    expect(screen.getByTestId("get-btn")).toHaveTextContent("Get");
  });

  it("component handles multiple requests", async () => {
    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    const getBtn = screen.getByTestId("get-btn");
    
    fireEvent.click(postBtn);
    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
    
    fireEvent.click(getBtn);
    await waitFor(() => {
      expect(screen.getByTestId("get-btn")).toBeInTheDocument();
    });
  });

  it("handles post API success response", async () => {
    const mockPost = jest.fn(() => Promise.resolve({ data: { IsSuccess: true, Data: { id: 1 } } }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

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

  it("handles get API success response", async () => {
    const mockGet = jest.fn(() => Promise.resolve({ data: { IsSuccess: true, Data: { id: 1 } } }));
    mockAxiosCreate.mockReturnValueOnce({
      post: jest.fn(),
      get: mockGet,
    });

    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

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

  it("handles API error with status 498", async () => {
    const mockPost = jest.fn(() => Promise.reject({
      response: {
        status: 498,
        data: { IsSuccess: false, Message: "Role Changed. Please Re-Login" }
      }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
  });

  it("handles API error with status 499", async () => {
    const mockPost = jest.fn(() => Promise.reject({
      response: {
        status: 499,
        data: { IsSuccess: false, Message: "User has been deleted. Please contact the administrator." }
      }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
  });

  it("handles API error with status 497", async () => {
    const mockPost = jest.fn(() => Promise.reject({
      response: {
        status: 497,
        data: { IsSuccess: false, Message: "User Status changed to Inactive. Please contact the administrator." }
      }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
  });

  it("handles not authorized error", async () => {
    const mockPost = jest.fn(() => Promise.reject({
      response: {
        status: 401,
        data: { IsSuccess: false, Message: "You are not Authorized" }
      }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
  });

  it("handles invalid token response", async () => {
    const mockPost = jest.fn(() => Promise.resolve({
      data: { IsSuccess: false, Message: "Not a valid token." }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(mockAcquireTokenSilent).toHaveBeenCalled();
    });
    // Test that the code path for invalid token was reached
  });

  it("handles get invalid token response", async () => {
    const mockGet = jest.fn(() => Promise.resolve({
      data: { IsSuccess: false, Message: "Not a valid token." }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: jest.fn(),
      get: mockGet,
    });

    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

    await waitFor(() => {
      expect(mockAcquireTokenSilent).toHaveBeenCalled();
    });
    // Test that the code path for invalid token was reached
  });

  it("handles requireData parameter in postApi", async () => {
    const mockPost = jest.fn(() => Promise.resolve({
      data: { IsSuccess: true, Data: { id: 1 }, Records: [] }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

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

  it("handles error response without response object", async () => {
    const mockPost = jest.fn(() => Promise.reject(new Error("Network error")));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

    await waitFor(() => {
      expect(screen.getByTestId("post-btn")).toBeInTheDocument();
    });
  });

  it("handles get API error", async () => {
    const mockGet = jest.fn(() => Promise.reject(new Error("Get error")));
    mockAxiosCreate.mockReturnValueOnce({
      post: jest.fn(),
      get: mockGet,
    });

    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

    await waitFor(() => {
      expect(screen.getByTestId("get-btn")).toBeInTheDocument();
    });
  });

  it("creates axios client with correct base URL and headers", async () => {
    const mockPost = jest.fn(() => Promise.resolve({ data: { IsSuccess: true, Data: {} } }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

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

  it("handles role changed popup element", async () => {
    render(<TestComponent endpoint="/test" data={{}} />);
    
    // Verify component renders without errors
    expect(screen.getByTestId("post-btn")).toBeInTheDocument();
  });

  it("handles get API with 498 error", async () => {
    const mockGet = jest.fn(() => Promise.reject({
      response: {
        status: 498,
        data: { IsSuccess: false, Message: "Role Changed. Please Re-Login" }
      }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: jest.fn(),
      get: mockGet,
    });

    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

    await waitFor(() => {
      expect(screen.getByTestId("get-btn")).toBeInTheDocument();
    });
  });

  it("handles get API with 499 error", async () => {
    const mockGet = jest.fn(() => Promise.reject({
      response: {
        status: 499,
        data: { IsSuccess: false, Message: "User has been deleted. Please contact the administrator." }
      }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: jest.fn(),
      get: mockGet,
    });

    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

    await waitFor(() => {
      expect(screen.getByTestId("get-btn")).toBeInTheDocument();
    });
  });

  it("handles get API with 497 error", async () => {
    const mockGet = jest.fn(() => Promise.reject({
      response: {
        status: 497,
        data: { IsSuccess: false, Message: "User Status changed to Inactive. Please contact the administrator." }
      }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: jest.fn(),
      get: mockGet,
    });

    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

    await waitFor(() => {
      expect(screen.getByTestId("get-btn")).toBeInTheDocument();
    });
  });

  it("handles get API with not authorized error", async () => {
    const mockGet = jest.fn(() => Promise.reject({
      response: {
        status: 401,
        data: { IsSuccess: false, Message: "You are not Authorized" }
      }
    }));
    mockAxiosCreate.mockReturnValue({
      post: jest.fn(),
      get: mockGet,
    });

    render(<TestComponent endpoint="/test" data={{}} />);
    
    const getBtn = screen.getByTestId("get-btn");
    fireEvent.click(getBtn);

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

  it("returns full data when requireData is true", async () => {
    const mockPost = jest.fn(() => Promise.resolve({
      data: { IsSuccess: true, Data: { id: 1 }, Records: [], Message: "Success" }
    }));
    mockAxiosCreate.mockReturnValueOnce({
      post: mockPost,
      get: jest.fn(),
    });

    render(<TestComponent endpoint="/test" data={{ name: "test" }} />);
    
    const postBtn = screen.getByTestId("post-btn");
    fireEvent.click(postBtn);

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