import React, { useEffect, useMemo, useState } from "react";
import Team from "../../../Models/DatabaseModels/Team";
import TeamMember from "../../../Models/DatabaseModels/TeamMember";
import BadDataException from "../../../Types/Exception/BadDataException";
import IconProp from "../../../Types/Icon/IconProp";
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
import Includes from "../../../Types/BaseDatabase/Includes";
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
import ObjectID from "../../../Types/ObjectID";
import API from "../../Utils/API/API";
import ModelAPI from "../../Utils/ModelAPI/ModelAPI";
import ProjectUtil from "../../Utils/Project";
import { ButtonStyleType } from "../Button/Button";
import BasicFormModal from "../FormModal/BasicFormModal";
import FormFieldSchemaType from "../Forms/Types/FormFieldSchemaType";
const USER_PREFIX = "user:";
const TEAM_PREFIX = "team:";
/**
 * Reusable hook that provides "Add Owner" and "Remove Owner" bulk actions
 * for any ModelTable whose model has companion `<Resource>OwnerUser` and
 * `<Resource>OwnerTeam` junction tables (e.g., `ServiceOwnerUser` +
 * `ServiceOwnerTeam` with `serviceId` as the foreign key).
 *
 * Usage:
 *   const { bulkActions, modals } = useBulkOwnerActions<Service>({
 *     ownerUserModelType: ServiceOwnerUser,
 *     ownerTeamModelType: ServiceOwnerTeam,
 *     resourceIdField: "serviceId",
 *   });
 *   <ModelTable bulkActions={{ buttons: [...bulkActions, ...] }} />
 *   {modals}
 */
