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

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

// Mock UserContext
jest.unstable_mockModule("../context/UserContext.js", () => ({
  __esModule: true,
  useUser: () => ({
    user: { user_email: "test@test.com" },
    permissions: [{ Path: "/user-profile", Permission_Id: "1" }],
  }),
  UserProvider: ({ children }) => <div>{children}</div>,
}));

// Mock child components
jest.unstable_mockModule("../components/CommonTable.js", () => ({
  __esModule: true,
  default: ({ data, columns, cols }) => (
    <div data-testid="common-table">
      <span data-testid="table-data-length">{data?.length || 0}</span>
      <span data-testid="table-columns-length">{columns?.length || 0}</span>
    </div>
  ),
}));

jest.unstable_mockModule("../components/SearchInput.js", () => ({
  __esModule: true,
  default: ({ onSearch, onClear }) => (
    <div data-testid="search-input">
      <input
        type="text"
        role="textbox"
        onChange={(e) => onSearch && onSearch(e.target.value)}
        data-testid="search-text-input"
      />
      <button onClick={onClear} data-testid="search-clear-button">Clear</button>
      <button onClick={() => onSearch && onSearch("")} data-testid="search-button">Search</button>
    </div>
  ),
}));

jest.unstable_mockModule("../components/DateFilterDropdown.js", () => ({
  __esModule: true,
  default: ({ onChange, selectedRange }) => (
    <div data-testid="date-filter-dropdown">
      <select
        role="combobox"
        onChange={(e) => {
          const range = { startDate: new Date(), endDate: new Date() };
          onChange && onChange(e.target.value, range);
        }}
        data-testid="date-filter-select"
      >
        <option value="Today">Today</option>
        <option value="Week">Week</option>
        <option value="Month">Month</option>
      </select>
    </div>
  ),
}));

jest.unstable_mockModule("../components/Loader.js", () => ({
  __esModule: true,
  default: () => <div data-testid="loader">Loading...</div>,
}));

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

const mockEmployeeData = [
  {
    EmpCode: "EMP001",
    EmployeeName: "John Doe",
    Location: "New York",
    TotalActive: "8:00",
    TotalIdle: "1:00",
    TotalSleep: "0:30",
    WorkLoggedOn: "2024-01-01T09:00:00",
    UpdatedOn: "2024-01-01T18:00:00",
  },
  {
    EmpCode: "EMP002",
    EmployeeName: "Jane Smith",
    Location: "Los Angeles",
    TotalActive: "7:30",
    TotalIdle: "1:30",
    TotalSleep: "0:45",
    WorkLoggedOn: "2024-01-01T09:00:00",
    UpdatedOn: "2024-01-01T17:30:00",
  },
];

const renderComponent = (props = {}) => {
  const defaultProps = {
    employeeId: null,
    summaryFilter: null,
    title: null,
    ...props,
  };

  return render(
    <BrowserRouter>
      <EmployeeWorkingTime {...defaultProps} />
    </BrowserRouter>
  );
};


