import React, {
  FunctionComponent,
  ReactElement,
  useEffect,
  useState,
  useCallback,
} from "react";
import ObjectID from "Common/Types/ObjectID";
import Color from "Common/Types/Color";
import IconProp from "Common/Types/Icon/IconProp";
import ModelAPI, { ListResult } from "Common/UI/Utils/ModelAPI/ModelAPI";
import IncidentRole from "Common/Models/DatabaseModels/IncidentRole";
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import ProjectUtil from "Common/UI/Utils/Project";
import ComponentLoader from "Common/UI/Components/ComponentLoader/ComponentLoader";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import API from "Common/UI/Utils/API/API";
import Dropdown, {
  DropdownOption,
  DropdownValue,
} from "Common/UI/Components/Dropdown/Dropdown";
import RoleLabel from "Common/UI/Components/RoleLabel/RoleLabel";
import Icon from "Common/UI/Components/Icon/Icon";
import ProjectUser from "../../Utils/ProjectUser";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";

export interface RoleAssignment {
  roleId: string;
  userIds: string[];
}

export interface IncidentEpisodeRoleFormFieldProps {
  onChange?: ((value: Array<RoleAssignment>) => void) | undefined;
  initialValue?: Array<RoleAssignment> | undefined;
}

interface RoleData {
  id: ObjectID;
  name: string;
  color: Color;
  icon?: IconProp;
  canAssignMultipleUsers: boolean;
  isPrimaryRole: boolean;
}

const IncidentEpisodeRoleFormField: FunctionComponent<
  IncidentEpisodeRoleFormFieldProps