function useBulkOwnerActions(config) {
    const [userOptions, setUserOptions] = useState([]);
    const [teamOptions, setTeamOptions] = useState([]);
    const [showAddModal, setShowAddModal] = useState(false);
    const [showRemoveModal, setShowRemoveModal] = useState(false);
    const [bulkActionProps, setBulkActionProps] = useState(null);
    useEffect(() => {
        const projectId = ProjectUtil.getCurrentProjectId();
        if (!projectId) {
            return;
        }
        const fetchOwners = async () => {
            var _a, _b, _c, _d, _e, _f;
            try {
                const [teamMembersResult, teamsResult] = await Promise.all([
                    ModelAPI.getList({
                        modelType: TeamMember,
                        query: { projectId: projectId },
                        limit: LIMIT_PER_PROJECT,
                        skip: 0,
                        select: {
                            _id: true,
                            user: {
                                _id: true,
                                name: true,
                                email: true,
                            },
                        },
                        sort: {},
                    }),
                    ModelAPI.getList({
                        modelType: Team,
                        query: { projectId: projectId },
                        limit: LIMIT_PER_PROJECT,
                        skip: 0,
                        select: {
                            _id: true,
                            name: true,
                        },
                        sort: {
                            name: SortOrder.Ascending,
                        },
                    }),
                ]);
                const seenUserIds = new Set();
                const users = [];
                for (const member of teamMembersResult.data) {
                    const userId = ((_b = (_a = member.user) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString()) || "";
                    if (!userId || seenUserIds.has(userId)) {
                        continue;
                    }
                    seenUserIds.add(userId);
                    users.push({
                        value: `${USER_PREFIX}${userId}`,
                        label: ((_d = (_c = member.user) === null || _c === void 0 ? void 0 : _c.name) === null || _d === void 0 ? void 0 : _d.toString()) ||
                            ((_f = (_e = member.user) === null || _e === void 0 ? void 0 : _e.email) === null || _f === void 0 ? void 0 : _f.toString()) ||
                            "",
                    });
                }
                users.sort((a, b) => {
                    return a.label.toLowerCase().localeCompare(b.label.toLowerCase());
                });
                setUserOptions(users);
                setTeamOptions(teamsResult.data.map((team) => {
                    var _a, _b;
                    return {
                        value: `${TEAM_PREFIX}${((_a = team._id) === null || _a === void 0 ? void 0 : _a.toString()) || ""}`,
                        label: ((_b = team.name) === null || _b === void 0 ? void 0 : _b.toString()) || "",
                    };
                }));
            }
            catch (_g) {
                // dropdowns will remain empty; modal will show no options
            }
        };
        void fetchOwners();
    }, []);
    const applyOwners = async (selectedKeys, mode) => {
        if (!bulkActionProps) {
            return;
        }
        const { items, onProgressInfo, onBulkActionStart, onBulkActionEnd } = bulkActionProps;
        // Close the form modal first so the progress modal is visible
        setShowAddModal(false);
        setShowRemoveModal(false);
        const userIds = [];
        const teamIds = [];
        for (const key of selectedKeys) {
            if (key.startsWith(USER_PREFIX)) {
                const id = key.slice(USER_PREFIX.length);
                if (id) {
                    userIds.push(id);
                }
            }
            else if (key.startsWith(TEAM_PREFIX)) {
                const id = key.slice(TEAM_PREFIX.length);
                if (id) {
                    teamIds.push(id);
                }
            }
        }
        const projectId = ProjectUtil.getCurrentProjectId();
        onBulkActionStart();
        const totalItems = [...items];
        const inProgressItems = [...items];
        const successItems = [];
        const failedItems = [];
        for (const item of totalItems) {
            inProgressItems.splice(inProgressItems.indexOf(item), 1);
            try {
                if (!item.id) {
                    throw new BadDataException("Item ID not found");
                }
                if (!projectId) {
                    throw new BadDataException("Project not found");
                }
                if (mode === "add") {
                    const fetchUsers = userIds.length > 0
                        ? ModelAPI.getList({
                            modelType: config.ownerUserModelType,
                            query: {
                                [config.resourceIdField]: item.id,
                                projectId: projectId,
                            },
                            limit: LIMIT_PER_PROJECT,
                            skip: 0,
                            select: { userId: true },
                            sort: {},
                        }).then((r) => {
                            return r.data;
                        })
                        : Promise.resolve([]);
                    const fetchTeams = teamIds.length > 0
                        ? ModelAPI.getList({
                            modelType: config.ownerTeamModelType,
                            query: {
                                [config.resourceIdField]: item.id,
                                projectId: projectId,
                            },
                            limit: LIMIT_PER_PROJECT,
                            skip: 0,
                            select: { teamId: true },
                            sort: {},
                        }).then((r) => {
                            return r.data;
                        })
                        : Promise.resolve([]);
                    const [existingUsers, existingTeams] = await Promise.all([fetchUsers, fetchTeams]);
                    const existingUserIds = new Set(existingUsers
                        .map((row) => {
                        var _a;
                        return (((_a = row.userId) === null || _a === void 0 ? void 0 : _a.toString()) || "");
                    })
                        .filter((id) => {
                        return id.length > 0;
                    }));
                    const existingTeamIds = new Set(existingTeams
                        .map((row) => {
                        var _a;
                        return (((_a = row.teamId) === null || _a === void 0 ? void 0 : _a.toString()) || "");
                    })
                        .filter((id) => {
                        return id.length > 0;
                    }));
                    for (const userId of userIds) {
                        if (existingUserIds.has(userId)) {
                            continue;
                        }
                        const ownerModel = new config.ownerUserModelType();
                        ownerModel["userId"] =
                            new ObjectID(userId);
                        ownerModel[config.resourceIdField] = item.id;
                        ownerModel["projectId"] =
                            projectId;
                        await ModelAPI.create({
                            model: ownerModel,
                            modelType: config.ownerUserModelType,
                        });
                    }
                    for (const teamId of teamIds) {
                        if (existingTeamIds.has(teamId)) {
                            continue;
                        }
                        const ownerModel = new config.ownerTeamModelType();
                        ownerModel["teamId"] =
                            new ObjectID(teamId);
                        ownerModel[config.resourceIdField] = item.id;
                        ownerModel["projectId"] =
                            projectId;
                        await ModelAPI.create({
                            model: ownerModel,
                            modelType: config.ownerTeamModelType,
                        });
                    }
                }
                else {
                    const fetchMatchingUsers = userIds.length > 0
                        ? ModelAPI.getList({
                            modelType: config.ownerUserModelType,
                            query: {
                                [config.resourceIdField]: item.id,
                                projectId: projectId,
                                userId: new Includes(userIds.map((id) => {
                                    return new ObjectID(id);
                                })),
                            },
                            limit: LIMIT_PER_PROJECT,
                            skip: 0,
                            select: { _id: true },
                            sort: {},
                        }).then((r) => {
                            return r.data;
                        })
                        : Promise.resolve([]);
                    const fetchMatchingTeams = teamIds.length > 0
                        ? ModelAPI.getList({
                            modelType: config.ownerTeamModelType,
                            query: {
                                [config.resourceIdField]: item.id,
                                projectId: projectId,
                                teamId: new Includes(teamIds.map((id) => {
                                    return new ObjectID(id);
                                })),
                            },
                            limit: LIMIT_PER_PROJECT,
                            skip: 0,
                            select: { _id: true },
                            sort: {},
                        }).then((r) => {
                            return r.data;
                        })
                        : Promise.resolve([]);
                    const [matchingUsers, matchingTeams] = await Promise.all([fetchMatchingUsers, fetchMatchingTeams]);
                    for (const row of matchingUsers) {
                        if (!row.id) {
                            continue;
                        }
                        await ModelAPI.deleteItem({
                            modelType: config.ownerUserModelType,
                            id: row.id,
                        });
                    }
                    for (const row of matchingTeams) {
                        if (!row.id) {
                            continue;
                        }
                        await ModelAPI.deleteItem({
                            modelType: config.ownerTeamModelType,
                            id: row.id,
                        });
                    }
                }
                successItems.push(item);
            }
            catch (err) {
                failedItems.push({
                    item: item,
                    failedMessage: API.getFriendlyMessage(err),
                });
            }
            onProgressInfo({
                totalItems: totalItems,
                failed: failedItems,
                successItems: successItems,
                inProgressItems: inProgressItems,
            });
        }
        onBulkActionEnd();
        setBulkActionProps(null);
    };
    const groupedOwnerOptions = useMemo(() => {
        const groups = [];
        if (userOptions.length > 0) {
            groups.push({ label: "People", options: userOptions });
        }
        if (teamOptions.length > 0) {
            groups.push({ label: "Teams", options: teamOptions });
        }
        return groups;
    }, [userOptions, teamOptions]);
    const addOwnerAction = {
        title: "Add Owner",
        buttonStyleType: ButtonStyleType.NORMAL,
        icon: IconProp.UserPlus,
        onClick: async (actionProps) => {
            setBulkActionProps(actionProps);
            setShowAddModal(true);
        },
    };
    const removeOwnerAction = {
        title: "Remove Owner",
        buttonStyleType: ButtonStyleType.NORMAL,
        icon: IconProp.UserMinus,
        onClick: async (actionProps) => {
            setBulkActionProps(actionProps);
            setShowRemoveModal(true);
        },
    };
    const modals = (React.createElement(React.Fragment, null,
        showAddModal && (React.createElement(BasicFormModal, { title: "Add Owner", description: "Select users and/or teams to add as owners to the selected items. Existing owners will be preserved.", onClose: () => {
                setShowAddModal(false);
                setBulkActionProps(null);
            }, submitButtonText: "Add Owner", onSubmit: async (formData) => {
                await applyOwners(formData.ownerKeys || [], "add");
            }, formProps: {
                fields: [
                    {
                        field: {
                            ownerKeys: true,
                        },
                        title: "Select Owners",
                        description: "These users and teams will be added as owners to each selected item.",
                        fieldType: FormFieldSchemaType.MultiSelectDropdown,
                        required: true,
                        dropdownOptions: groupedOwnerOptions,
                    },
                ],
            } })),
        showRemoveModal && (React.createElement(BasicFormModal, { title: "Remove Owner", description: "Select users and/or teams to remove from the selected items. Items that do not have any of these owners will be skipped.", onClose: () => {
                setShowRemoveModal(false);
                setBulkActionProps(null);
            }, submitButtonText: "Remove Owner", onSubmit: async (formData) => {
                await applyOwners(formData.ownerKeys || [], "remove");
            }, formProps: {
                fields: [
                    {
                        field: {
                            ownerKeys: true,
                        },
                        title: "Select Owners",
                        description: "These users and teams will be removed as owners from each selected item.",
                        fieldType: FormFieldSchemaType.MultiSelectDropdown,
                        required: true,
                        dropdownOptions: groupedOwnerOptions,
                    },
                ],
            } }))));
    return {
        bulkActions: [addOwnerAction, removeOwnerAction],
        modals: modals,
    };
}
export default useBulkOwnerActions;
//# sourceMappingURL=BulkOwnerActions.js.map