import React from "react";
import { render, screen, fireEvent, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import { jest } from "@jest/globals";
import { CreateEditRoleDialog } from "../pages/ManageRoles/CreateEditRoleDialog.js";
import { UserRoleHeirarchy } from "../constants/userStatus.js";
import { permissionTypes } from "../constants/userConstants.js";

// Mock data for testing
const mockRoleData = {
  RoleId: "1",
  RoleName: "Admin",
  ViewType: "admin",
};

const mockPermissions = [
  {
    FeatureSubId: "1",
    SubFeatureName: "User Management",
    selectedPermission: 1,
  },
  {
    FeatureSubId: "2",
    SubFeatureName: "Report Generation",
    selectedPermission: 2,
  },
  {
    FeatureSubId: "3",
    SubFeatureName: "Settings",
    selectedPermission: 0,
  },
];

const defaultProps = {
  open: true,
  onClose: jest.fn(),
  roleData: null,
  showPermissions: false,
  roleName: "",
  handleRoleSave: jest.fn(),
  viewType: "user",
  setRoleName: jest.fn(),
  setViewType: jest.fn(),
  permissions: mockPermissions,
  handlePermissionSave: jest.fn(),
  handlePermissionChange: jest.fn(),
  oldViewtype: "user",
  setShowPermissions: jest.fn(),
};

describe("CreateEditRoleDialog", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("Rendering", () => {
    it("should render the dialog when open is true", () => {
      render(<CreateEditRoleDialog {...defaultProps} />);
      expect(screen.getByRole("dialog")).toBeInTheDocument();
    });

    it("should display 'Create Role' title when roleData is null", () => {
      render(<CreateEditRoleDialog {...defaultProps} />);
      expect(screen.getByText("Create Role")).toBeInTheDocument();
    });

    it("should display 'Edit Role' title when roleData is provided", () => {
      const props = { ...defaultProps, roleData: mockRoleData };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Edit Role")).toBeInTheDocument();
    });

    it("should render close button", () => {
      render(<CreateEditRoleDialog {...defaultProps} />);
      const closeButton = screen.getByLabelText("close");
      expect(closeButton).toBeInTheDocument();
    });

    it("should call onClose when close button is clicked", () => {
      const onCloseMock = jest.fn();
      render(<CreateEditRoleDialog {...defaultProps} onClose={onCloseMock} />);
      fireEvent.click(screen.getByLabelText("close"));
      expect(onCloseMock).toHaveBeenCalled();
    });
  });

  describe("Role Name Input", () => {
    it("should display role name input field", () => {
      render(<CreateEditRoleDialog {...defaultProps} />);
      const input = screen.getByLabelText("Role Name");
      expect(input).toBeInTheDocument();
    });

    it("should update role name when user types", async () => {
      const setRoleNameMock = jest.fn();
      const props = { ...defaultProps, setRoleName: setRoleNameMock };
      render(<CreateEditRoleDialog {...props} />);

      const input = screen.getByLabelText("Role Name");
      fireEvent.change(input, { target: { value: "Manager" } });

      expect(setRoleNameMock).toHaveBeenCalled();
    });

    it("should accept only letters and spaces in role name", async () => {
      const setRoleNameMock = jest.fn((value) => value);
      const props = { ...defaultProps, setRoleName: setRoleNameMock };
      render(<CreateEditRoleDialog {...props} />);

      const input = screen.getByLabelText("Role Name");
      fireEvent.change(input, { target: { value: "Admin123" } });

      // The component should filter out numbers, so "123" shouldn't be in the final value
      expect(setRoleNameMock).not.toHaveBeenCalledWith(
        expect.stringContaining("123")
      );
    });

    it("should replace multiple spaces with single space", async () => {
      const setRoleNameMock = jest.fn();
      const props = { ...defaultProps, setRoleName: setRoleNameMock };
      render(<CreateEditRoleDialog {...props} />);

      const input = screen.getByLabelText("Role Name");
      fireEvent.change(input, { target: { value: "Admin  Manager" } });

      expect(setRoleNameMock).toHaveBeenCalled();
    });

    it("should display info icon when role name is provided", () => {
      const props = { ...defaultProps, roleName: "Admin" };
      render(<CreateEditRoleDialog {...props} />);
      // The info icon is an SVG with aria-label, not role="img"
      const infoIcon = screen.getByLabelText(/Role names can only contain letters/);
      expect(infoIcon).toBeInTheDocument();
    });

    it("should show tooltip on info icon hover", async () => {
      const props = { ...defaultProps, roleName: "Admin" };
      render(<CreateEditRoleDialog {...props} />);
      // The tooltip text is in aria-label, not title
      const infoIcon = screen.getByLabelText(
        /Role names can only contain letters and a single space between words/
      );
      expect(infoIcon).toBeInTheDocument();
    });
  });

  describe("View Type Permission", () => {
    it("should display view type permission radio group", () => {
      render(<CreateEditRoleDialog {...defaultProps} />);
      expect(screen.getByText("View Type Permission")).toBeInTheDocument();
    });

    it("should render all user role hierarchy options", () => {
      render(<CreateEditRoleDialog {...defaultProps} />);
      UserRoleHeirarchy.forEach((role) => {
        expect(screen.getByLabelText(role.value)).toBeInTheDocument();
      });
    });

    it("should update view type when radio option is selected", async () => {
      const setViewTypeMock = jest.fn();
      const props = { ...defaultProps, setViewType: setViewTypeMock };
      render(<CreateEditRoleDialog {...props} />);

      const firstRole = UserRoleHeirarchy[0];
      const radioButton = screen.getByRole("radio", { name: firstRole.value });
      fireEvent.click(radioButton);

      expect(setViewTypeMock).toHaveBeenCalled();
    });

    it("should use roleData ViewType when available", () => {
      const props = { ...defaultProps, roleData: mockRoleData };
      render(<CreateEditRoleDialog {...props} />);

      // Radio options are Full View, Hierarchical View, Individual View - not Admin
      const fullViewRadio = screen.getByRole("radio", { name: /Full View/ });
      // The radio group value should reflect the roleData
      expect(fullViewRadio).toBeInTheDocument();
    });
  });

  describe("Save Button", () => {
    it("should display Save button when creating new role", () => {
      const props = { ...defaultProps, roleData: null };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Save")).toBeInTheDocument();
    });

    it("should display Update button when editing existing role", () => {
      const props = { ...defaultProps, roleData: mockRoleData };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Update")).toBeInTheDocument();
    });

    it("should disable save button when role name is empty for new role", () => {
      const props = { ...defaultProps, roleName: "" };
      render(<CreateEditRoleDialog {...props} />);
      const saveButton = screen.getByRole("button", { name: "Save" });
      expect(saveButton).toBeDisabled();
    });

    it("should enable save button when role name is provided for new role", () => {
      const props = { ...defaultProps, roleName: "Manager" };
      render(<CreateEditRoleDialog {...props} />);
      const saveButton = screen.getByRole("button", { name: "Save" });
      expect(saveButton).not.toBeDisabled();
    });

    it("should disable update button when no changes are made", () => {
      const props = {
        ...defaultProps,
        roleData: mockRoleData,
        roleName: mockRoleData.RoleName,
        viewType: mockRoleData.ViewType,
        oldViewtype: mockRoleData.ViewType,
      };
      render(<CreateEditRoleDialog {...props} />);
      const updateButton = screen.getByRole("button", { name: "Update" });
      expect(updateButton).toBeDisabled();
    });

    it("should enable update button when role name changes", () => {
      const props = {
        ...defaultProps,
        roleData: mockRoleData,
        roleName: "New Manager Name",
        oldViewtype: mockRoleData.ViewType,
      };
      render(<CreateEditRoleDialog {...props} />);
      const updateButton = screen.getByRole("button", { name: "Update" });
      expect(updateButton).not.toBeDisabled();
    });

    it("should enable update button when view type changes", () => {
      const props = {
        ...defaultProps,
        roleData: mockRoleData,
        roleName: mockRoleData.RoleName,
        viewType: "different",
        oldViewtype: mockRoleData.ViewType,
      };
      render(<CreateEditRoleDialog {...props} />);
      const updateButton = screen.getByRole("button", { name: "Update" });
      expect(updateButton).not.toBeDisabled();
    });

    it("should call handleRoleSave when save button is clicked", async () => {
      const handleRoleSaveMock = jest.fn();
      const props = {
        ...defaultProps,
        roleName: "Manager",
        handleRoleSave: handleRoleSaveMock,
      };
      render(<CreateEditRoleDialog {...props} />);

      const saveButton = screen.getByRole("button", { name: "Save" });
      fireEvent.click(saveButton);

      expect(handleRoleSaveMock).toHaveBeenCalled();
    });
  });

  describe("Edit Permissions Button", () => {
    it("should not display edit permissions button for new role", () => {
      const props = { ...defaultProps, roleData: null };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.queryByText("EDIT PERMISSIONS")).not.toBeInTheDocument();
    });

    it("should display edit permissions button for existing role with RoleId", () => {
      const props = { ...defaultProps, roleData: mockRoleData };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("EDIT PERMISSIONS")).toBeInTheDocument();
    });

    it("should display edit permissions button for existing role with Role_Id", () => {
      const roleDataWithUnderscoreId = { Role_Id: "1", RoleName: "Admin" };
      const props = {
        ...defaultProps,
        roleData: roleDataWithUnderscoreId,
      };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("EDIT PERMISSIONS")).toBeInTheDocument();
    });

    it("should call setShowPermissions when edit permissions button is clicked", async () => {
      const setShowPermissionsMock = jest.fn();
      const props = {
        ...defaultProps,
        roleData: mockRoleData,
        setShowPermissions: setShowPermissionsMock,
      };
      render(<CreateEditRoleDialog {...props} />);

      const editButton = screen.getByText("EDIT PERMISSIONS");
      fireEvent.click(editButton);

      expect(setShowPermissionsMock).toHaveBeenCalledWith(true);
    });

    it("should have hover styling on edit permissions button", async () => {
      const props = { ...defaultProps, roleData: mockRoleData };
      render(<CreateEditRoleDialog {...props} />);

      const editButton = screen.getByText("EDIT PERMISSIONS");
      fireEvent.mouseEnter(editButton);
      fireEvent.mouseLeave(editButton);

      // Button should still be in document after hover
      expect(editButton).toBeInTheDocument();
    });
  });

  describe("Permissions View", () => {
    it("should not display permissions container when showPermissions is false", () => {
      const props = { ...defaultProps, showPermissions: false };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.queryByText("Update Permissions")).not.toBeInTheDocument();
    });

    it("should display permissions container when showPermissions is true", () => {
      const props = { ...defaultProps, showPermissions: true };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Update Permissions")).toBeInTheDocument();
    });

    it("should render all permission modules", () => {
      const props = { ...defaultProps, showPermissions: true };
      render(<CreateEditRoleDialog {...props} />);

      mockPermissions.forEach((permission) => {
        expect(screen.getByText(permission.SubFeatureName)).toBeInTheDocument();
      });
    });

    it("should render permission type radio buttons for each module", () => {
      const props = { ...defaultProps, showPermissions: true };
      render(<CreateEditRoleDialog {...props} />);

      // Should render radio buttons for all permission types for each module
      permissionTypes.forEach((type) => {
        const buttons = screen.getAllByRole("radio", { name: type.value });
        expect(buttons.length).toBeGreaterThanOrEqual(mockPermissions.length);
      });
    });

    it("should call handlePermissionChange when permission radio is clicked", async () => {
      const handlePermissionChangeMock = jest.fn();
      const props = {
        ...defaultProps,
        showPermissions: true,
        handlePermissionChange: handlePermissionChangeMock,
      };
      render(<CreateEditRoleDialog {...props} />);

      const firstPermissionRadios = screen.getAllByRole("radio").slice(0, 3);
      fireEvent.click(firstPermissionRadios[1]);

      expect(handlePermissionChangeMock).toHaveBeenCalled();
    });

    it("should call handlePermissionSave when update permissions button is clicked", () => {
      const handlePermissionSaveMock = jest.fn();
      const props = {
        ...defaultProps,
        showPermissions: true,
        handlePermissionSave: handlePermissionSaveMock,
      };
      render(<CreateEditRoleDialog {...props} />);

      const updateButton = screen.getByRole("button", {
        name: "Update Permissions",
      });
      fireEvent.click(updateButton);

      expect(handlePermissionSaveMock).toHaveBeenCalled();
    });

    it("should display permission module name for each permission", () => {
      const props = { ...defaultProps, showPermissions: true };
      render(<CreateEditRoleDialog {...props} />);

      expect(screen.getByText("User Management")).toBeInTheDocument();
      expect(screen.getByText("Report Generation")).toBeInTheDocument();
      expect(screen.getByText("Settings")).toBeInTheDocument();
    });
  });

  describe("Dialog Behavior", () => {
    it("should toggle between role view and permissions view", async () => {
      const setShowPermissionsMock = jest.fn();
      const props = {
        ...defaultProps,
        roleData: mockRoleData,
        setShowPermissions: setShowPermissionsMock,
      };
      const { rerender } = render(<CreateEditRoleDialog {...props} />);

      // Initially in role view
      expect(screen.getByText("EDIT PERMISSIONS")).toBeInTheDocument();

      // Click edit permissions
      fireEvent.click(screen.getByText("EDIT PERMISSIONS"));
      expect(setShowPermissionsMock).toHaveBeenCalledWith(true);

      // Rerender with showPermissions true
      rerender(
        <CreateEditRoleDialog {...props} showPermissions={true} />
      );
      expect(screen.getByText("Update Permissions")).toBeInTheDocument();
    });

    it("should preserve dialog title when switching views", async () => {
      const props = {
        ...defaultProps,
        roleData: mockRoleData,
        showPermissions: false,
      };
      const { rerender } = render(<CreateEditRoleDialog {...props} />);

      expect(screen.getByText("Edit Role")).toBeInTheDocument();

      rerender(<CreateEditRoleDialog {...props} showPermissions={true} />);
      expect(screen.getByText("Edit Role")).toBeInTheDocument();
    });
  });

  describe("Edge Cases", () => {
    it("should handle Role_Id when RoleId is not present", () => {
      const roleDataWithUnderscoreId = {
        Role_Id: "1",
        RoleName: "Admin",
        ViewType: "admin",
      };
      const props = {
        ...defaultProps,
        roleData: roleDataWithUnderscoreId,
      };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Edit Role")).toBeInTheDocument();
    });

    it("should handle empty permissions array", () => {
      const props = {
        ...defaultProps,
        showPermissions: true,
        permissions: [],
      };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Update Permissions")).toBeInTheDocument();
    });

    it("should handle undefined selectedPermission", () => {
      const permissionsWithUndefined = [
        {
          FeatureSubId: "1",
          SubFeatureName: "Test Module",
          selectedPermission: undefined,
        },
      ];
      const props = {
        ...defaultProps,
        showPermissions: true,
        permissions: permissionsWithUndefined,
      };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Test Module")).toBeInTheDocument();
    });

    it("should handle whitespace-only role name as empty", () => {
      const props = { ...defaultProps, roleName: "   " };
      render(<CreateEditRoleDialog {...props} />);
      const saveButton = screen.getByRole("button", { name: "Save" });
      expect(saveButton).toBeDisabled();
    });

    it("should handle when oldViewtype is not provided", () => {
      const props = {
        ...defaultProps,
        roleData: mockRoleData,
        oldViewtype: undefined,
      };
      render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("Edit Role")).toBeInTheDocument();
    });
  });

  describe("PropTypes Validation", () => {
    it("should have required prop-types defined", () => {
      const { propTypes } = CreateEditRoleDialog;
      expect(propTypes.open).toBeDefined();
      expect(propTypes.onClose).toBeDefined();
      expect(propTypes.roleName).toBeDefined();
      expect(propTypes.showPermissions).toBeDefined();
      expect(propTypes.viewType).toBeDefined();
      expect(propTypes.permissions).toBeDefined();
      expect(propTypes.handleRoleSave).toBeDefined();
      expect(propTypes.handlePermissionSave).toBeDefined();
      expect(propTypes.handlePermissionChange).toBeDefined();
      expect(propTypes.setRoleName).toBeDefined();
      expect(propTypes.setViewType).toBeDefined();
      expect(propTypes.setShowPermissions).toBeDefined();
    });
  });

  describe("Input Validation", () => {
    it("should prevent saving with empty trimmed name for new role", () => {
      const props = { ...defaultProps, roleName: "   " };
      render(<CreateEditRoleDialog {...props} />);
      const saveButton = screen.getByRole("button", { name: "Save" });
      expect(saveButton).toBeDisabled();
    });

    it("should allow saving with valid name for new role", () => {
      const props = { ...defaultProps, roleName: "Valid Role Name" };
      render(<CreateEditRoleDialog {...props} />);
      const saveButton = screen.getByRole("button", { name: "Save" });
      expect(saveButton).not.toBeDisabled();
    });

    it("should validate both RoleId and Role_Id for identifying existing role", () => {
      const roleWithRoleId = { ...mockRoleData, RoleId: "123" };
      const props = { ...defaultProps, roleData: roleWithRoleId };
      const { unmount: unmount1 } = render(<CreateEditRoleDialog {...props} />);
      expect(screen.getByText("EDIT PERMISSIONS")).toBeInTheDocument();
      unmount1();

      const roleWithRole_Id = { Role_Id: "456", RoleName: "Test" };
      const propsWithUnderscore = {
        ...defaultProps,
        roleData: roleWithRole_Id,
      };
      render(<CreateEditRoleDialog {...propsWithUnderscore} />);
      expect(screen.getByText("EDIT PERMISSIONS")).toBeInTheDocument();
    });
  });
});