import os from 'os';
import { z } from 'zod';
import { globalOptionsZod } from '../../../../Command.js';
import request from '../../../../request.js';
import { formatting } from '../../../../utils/formatting.js';
import { validation } from '../../../../utils/validation.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';
import { cli } from '../../../../cli/cli.js';
export const options = z.strictObject({
    ...globalOptionsZod.shape,
    appId: z.uuid().optional(),
    appObjectId: z.uuid().optional(),
    appDisplayName: z.string().optional(),
    resource: z.string().alias('r'),
    scopes: z.string().transform((value) => value.split(',').map(String)).alias('s')
});
class EntraAppRoleAssignmentAddCommand extends GraphCommand {
    get name() {
        return commands.APPROLEASSIGNMENT_ADD;
    }
    get description() {
        return 'Adds service principal permissions also known as scopes and app role assignments for specified Microsoft Entra application registration';
    }
    get schema() {
        return options;
    }
    getRefinedSchema(schema) {
        return schema
            .refine(options => [options.appId, options.appObjectId, options.appDisplayName].filter(o => o !== undefined).length === 1, {
            error: 'Specify either appId, appObjectId, or appDisplayName',
            params: {
                customCode: 'optionSet',
                options: ['appId', 'appObjectId', 'appDisplayName']
            }
        });
    }
    async commandAction(logger, args) {
        let objectId;
        let queryFilter;
        if (args.options.appId) {
            queryFilter = `$filter=appId eq '${formatting.encodeQueryParameter(args.options.appId)}'`;
        }
        else if (args.options.appObjectId) {
            queryFilter = `$filter=id eq '${formatting.encodeQueryParameter(args.options.appObjectId)}'`;
        }
        else {
            queryFilter = `$filter=displayName eq '${formatting.encodeQueryParameter(args.options.appDisplayName)}'`;
        }
        const getServicePrinciplesRequestOptions = {
            url: `${this.resource}/v1.0/servicePrincipals?${queryFilter}`,
            headers: {
                accept: 'application/json'
            },
            responseType: 'json'
        };
        try {
            const servicePrincipalResult = await request.get(getServicePrinciplesRequestOptions);
            if (servicePrincipalResult.value.length === 0) {
                throw `The specified service principal doesn't exist`;
            }
            if (servicePrincipalResult.value.length > 1) {
                const resultAsKeyValuePair = formatting.convertArrayToHashTable('id', servicePrincipalResult.value);
                const result = await cli.handleMultipleResultsFound(`Multiple service principal found.`, resultAsKeyValuePair);
                objectId = result.id;
            }
            else {
                objectId = servicePrincipalResult.value[0].id;
            }
            let resource = formatting.encodeQueryParameter(args.options.resource);
            // try resolve aliases that the user might enter since these are seen in the Azure portal
            switch (args.options.resource.toLocaleLowerCase()) {
                case 'sharepoint':
                    resource = 'Office 365 SharePoint Online';
                    break;
                case 'intune':
                    resource = 'Microsoft Intune API';
                    break;
                case 'exchange':
                    resource = 'Office 365 Exchange Online';
                    break;
            }
            // will perform resource name, appId or objectId search
            let filter = `$filter=(displayName eq '${resource}' or startswith(displayName,'${resource}'))`;
            if (validation.isValidGuid(resource)) {
                filter += ` or appId eq '${resource}' or id eq '${resource}'`;
            }
            const requestOptions = {
                url: `${this.resource}/v1.0/servicePrincipals?${filter}`,
                headers: {
                    'accept': 'application/json'
                },
                responseType: 'json'
            };
            const res = await request.get(requestOptions);
            const appRoles = [];
            // flatten the app roles found
            const appRolesFound = [];
            for (const servicePrincipal of res.value) {
                for (const role of servicePrincipal.appRoles) {
                    appRolesFound.push({
                        resourceId: servicePrincipal.id,
                        objectId: role.id,
                        value: role.value
                    });
                }
            }
            if (!appRolesFound.length) {
                throw `The resource '${args.options.resource}' does not have any application permissions available.`;
            }
            // search for match between the found app roles and the specified scopes option value
            for (const scope of args.options.scopes) {
                const existingRoles = appRolesFound.filter((role) => {
                    return role.value.toLocaleLowerCase() === scope.toLocaleLowerCase().trim();
                });
                if (!existingRoles.length) {
                    // the role specified in the scopes option does not belong to the found service principles
                    // throw an error and show list with available roles (scopes)
                    let availableRoles = '';
                    appRolesFound.map((r) => availableRoles += `${os.EOL}${r.value}`);
                    throw `The scope value '${scope}' you have specified does not exist for ${args.options.resource}. ${os.EOL}Available scopes (application permissions) are: ${availableRoles}`;
                }
                appRoles.push(existingRoles[0]);
            }
            const tasks = [];
            for (const appRole of appRoles) {
                tasks.push(this.addRoleToServicePrincipal(objectId, appRole));
            }
            const rolesAddedResponse = await Promise.all(tasks);
            if (args.options.output && args.options.output.toLowerCase() === 'json') {
                await logger.log(rolesAddedResponse);
            }
            else {
                await logger.log(rolesAddedResponse.map((result) => ({
                    objectId: result.id,
                    principalDisplayName: result.principalDisplayName,
                    resourceDisplayName: result.resourceDisplayName
                })));
            }
        }
        catch (err) {
            this.handleRejectedODataJsonPromise(err);
        }
    }
    async addRoleToServicePrincipal(objectId, appRole) {
        const requestOptions = {
            url: `${this.resource}/v1.0/servicePrincipals/${objectId}/appRoleAssignments`,
            headers: {
                'Content-Type': 'application/json'
            },
            responseType: 'json',
            data: {
                appRoleId: appRole.objectId,
                principalId: objectId,
                resourceId: appRole.resourceId
            }
        };
        return request.post(requestOptions);
    }
}
export default new EntraAppRoleAssignmentAddCommand();
//# sourceMappingURL=approleassignment-add.js.map