import _ from 'lodash-es';
import angular from 'angular';

import AccessViewerPolicyModel from '../../models/access';

class AccessViewerController {
  /* @ngInject */
  constructor(Notifications, ExtensionService, RoleService, UserService, EndpointService, GroupService, TeamService, TeamMembershipService) {
    this.Notifications = Notifications;
    this.ExtensionService = ExtensionService;
    this.RoleService = RoleService;
    this.UserService = UserService;
    this.EndpointService = EndpointService;
    this.GroupService = GroupService;
    this.TeamService = TeamService;
    this.TeamMembershipService = TeamMembershipService;
  }

  onUserSelect() {
    this.userRoles = [];
    const userRoles = {};
    const user = this.selectedUser;
    const userMemberships = _.filter(this.teamMemberships, { UserId: user.Id });

    for (const [, endpoint] of _.entries(this.endpoints)) {
      let role = this.getRoleFromUserEndpointPolicy(user, endpoint);
      if (role) {
        userRoles[endpoint.Id] = role;
        continue;
      }

      role = this.getRoleFromUserEndpointGroupPolicy(user, endpoint);
      if (role) {
        userRoles[endpoint.Id] = role;
        continue;
      }

      role = this.getRoleFromTeamEndpointPolicies(userMemberships, endpoint);
      if (role) {
        userRoles[endpoint.Id] = role;
        continue;
      }

      role = this.getRoleFromTeamEndpointGroupPolicies(userMemberships, endpoint);
      if (role) {
        userRoles[endpoint.Id] = role;
      }
    }

    this.userRoles = _.values(userRoles);
  }

  findLowestRole(policies) {
    return _.first(_.orderBy(policies, 'RoleId', 'desc'));
  }

  getRoleFromUserEndpointPolicy(user, endpoint) {
    const policyRoles = [];
    const policy = endpoint.UserAccessPolicies[user.Id];
    if (policy) {
      const accessPolicy = new AccessViewerPolicyModel(policy, endpoint, this.roles, null, null);
      policyRoles.push(accessPolicy);
    }
    return this.findLowestRole(policyRoles);
  }

  getRoleFromUserEndpointGroupPolicy(user, endpoint) {
    const policyRoles = [];
    const policy = this.groupUserAccessPolicies[endpoint.GroupId][user.Id];
    if (policy) {
      const accessPolicy = new AccessViewerPolicyModel(policy, endpoint, this.roles, this.groups[endpoint.GroupId], null);
      policyRoles.push(accessPolicy);
    }
    return this.findLowestRole(policyRoles);
  }

  getRoleFromTeamEndpointPolicies(memberships, endpoint) {
    const policyRoles = [];
    for (const membership of memberships) {
      const policy = endpoint.TeamAccessPolicies[membership.TeamId];
      if (policy) {
        const accessPolicy = new AccessViewerPolicyModel(policy, endpoint, this.roles, null, this.teams[membership.TeamId]);
        policyRoles.push(accessPolicy);
      }
    }
    return this.findLowestRole(policyRoles);
  }

  getRoleFromTeamEndpointGroupPolicies(memberships, endpoint) {
    const policyRoles = [];
    for (const membership of memberships) {
      const policy = this.groupTeamAccessPolicies[endpoint.GroupId][membership.TeamId];
      if (policy) {
        const accessPolicy = new AccessViewerPolicyModel(policy, endpoint, this.roles, this.groups[endpoint.GroupId], this.teams[membership.TeamId]);
        policyRoles.push(accessPolicy);
      }
    }
    return this.findLowestRole(policyRoles);
  }

  async $onInit() {
    try {
      this.rbacEnabled = await this.ExtensionService.extensionEnabled(this.ExtensionService.EXTENSIONS.RBAC);
      if (this.rbacEnabled) {
        this.users = await this.UserService.users();
        this.endpoints = _.keyBy((await this.EndpointService.endpoints()).value, 'Id');
        const groups = await this.GroupService.groups();
        this.groupUserAccessPolicies = {};
        this.groupTeamAccessPolicies = {};
        _.forEach(groups, (group) => {
          this.groupUserAccessPolicies[group.Id] = group.UserAccessPolicies;
          this.groupTeamAccessPolicies[group.Id] = group.TeamAccessPolicies;
        });
        this.groups = _.keyBy(groups, 'Id');
        this.roles = _.keyBy(await this.RoleService.roles(), 'Id');
        this.teams = _.keyBy(await this.TeamService.teams(), 'Id');
        this.teamMemberships = await this.TeamMembershipService.memberships();
      }
    } catch (err) {
      this.Notifications.error('Failure', err, 'Unable to retrieve accesses');
    }
  }
}

export default AccessViewerController;
angular.module('portainer.app').controller('AccessViewerController', AccessViewerController);
