import { z } from 'zod';
import { globalOptionsZod } from '../../../../Command.js';
import request from '../../../../request.js';
import { entraGroup } from '../../../../utils/entraGroup.js';
import { formatting } from '../../../../utils/formatting.js';
import { planner } from '../../../../utils/planner.js';
import { validation } from '../../../../utils/validation.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';
export const options = z.object({
    ...globalOptionsZod.shape,
    id: z.string().optional().alias('i'),
    title: z.string().optional().alias('t'),
    ownerGroupId: z.string()
        .refine(val => validation.isValidGuid(val), {
        message: 'The value is not a valid GUID.'
    })
        .optional(),
    ownerGroupName: z.string().optional(),
    rosterId: z.string().optional(),
    newTitle: z.string().optional(),
    shareWithUserIds: z.string()
        .refine(val => validation.isValidGuidArray(val) === true, {
        message: 'The value contains invalid GUIDs.'
    })
        .optional(),
    shareWithUserNames: z.string()
        .refine(val => validation.isValidUserPrincipalNameArray(val) === true, {
        message: 'The value contains invalid user principal names.'
    })
        .optional()
}).catchall(z.unknown());
class PlannerPlanSetCommand extends GraphCommand {
    get name() {
        return commands.PLAN_SET;
    }
    get description() {
        return 'Updates a Microsoft Planner plan';
    }
    get schema() {
        return options;
    }
    getRefinedSchema(schema) {
        return schema
            .refine(opts => [opts.id, opts.title, opts.rosterId].filter(x => x !== undefined).length === 1, {
            message: `Specify exactly one of the following options: 'id', 'title' or 'rosterId'.`,
            params: {
                customCode: 'optionSet',
                options: ['id', 'title', 'rosterId']
            }
        })
            .refine(opts => !opts.title || [opts.ownerGroupId, opts.ownerGroupName].filter(x => x !== undefined).length === 1, {
            message: `Specify exactly one of the following options: 'ownerGroupId' or 'ownerGroupName'.`,
            params: {
                customCode: 'optionSet',
                options: ['ownerGroupId', 'ownerGroupName']
            }
        })
            .refine(opts => !opts.shareWithUserIds || !opts.shareWithUserNames, {
            message: 'Specify either shareWithUserIds or shareWithUserNames but not both'
        })
            .refine(opts => {
            const allowedCategories = Array.from({ length: 25 }, (_, i) => `category${i + 1}`);
            const keys = Object.keys(opts);
            const hasCategoryKey = keys.some(key => key.indexOf('category') !== -1);
            if (!hasCategoryKey) {
                return true;
            }
            return keys.filter(key => key.indexOf('category') !== -1).every(key => allowedCategories.indexOf(key) !== -1);
        }, {
            message: 'Specify category values between category1 to category25'
        });
    }
    allowUnknownOptions() {
        return true;
    }
    async getGroupId(args) {
        const { ownerGroupId, ownerGroupName } = args.options;
        if (ownerGroupId) {
            return ownerGroupId;
        }
        const id = await entraGroup.getGroupIdByDisplayName(ownerGroupName);
        return id;
    }
    async getPlanId(args) {
        const { id, title } = args.options;
        if (id) {
            return id;
        }
        if (args.options.rosterId) {
            const id = await planner.getPlanIdByRosterId(args.options.rosterId);
            return id;
        }
        else {
            const groupId = await this.getGroupId(args);
            const id = await planner.getPlanIdByTitle(title, groupId);
            return id;
        }
    }
    async getUserIds(options) {
        if (options.shareWithUserIds) {
            return options.shareWithUserIds.split(',');
        }
        const userNames = options.shareWithUserNames;
        const userArr = userNames.split(',').map(o => o.trim());
        const promises = userArr.map(user => {
            const requestOptions = {
                url: `${this.resource}/v1.0/users?$filter=userPrincipalName eq '${formatting.encodeQueryParameter(user)}'&$select=id,userPrincipalName`,
                headers: {
                    'content-type': 'application/json'
                },
                responseType: 'json'
            };
            return request.get(requestOptions);
        });
        const usersRes = await Promise.all(promises);
        const userUpns = usersRes.map(res => res.value[0]?.userPrincipalName);
        const userIds = usersRes.map(res => res.value[0]?.id);
        // Find the members where no graph response was found
        const invalidUsers = userArr.filter(user => !userUpns.some((upn) => upn?.toLowerCase() === user.toLowerCase()));
        if (invalidUsers && invalidUsers.length > 0) {
            throw `Cannot proceed with planner plan creation. The following users provided are invalid: ${invalidUsers.join(',')}`;
        }
        return userIds;
    }
    async generateSharedWith(options) {
        const sharedWith = {};
        const userIds = await this.getUserIds(options);
        userIds.map(x => sharedWith[x] = true);
        return sharedWith;
    }
    async getPlanEtag(planId) {
        const requestOptions = {
            url: `${this.resource}/v1.0/planner/plans/${planId}`,
            headers: {
                accept: 'application/json'
            },
            responseType: 'json'
        };
        const response = await request.get(requestOptions);
        return response['@odata.etag'];
    }
    async getPlanDetailsEtag(planId) {
        const requestOptions = {
            url: `${this.resource}/v1.0/planner/plans/${planId}/details`,
            headers: {
                accept: 'application/json'
            },
            responseType: 'json'
        };
        const response = await request.get(requestOptions);
        return response['@odata.etag'];
    }
    async getPlanDetails(plan) {
        const requestOptionsTaskDetails = {
            url: `${this.resource}/v1.0/planner/plans/${plan.id}/details`,
            headers: {
                'accept': 'application/json;odata.metadata=none',
                'Prefer': 'return=representation'
            },
            responseType: 'json'
        };
        const planDetails = await request.get(requestOptionsTaskDetails);
        return { ...plan, ...planDetails };
    }
    async updatePlanDetails(options, planId) {
        const plan = await planner.getPlanById(planId);
        const categories = {};
        let categoriesCount = 0;
        Object.keys(options).forEach(key => {
            if (key.indexOf('category') !== -1) {
                categories[key] = options[key];
                categoriesCount++;
            }
        });
        if (!options.shareWithUserIds && !options.shareWithUserNames && categoriesCount === 0) {
            return this.getPlanDetails(plan);
        }
        const requestBody = {};
        if (options.shareWithUserIds || options.shareWithUserNames) {
            const sharedWith = await this.generateSharedWith(options);
            requestBody['sharedWith'] = sharedWith;
        }
        if (categoriesCount > 0) {
            requestBody['categoryDescriptions'] = categories;
        }
        const etag = await this.getPlanDetailsEtag(planId);
        const requestOptionsPlanDetails = {
            url: `${this.resource}/v1.0/planner/plans/${planId}/details`,
            headers: {
                'accept': 'application/json;odata.metadata=none',
                'If-Match': etag,
                'Prefer': 'return=representation'
            },
            responseType: 'json',
            data: requestBody
        };
        const planDetails = await request.patch(requestOptionsPlanDetails);
        return { ...plan, ...planDetails };
    }
    async commandAction(logger, args) {
        try {
            const planId = await this.getPlanId(args);
            if (args.options.newTitle) {
                const etag = await this.getPlanEtag(planId);
                const requestOptions = {
                    url: `${this.resource}/v1.0/planner/plans/${planId}`,
                    headers: {
                        accept: 'application/json;odata.metadata=none',
                        'If-Match': etag,
                        'Prefer': 'return=representation'
                    },
                    responseType: 'json',
                    data: {
                        "title": args.options.newTitle
                    }
                };
                await request.patch(requestOptions);
            }
            const result = await this.updatePlanDetails(args.options, planId);
            await logger.log(result);
        }
        catch (err) {
            this.handleRejectedODataJsonPromise(err);
        }
    }
}
export default new PlannerPlanSetCommand();
//# sourceMappingURL=plan-set.js.map