import React from "react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { jest } from "@jest/globals";
import { UserProvider } from "../context/UserContext.js";

const mockToast = {
  success: jest.fn(),
  error: jest.fn(),
  warning: jest.fn(),
  info: jest.fn(),
};

await jest.unstable_mockModule("react-toastify", () => ({
  toast: mockToast,
}));

const mockPostApi = jest.fn();
await jest.unstable_mockModule("../services/apiService.js", () => ({
  default: () => ({ postApi: mockPostApi }),
}));

// ⬇️ imports must come AFTER the mocks
const { default: GlobalConfig } = await import(
  "../pages/GlobalConfig/GlobalConfig.js"
);

// sample fake data
const fakeConfig = [
  {
    Id: 1,
    Label: "Field One",
    Type: "text",
    ProValue: "abc",
    Editable: true,
    Min: 1,
    Max: 100,
    HelpText: "Help1",
    FieldOrder: 1,
  },
  {
    Id: 2,
    Label: "Field Two",
    Type: "number",
    ProValue: "10",
    Editable: true,
    Min: 5,
    Max: 15,
    HelpText: "Help2",
    FieldOrder: 2,
  },
  {
    Id: 3,
    Label: "Read Only",
    Type: "text",
    ProValue: "locked",
    Editable: false,
    Min: 0,
    Max: 10,
    HelpText: "Help3",
    FieldOrder: 3,
  },
  {
    Id: 4,
    Label: "Email Field",
    Type: "email",
    ProValue: "test@example.com",
    Editable: true,
    Min: 5,
    Max: 50,
    Pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$",
    ErrorText: "Invalid email",
    HelpText: "Help4",
    FieldOrder: 4,
  },
  {
    Id: 5,
    Label: "List Field",
    Type: "list",
    ProValue: "item1,item2",
    Editable: true,
    HelpText: "Help5",
    FieldOrder: 5,
  },
];