describe("EmployeeWorkingTime Component", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    mockPostApi.mockResolvedValue(mockEmployeeData);
  });

  describe("Initial Rendering", () => {
    it("should render the component with all main elements", async () => {
      await act(async () => {
        renderComponent();
      });

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

      expect(screen.getByTestId("date-filter-dropdown")).toBeInTheDocument();
      expect(screen.getByTestId("search-input")).toBeInTheDocument();
    });

    it("should render inner-container wrapper", () => {
      const { container } = renderComponent();
      expect(container.querySelector(".inner-container")).toBeInTheDocument();
    });

    it("should render filters-wrapper", () => {
      const { container } = renderComponent();
      expect(container.querySelector(".filters-wrapper")).toBeInTheDocument();
    });

    it("should show loader during initial data fetch", async () => {
      mockPostApi.mockImplementation(
        () => new Promise((resolve) => setTimeout(() => resolve(mockEmployeeData), 100))
      );

      await act(async () => {
        renderComponent();
      });

      expect(screen.getByTestId("loader")).toBeInTheDocument();

      await waitFor(() => {
        expect(screen.queryByTestId("loader")).not.toBeInTheDocument();
      });
    });

    it("should hide loader after data is fetched", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.queryByTestId("loader")).not.toBeInTheDocument();
      });
    });
  });

  describe("Props Handling", () => {
    it("should render without employeeId prop (shows search input)", async () => {
      await act(async () => {
        renderComponent();
      });

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

    it("should render with employeeId prop (hides search input)", async () => {
      await act(async () => {
        renderComponent({ employeeId: "EMP001" });
      });

      await waitFor(() => {
        expect(screen.queryByTestId("search-input")).not.toBeInTheDocument();
      });
    });

    it("should render with numeric employeeId", async () => {
      await act(async () => {
        renderComponent({ employeeId: 12345 });
      });

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

    it("should render with summaryFilter prop containing CallDate", async () => {
      const summaryFilter = {
        CallDate: {
          startDate: new Date("2024-01-01"),
          endDate: new Date("2024-01-31"),
        },
        ResultsFor: "month",
      };

      await act(async () => {
        renderComponent({ summaryFilter });
      });

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

    it("should render with title prop as userProfile", async () => {
      await act(async () => {
        renderComponent({ title: "userProfile" });
      });

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

      // UserProfile should have 6 columns
      await waitFor(() => {
        expect(screen.getByTestId("table-columns-length")).toHaveTextContent("6");
      });
    });

    it("should render with title prop other than userProfile", async () => {
      await act(async () => {
        renderComponent({ title: "dashboard" });
      });

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

      // Dashboard should have 7 columns
      await waitFor(() => {
        expect(screen.getByTestId("table-columns-length")).toHaveTextContent("7");
      });
    });

    it("should handle summaryFilter with ResultsFor property", async () => {
      const summaryFilter = {
        CallDate: {
          startDate: new Date("2024-01-01"),
          endDate: new Date("2024-01-31"),
        },
        ResultsFor: "day",
      };

      await act(async () => {
        renderComponent({ summaryFilter });
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            Filter: expect.objectContaining({
              ResultsFor: "day",
            }),
          })
        );
      });
    });
  });

  describe("Data Fetching", () => {
    it("should call API on component mount", async () => {
      await act(async () => {
        renderComponent();
      });

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

    it("should fetch data successfully and update state", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("table-data-length")).toHaveTextContent("2");
      });
    });

    it("should handle API error gracefully", async () => {
      mockPostApi.mockRejectedValueOnce(new Error("API Error"));

      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("common-table")).toBeInTheDocument();
        expect(screen.getByTestId("table-data-length")).toHaveTextContent("0");
      });
    });

    it("should handle null response from API", async () => {
      mockPostApi.mockResolvedValueOnce(null);

      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("table-data-length")).toHaveTextContent("0");
      });
    });

    it("should handle empty array response", async () => {
      mockPostApi.mockResolvedValueOnce([]);

      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("table-data-length")).toHaveTextContent("0");
      });
    });
  });

  describe("Search Functionality", () => {
    it("should handle search input changes", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("search-text-input")).toBeInTheDocument();
      });

      const searchInput = screen.getByTestId("search-text-input");

      await act(async () => {
        fireEvent.change(searchInput, { target: { value: "John" } });
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            SearchText: "john",
          })
        );
      });
    });

    it("should handle search with empty string", async () => {
      await act(async () => {
        renderComponent();
      });

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

      const searchButton = screen.getByTestId("search-button");

      jest.clearAllMocks();

      await act(async () => {
        fireEvent.click(searchButton);
      });

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

    it("should handle clear button click", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("search-text-input")).toBeInTheDocument();
      });

      const searchInput = screen.getByTestId("search-text-input");

      await act(async () => {
        fireEvent.change(searchInput, { target: { value: "test search" } });
      });

      jest.clearAllMocks();

      const clearButton = screen.getByTestId("search-clear-button");

      await act(async () => {
        fireEvent.click(clearButton);
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            SearchText: "",
            Filter: expect.objectContaining({
              WorkLoggedOn: null,
              WorkEndedOn: null,
            }),
          })
        );
      });
    });

    it("should convert search text to lowercase", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("search-text-input")).toBeInTheDocument();
      });

      const searchInput = screen.getByTestId("search-text-input");

      await act(async () => {
        fireEvent.change(searchInput, { target: { value: "JOHN DOE" } });
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            SearchText: "john doe",
          })
        );
      });
    });
  });

  describe("Date Filter Functionality", () => {
    it("should handle date filter change", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("date-filter-select")).toBeInTheDocument();
      });

      jest.clearAllMocks();

      const dateFilter = screen.getByTestId("date-filter-select");

      await act(async () => {
        fireEvent.change(dateFilter, { target: { value: "Week" } });
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            Filter: expect.objectContaining({
              WorkLoggedOn: expect.any(Date),
              WorkEndedOn: expect.any(Date),
            }),
          })
        );
      });
    });

    it("should update selectedRange when date filter changes", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("date-filter-select")).toBeInTheDocument();
      });

      const dateFilter = screen.getByTestId("date-filter-select");

      await act(async () => {
        fireEvent.change(dateFilter, { target: { value: "Month" } });
      });

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

    it("should include employeeId in filter when provided", async () => {
      await act(async () => {
        renderComponent({ employeeId: "EMP123" });
      });

      await waitFor(() => {
        expect(screen.getByTestId("date-filter-select")).toBeInTheDocument();
      });

      jest.clearAllMocks();

      const dateFilter = screen.getByTestId("date-filter-select");

      await act(async () => {
        fireEvent.change(dateFilter, { target: { value: "Week" } });
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            SearchText: "EMP123",
            Filter: expect.objectContaining({
              EmpCode: "EMP123",
            }),
          })
        );
      });
    });
  });

  describe("Columns Configuration", () => {
    it("should set correct columns for userProfile title", async () => {
      await act(async () => {
        renderComponent({ title: "userProfile" });
      });

      await waitFor(() => {
        const columnsLength = screen.getByTestId("table-columns-length");
        expect(columnsLength).toHaveTextContent("6");
      });
    });

    it("should set correct columns for non-userProfile title", async () => {
      await act(async () => {
        renderComponent({ title: "dashboard" });
      });

      await waitFor(() => {
        const columnsLength = screen.getByTestId("table-columns-length");
        expect(columnsLength).toHaveTextContent("7");
      });
    });

    it("should update columns when title prop changes", async () => {
      const { rerender } = renderComponent({ title: "dashboard" });

      await waitFor(() => {
        expect(screen.getByTestId("table-columns-length")).toHaveTextContent("7");
      });

      rerender(
        <BrowserRouter>
          <EmployeeWorkingTime title="userProfile" />
        </BrowserRouter>
      );

      await waitFor(() => {
        expect(screen.getByTestId("table-columns-length")).toHaveTextContent("6");
      });
    });
  });

  describe("Filter State Management", () => {
    it("should initialize with today's date range", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            Filter: expect.objectContaining({
              WorkLoggedOn: expect.any(Date),
              WorkEndedOn: expect.any(Date),
            }),
          })
        );
      });
    });

    it("should use summaryFilter startDate and endDate when provided", async () => {
      const summaryFilter = {
        CallDate: {
          startDate: new Date("2024-01-01"),
          endDate: new Date("2024-01-31"),
        },
      };

      await act(async () => {
        renderComponent({ summaryFilter });
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            Filter: expect.objectContaining({
              WorkLoggedOn: summaryFilter.CallDate.startDate,
              WorkEndedOn: summaryFilter.CallDate.endDate,
            }),
          })
        );
      });
    });

    it("should reset filter state on clear", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("search-text-input")).toBeInTheDocument();
      });

      // Add search text first
      const searchInput = screen.getByTestId("search-text-input");
      await act(async () => {
        fireEvent.change(searchInput, { target: { value: "test" } });
      });

      jest.clearAllMocks();

      // Clear filters
      const clearButton = screen.getByTestId("search-clear-button");
      await act(async () => {
        fireEvent.click(clearButton);
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            SearchText: "",
            Filter: expect.objectContaining({
              WorkLoggedOn: null,
              WorkEndedOn: null,
            }),
          })
        );
      });
    });
  });

  describe("API Integration", () => {
    it("should call postApi with correct endpoint", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.stringContaining("GetEmployeeWorkingTime"),
          expect.any(Object)
        );
      });
    });

    it("should include pagination parameters in API call", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            PageSize: expect.any(Number),
            PageNumber: expect.any(Number),
            IgnorePaging: expect.any(Boolean),
          })
        );
      });
    });

    it("should handle multiple API calls correctly", async () => {
      await act(async () => {
        renderComponent();
      });

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

      // Trigger search
      const searchInput = screen.getByTestId("search-text-input");
      await act(async () => {
        fireEvent.change(searchInput, { target: { value: "test" } });
      });

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

  describe("Edge Cases", () => {
    it("should handle component unmount gracefully", async () => {
      const { unmount } = renderComponent();

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

      expect(() => unmount()).not.toThrow();
    });

    it("should handle rapid filter changes", async () => {
      await act(async () => {
        renderComponent();
      });

      await waitFor(() => {
        expect(screen.getByTestId("date-filter-select")).toBeInTheDocument();
      });

      const dateFilter = screen.getByTestId("date-filter-select");

      // Rapid changes
      await act(async () => {
        fireEvent.change(dateFilter, { target: { value: "Week" } });
        fireEvent.change(dateFilter, { target: { value: "Month" } });
        fireEvent.change(dateFilter, { target: { value: "Today" } });
      });

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

    it("should handle both employeeId and search functionality", async () => {
      await act(async () => {
        renderComponent({ employeeId: "EMP001" });
      });

      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledWith(
          expect.any(String),
          expect.objectContaining({
            SearchText: "EMP001",
          })
        );
      });
    });

    it("should prevent multiple initializations", async () => {
      const { rerender } = renderComponent();

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

      // Rerender component
      rerender(
        <BrowserRouter>
          <EmployeeWorkingTime />
        </BrowserRouter>
      );

      // Should still only be called once
      await waitFor(() => {
        expect(mockPostApi).toHaveBeenCalledTimes(1);
      });
    });
  });
});