> = (props: IncidentEpisodeRoleFormFieldProps): ReactElement => {
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [error, setError] = useState<string>("");
  const [roles, setRoles] = useState<Array<RoleData>>([]);
  const [userOptions, setUserOptions] = useState<Array<DropdownOption>>([]);
  const [assignments, setAssignments] = useState<Array<RoleAssignment>>(
    props.initialValue || [],
  );

  const fetchRolesAndUsers: () => Promise<void> =
    useCallback(async (): Promise<void> => {
      setIsLoading(true);
      setError("");

      try {
        const projectId: ObjectID | null = ProjectUtil.getCurrentProjectId();

        if (!projectId) {
          setError("Project not found");
          setIsLoading(false);
          return;
        }

        // Fetch incident roles (same roles used for incidents and episodes)
        const rolesResult: ListResult<IncidentRole> =
          await ModelAPI.getList<IncidentRole>({
            modelType: IncidentRole,
            query: {
              projectId: projectId,
            },
            limit: LIMIT_PER_PROJECT,
            skip: 0,
            select: {
              _id: true,
              name: true,
              color: true,
              roleIcon: true,
              canAssignMultipleUsers: true,
              isPrimaryRole: true,
            },
            sort: {
              name: SortOrder.Ascending,
            },
          });

        const roleData: Array<RoleData> = rolesResult.data
          .map((role: IncidentRole): RoleData => {
            const data: RoleData = {
              id: role.id!,
              name: role.name || "",
              color: role.color || new Color("#000000"),
              canAssignMultipleUsers: role.canAssignMultipleUsers || false,
              isPrimaryRole: role.isPrimaryRole || false,
            };

            if (role.roleIcon) {
              data.icon = role.roleIcon;
            }

            return data;
          })
          .sort((a: RoleData, b: RoleData) => {
            // Primary roles first, then sort by name
            if (a.isPrimaryRole && !b.isPrimaryRole) {
              return -1;
            }
            if (!a.isPrimaryRole && b.isPrimaryRole) {
              return 1;
            }
            return a.name.localeCompare(b.name);
          });

        setRoles(roleData);

        // Fetch project users
        const users: Array<DropdownOption> =
          await ProjectUser.fetchProjectUsersAsDropdownOptions(projectId);
        setUserOptions(users);
      } catch (err) {
        setError(API.getFriendlyMessage(err));
      }

      setIsLoading(false);
    }, []);

  useEffect(() => {
    fetchRolesAndUsers();
  }, [fetchRolesAndUsers]);

  const updateAssignment: (roleId: string, userIds: string[]) => void =
    useCallback(
      (roleId: string, userIds: string[]): void => {
        setAssignments((prevAssignments: Array<RoleAssignment>) => {
          const existingIndex: number = prevAssignments.findIndex(
            (a: RoleAssignment) => {
              return a.roleId === roleId;
            },
          );

          let newAssignments: Array<RoleAssignment>;

          if (userIds.length === 0) {
            // Remove the assignment if no users selected
            newAssignments = prevAssignments.filter((a: RoleAssignment) => {
              return a.roleId !== roleId;
            });
          } else if (existingIndex >= 0) {
            // Update existing assignment
            newAssignments = [...prevAssignments];
            newAssignments[existingIndex] = { roleId, userIds };
          } else {
            // Add new assignment
            newAssignments = [...prevAssignments, { roleId, userIds }];
          }

          // Notify parent of change
          if (props.onChange) {
            props.onChange(newAssignments);
          }

          return newAssignments;
        });
      },
      [props.onChange],
    );

  const getSelectedUsersForRole: (roleId: string) => string[] = useCallback(
    (roleId: string): string[] => {
      const assignment: RoleAssignment | undefined = assignments.find(
        (a: RoleAssignment) => {
          return a.roleId === roleId;
        },
      );
      return assignment?.userIds || [];
    },
    [assignments],
  );

  const removeUserFromRole: (roleId: string, userId: string) => void =
    useCallback(
      (roleId: string, userId: string): void => {
        const currentUsers: string[] = getSelectedUsersForRole(roleId);
        const newUsers: string[] = currentUsers.filter((id: string) => {
          return id !== userId;
        });
        updateAssignment(roleId, newUsers);
      },
      [getSelectedUsersForRole, updateAssignment],
    );

  if (isLoading) {
    return <ComponentLoader />;
  }

  if (error) {
    return <ErrorMessage message={error} />;
  }

  if (roles.length === 0) {
    return (
      <p className="text-gray-500">
        No incident roles defined. Go to Incidents {">"} Settings {">"} Roles to
        create roles.
      </p>
    );
  }

  return (
    <div className="space-y-4">
      {roles.map((role: RoleData) => {
        const selectedUsers: string[] = getSelectedUsersForRole(
          role.id.toString(),
        );
        const canAddMore: boolean =
          role.canAssignMultipleUsers || selectedUsers.length === 0;

        return (
          <div
            key={role.id.toString()}
            className="border border-gray-200 rounded-lg p-4"
          >
            <div className="flex items-center justify-between mb-3">
              <div className="flex items-center space-x-2">
                <RoleLabel
                  name={role.name}
                  color={role.color}
                  icon={role.icon}
                />
                {role.isPrimaryRole && (
                  <span className="text-xs bg-indigo-100 text-indigo-700 px-2 py-0.5 rounded font-medium">
                    Primary
                  </span>
                )}
                {role.canAssignMultipleUsers && (
                  <span className="text-xs bg-gray-100 text-gray-600 px-2 py-0.5 rounded">
                    Multiple
                  </span>
                )}
              </div>
            </div>

            {/* Selected users */}
            {selectedUsers.length > 0 && (
              <div className="mb-3 flex flex-wrap gap-2">
                {selectedUsers.map((userId: string) => {
                  const userOption: DropdownOption | undefined =
                    userOptions.find((opt: DropdownOption) => {
                      return opt.value === userId;
                    });
                  return (
                    <div
                      key={userId}
                      className="flex items-center bg-gray-100 rounded-full px-3 py-1 text-sm"
                    >
                      <span>{userOption?.label || "Unknown User"}</span>
                      <button
                        type="button"
                        onClick={() => {
                          removeUserFromRole(role.id.toString(), userId);
                        }}
                        className="ml-2 text-gray-500 hover:text-gray-700"
                      >
                        <Icon icon={IconProp.Close} className="h-3 w-3" />
                      </button>
                    </div>
                  );
                })}
              </div>
            )}

            {/* User selection dropdown */}
            {canAddMore && (
              <div className="flex items-center gap-2">
                <div className="flex-1">
                  <Dropdown
                    placeholder={`Select user for ${role.name}`}
                    options={userOptions.filter((opt: DropdownOption) => {
                      // Filter out already selected users
                      return !selectedUsers.includes(opt.value as string);
                    })}
                    value={undefined}
                    onChange={(
                      value: DropdownValue | Array<DropdownValue> | null,
                    ) => {
                      if (value && typeof value === "string") {
                        const newUsers: string[] = [...selectedUsers, value];
                        updateAssignment(role.id.toString(), newUsers);
                      }
                    }}
                  />
                </div>
              </div>
            )}

            {!canAddMore && (
              <p className="text-xs text-gray-500">
                Only one user can be assigned to this role.
              </p>
            )}
          </div>
        );
      })}
    </div>
  );
};

export default IncidentEpisodeRoleFormField;