describe("GlobalConfig", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    mockPostApi.mockResolvedValue({ Records: fakeConfig });
    localStorage.setItem("user", JSON.stringify({ user_role_id: 1 }));
    localStorage.setItem("permissions", JSON.stringify([]));
  });

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

  const renderWithUser = () =>
    render(
      <UserProvider>
        <GlobalConfig />
      </UserProvider>
    );

  it("renders Configuration Editor title", async () => {
    renderWithUser();
    expect(mockPostApi).toHaveBeenCalled();
    expect(
      await screen.findByText(/Configuration Editor/i)
    ).toBeInTheDocument();
  });

  it("loads fields from API and displays them", async () => {
    renderWithUser();
    const fieldOnes = await screen.findAllByText("Field One");
    expect(fieldOnes.length).toBeGreaterThanOrEqual(1);
  });

  it("shows lock icon for non-editable fields", async () => {
    renderWithUser();
    const readOnlyElements = await screen.findAllByText("Read Only");
    // The Lock icon should be present
    expect(readOnlyElements.length).toBeGreaterThan(0);
  });

  it("enables Edit Selected button when checkboxes are selected", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    const editSelectedBtn = screen.getByRole("button", { name: /edit selected/i });
    expect(editSelectedBtn).toBeDisabled();

    const checkboxes = screen.getAllByRole("checkbox");
    fireEvent.click(checkboxes[1]); // First editable item

    expect(editSelectedBtn).not.toBeDisabled();
  }, 15000);

  it("handles individual edit click", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    const editButtons = screen.getAllByRole("button", { name: /^edit$/i });
    fireEvent.click(editButtons[0]);

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

  it("handles cancel edit", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    const editButtons = screen.getAllByRole("button", { name: /^edit$/i });
    fireEvent.click(editButtons[0]);

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

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

    await waitFor(() => {
      screen.getAllByRole("button", { name: /^edit$/i });
    });
    expect(screen.getAllByRole("button", { name: /^edit$/i }).length).toBeGreaterThan(0);
  });

  it("handles save click with valid data", async () => {
    mockPostApi
      .mockResolvedValueOnce({ Records: fakeConfig })
      .mockResolvedValueOnce({
        Records: [{ Message: "Record updated successfully!" }],
      })
      .mockResolvedValueOnce({ Records: fakeConfig });

    renderWithUser();
    await screen.findAllByText("Field One");

    const editButtons = screen.getAllByRole("button", { name: /^edit$/i });
    fireEvent.click(editButtons[0]);

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

    fireEvent.click(screen.getByRole("button", { name: /^save$/i }));

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

  it("handles bulk edit mode", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    // Select all using master checkbox
    const masterCheckbox = screen.getAllByRole("checkbox")[0];
    fireEvent.click(masterCheckbox);

    fireEvent.click(screen.getByRole("button", { name: /edit selected/i }));

    await waitFor(() => {
      screen.getByRole("button", { name: /save selected/i });
    });

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

  it("handles bulk cancel", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    const masterCheckbox = screen.getAllByRole("checkbox")[0];
    fireEvent.click(masterCheckbox);

    fireEvent.click(screen.getByRole("button", { name: /edit selected/i }));

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

    fireEvent.click(screen.getByRole("button", { name: /cancel editing/i }));

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

  it("handles bulk save with valid data", async () => {
    mockPostApi
      .mockResolvedValueOnce({ Records: fakeConfig })
      .mockResolvedValueOnce({
        Records: [
          { Message: "Record 1 updated!" },
          { Message: "Record 2 updated!" },
        ],
      })
      .mockResolvedValueOnce({ Records: fakeConfig });

    renderWithUser();
    await screen.findAllByText("Field One");

    const checkboxes = screen.getAllByRole("checkbox");
    fireEvent.click(checkboxes[1]);

    fireEvent.click(screen.getByRole("button", { name: /edit selected/i }));

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

    fireEvent.click(screen.getByRole("button", { name: /save selected/i }));

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

  it("deselects checkbox when clicked again", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    const checkboxes = screen.getAllByRole("checkbox");
    fireEvent.click(checkboxes[1]);
    fireEvent.click(checkboxes[1]);

    const editSelectedBtn = screen.getByRole("button", { name: /edit selected/i });
    expect(editSelectedBtn).toBeDisabled();
  });

  it("handles empty config data response", async () => {
    mockPostApi.mockResolvedValueOnce({ Records: [] });
    renderWithUser();
    await waitFor(() => {
      expect(mockPostApi).toHaveBeenCalled();
    });
    expect(screen.getByText("Configuration Editor")).toBeInTheDocument();
  });

  it("handles null response", async () => {
    mockPostApi.mockResolvedValueOnce(null);
    renderWithUser();
    await waitFor(() => {
      expect(mockPostApi).toHaveBeenCalled();
    });
    expect(screen.getByText("Configuration Editor")).toBeInTheDocument();
  });

  it("handles domain error response in bulk save", async () => {
    mockPostApi
      .mockResolvedValueOnce({ Records: fakeConfig })
      .mockResolvedValueOnce({
        Records: [{ Message: "Cannot delete domain because it has users" }],
      })
      .mockResolvedValueOnce({ Records: fakeConfig });

    renderWithUser();
    await screen.findAllByText("Field One");

    const checkboxes = screen.getAllByRole("checkbox");
    fireEvent.click(checkboxes[1]);

    fireEvent.click(screen.getByRole("button", { name: /edit selected/i }));

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

    fireEvent.click(screen.getByRole("button", { name: /save selected/i }));

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

  it("handles input change for different field types", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    const editButtons = screen.getAllByRole("button", { name: /^edit$/i });
    fireEvent.click(editButtons[0]);

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

    const textInputs = screen.getAllByRole("textbox");
    if (textInputs.length > 0) {
      fireEvent.change(textInputs[0], { target: { value: "new value" } });
    }
  });

  it("validates text length - too short", async () => {
    const shortTextConfig = [
      {
        Id: 1,
        Label: "Text Field",
        Type: "text",
        ProValue: "a",
        Editable: true,
        Min: 5,
        Max: 100,
        FieldOrder: 1,
      },
    ];

    mockPostApi.mockResolvedValueOnce({ Records: shortTextConfig });

    renderWithUser();
    await screen.findAllByText("Text Field");

    const editBtn = screen.getByRole("button", { name: /^edit$/i });
    fireEvent.click(editBtn);

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

    fireEvent.click(screen.getByRole("button", { name: /^save$/i }));

    // Should not call API with toast success because validation fails
    await waitFor(() => {
      expect(mockToast.success).not.toHaveBeenCalled();
    });
  });

  it("validates number range - out of range", async () => {
    const outOfRangeConfig = [
      {
        Id: 1,
        Label: "Number Field",
        Type: "number",
        ProValue: "200",
        Editable: true,
        Min: 1,
        Max: 100,
        FieldOrder: 1,
      },
    ];

    mockPostApi.mockResolvedValueOnce({ Records: outOfRangeConfig });

    renderWithUser();
    await screen.findAllByText("Number Field");

    const editBtn = screen.getByRole("button", { name: /^edit$/i });
    fireEvent.click(editBtn);

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

    fireEvent.click(screen.getByRole("button", { name: /^save$/i }));

    await waitFor(() => {
      expect(mockToast.success).not.toHaveBeenCalled();
    });
  });

  it("handles select all using master checkbox", async () => {
    renderWithUser();
    await screen.findAllByText("Field One");

    const masterCheckbox = screen.getAllByRole("checkbox")[0];
    
    // Select all
    fireEvent.click(masterCheckbox);
    expect(screen.getByRole("button", { name: /edit selected/i })).not.toBeDisabled();
    
    // Deselect all
    fireEvent.click(masterCheckbox);
    expect(screen.getByRole("button", { name: /edit selected/i })).toBeDisabled();
  });
});
