'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _propTypes = require('prop-types');

var _propTypes2 = _interopRequireDefault(_propTypes);

var _immutable = require('immutable');

var _immutable2 = _interopRequireDefault(_immutable);

var _reactIntl = require('react-intl');

var _get = require('lodash/get');

var _get2 = _interopRequireDefault(_get);

var _cspaceInput = require('cspace-input');

var _configHelpers = require('../../helpers/configHelpers');

var _PermissionButton = require('../../../styles/cspace-ui/PermissionButton.css');

var _PermissionButton2 = _interopRequireDefault(_PermissionButton);

var _PermissionsInput = require('../../../styles/cspace-ui/PermissionsInput.css');

var _PermissionsInput2 = _interopRequireDefault(_PermissionsInput);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const MiniButton = _cspaceInput.components.MiniButton;
var _inputHelpers$pathHel = _cspaceInput.helpers.pathHelpers;
const getPath = _inputHelpers$pathHel.getPath,
      pathPropType = _inputHelpers$pathHel.pathPropType;


const permMessages = (0, _reactIntl.defineMessages)({
  '': {
    'id': 'permissionsInput.perm.none',
    'defaultMessage': 'None'
  },
  RL: {
    'id': 'permissionsInput.perm.read',
    'defaultMessage': 'Read'
  },
  CRUL: {
    'id': 'permissionsInput.perm.write',
    'defaultMessage': 'Write'
  },
  CRUDL: {
    'id': 'permissionsInput.perm.delete',
    'defaultMessage': 'Delete'
  }
});

const serviceTypeMessages = (0, _reactIntl.defineMessages)({
  object: {
    'id': 'permissionsInput.serviceType.object',
    'defaultMessage': 'Objects'
  },
  procedure: {
    'id': 'permissionsInput.serviceType.procedure',
    'defaultMessage': 'Procedures'
  },
  authority: {
    'id': 'permissionsInput.serviceType.authority',
    'defaultMessage': 'Authorities'
  },
  utility: {
    'id': 'permissionsInput.serviceType.utility',
    'defaultMessage': 'Utility Resources'
  },
  security: {
    'id': 'permissionsInput.serviceType.security',
    'defaultMessage': 'Security Resources'
  }
});

const serviceTypes = ['object', 'procedure', 'authority', 'utility', 'security'];

const propTypes = {
  /* eslint-disable react/no-unused-prop-types */
  name: _propTypes2.default.string,
  parentPath: pathPropType,
  subpath: pathPropType,
  /* eslint-enable react/no-unused-prop-types */
  readOnly: _propTypes2.default.bool,
  resourceNames: _propTypes2.default.instanceOf(_immutable2.default.List),
  value: _propTypes2.default.oneOfType([_propTypes2.default.instanceOf(_immutable2.default.List), _propTypes2.default.instanceOf(_immutable2.default.Map)]),
  readPerms: _propTypes2.default.func,
  onCommit: _propTypes2.default.func
};

const contextTypes = {
  intl: _reactIntl.intlShape,
  config: _propTypes2.default.object
};

class PermissionsInput extends _react.Component {
  constructor(props, context) {
    super(props, context);

    this.handleHeaderButtonClick = this.handleHeaderButtonClick.bind(this);
    this.handleRadioChange = this.handleRadioChange.bind(this);
  }

  componentDidMount() {
    var _props = this.props;
    const resourceNames = _props.resourceNames,
          readPerms = _props.readPerms;
    const config = this.context.config;


    if (!resourceNames && readPerms) {
      readPerms(config);
    }
  }

  getPermsMap() {
    // The services layer gives us a list of resources and action groups. Build a map of resource
    // names to UI permissions.

    let permissionsList = this.props.value;

    if (!permissionsList) {
      return undefined;
    }

    if (!_immutable2.default.List.isList(permissionsList)) {
      permissionsList = _immutable2.default.List.of(permissionsList);
    }

    const perms = {};

    permissionsList.forEach(permission => {
      const resourceName = permission.get('resourceName');
      const actionGroup = permission.get('actionGroup');

      perms[resourceName] = actionGroup;
    });

    return perms;
  }

  getRecordTypeConfigs() {
    const config = this.context.config;
    const resourceNames = this.props.resourceNames;


    return resourceNames.map(resourceName => (0, _configHelpers.getRecordTypeConfigByServicePath)(config, resourceName)).filter(recordTypeConfig => recordTypeConfig && !recordTypeConfig.disabled);
  }

  updatePerms(updates) {
    const perms = this.getPermsMap() || {};

    Object.assign(perms, updates);

    return _immutable2.default.List(Object.keys(perms).filter(resourceName => !!perms[resourceName]).map(resourceName => _immutable2.default.Map({
      resourceName,
      actionGroup: perms[resourceName]
    })));
  }

  stageUpdate(recordType, actionGroup, updates = {}) {
    /* The updates arg is mutated by this method. */
    /* eslint-disable no-param-reassign */

    const config = this.context.config;


    const serviceConfig = (0, _get2.default)(config, ['recordTypes', recordType, 'serviceConfig']);

    const documentName = serviceConfig.documentName,
          servicePath = serviceConfig.servicePath,
          serviceType = serviceConfig.serviceType;


    const resourceName = servicePath;
    const workflowDeleteResourceName = `/${resourceName}/*/workflow/delete`;

    updates[resourceName] = actionGroup;

    if (serviceType === 'object' || serviceType === 'procedure' || serviceType === 'authority' || serviceType === 'utility' &&
    // TODO: These utility records don't have soft delete. Make this configured, instead of
    // hardcoded.
    resourceName !== 'idgenerators' && resourceName !== 'structureddates' &&
    // TODO: Relation records have soft delete, but the UI issues hard deletes (for
    // consistency with pre-5.0 behavior). Therefore hard-delete permission needs to be
    // retained. Make this configured, instead of hard-coded.
    resourceName !== 'relations') {
      // For object, procedure, authority, and utility types, replace delete permission with
      // permissions on the delete workflow transition. (Security records do not have
      // soft-delete).

      if (actionGroup) {
        if (actionGroup.includes('D')) {
          updates[workflowDeleteResourceName] = 'CRUDL';
          updates[resourceName] = actionGroup.replace('D', '');
        } else {
          updates[workflowDeleteResourceName] = 'RL';
        }
      } else {
        updates[workflowDeleteResourceName] = '';
      }
    }

    if (resourceName === 'relations') {
      // The UI might switch to soft-deleting relations in the future, so also grant permission on
      // the delete workflow transition if delete permission exists.

      if (actionGroup) {
        if (actionGroup.includes('D')) {
          updates[workflowDeleteResourceName] = 'CRUDL';
        } else {
          updates[workflowDeleteResourceName] = 'RL';
        }
      } else {
        updates[workflowDeleteResourceName] = '';
      }
    }

    if (serviceType === 'authority') {
      // Permissions on authorities should be set on both the authorities and their items.

      const itemResourceName = documentName;

      updates[itemResourceName] = actionGroup;
    }

    if (resourceName === 'authorization/roles') {
      // If the authorization/roles resource can be read, allow permissions to be read and
      // listed. This allows the permissions list to be displayed when viewing the role.

      const permissionsResourceName = 'authorization/permissions';

      updates[permissionsResourceName] = actionGroup && actionGroup.includes('R') ? 'RL' : '';
    }

    // Always allow read and list on servicegroups.

    updates.servicegroups = 'RL';

    return updates;
    /* eslint-enable no-param-reassign */
  }

  handleHeaderButtonClick(event) {
    var _event$currentTarget$ = event.currentTarget.dataset;
    const serviceType = _event$currentTarget$.servicetype,
          actionGroup = _event$currentTarget$.actiongroup;
    const onCommit = this.props.onCommit;


    if (onCommit) {
      const stagedUpdates = {};

      this.getRecordTypeConfigs().filter(recordTypeConfig => recordTypeConfig.serviceConfig.serviceType === serviceType).forEach(recordTypeConfig => {
        this.stageUpdate(recordTypeConfig.name, actionGroup, stagedUpdates);
      });

      const updatedPerms = this.updatePerms(stagedUpdates);

      onCommit(getPath(this.props), updatedPerms);
    }
  }

  handleRadioChange(event) {
    var _event$target = event.target;
    const actionGroup = _event$target.value,
          selected = _event$target.checked;
    const recordType = event.target.dataset.name;
    const onCommit = this.props.onCommit;


    if (selected && onCommit) {
      const stagedUpdates = this.stageUpdate(recordType, actionGroup);
      const updatedPerms = this.updatePerms(stagedUpdates);

      onCommit(getPath(this.props), updatedPerms);
    }
  }

  renderHeaderButton(serviceType, actionGroup) {
    const readOnly = this.props.readOnly;


    return _react2.default.createElement(
      MiniButton,
      {
        autoWidth: true,
        onClick: this.handleHeaderButtonClick,
        'data-servicetype': serviceType,
        'data-actiongroup': actionGroup,
        disabled: readOnly
      },
      _react2.default.createElement(_reactIntl.FormattedMessage, permMessages[actionGroup])
    );
  }

  renderRadioButton(perms, recordType, resourceName, value) {
    const readOnly = this.props.readOnly;


    const className = readOnly ? _PermissionButton2.default.readOnly : _PermissionButton2.default.normal;

    let checked = false;

    if (perms) {
      let effectivePerms = perms[resourceName];

      if (effectivePerms === 'CRUL') {
        // Check for soft-delete perm, and synthesize a delete perm if present.

        const workflowDeleteResourceName = `/${resourceName}/*/workflow/delete`;

        if (perms[workflowDeleteResourceName] === 'CRUDL') {
          effectivePerms = 'CRUDL';
        }
      }

      checked = value ? effectivePerms === value : !effectivePerms;
    }

    return (
      // FIXME: Do I really need for if I'm wrapping the input in the label?
      // eslint-disable-next-line jsx-a11y/label-has-for
      _react2.default.createElement(
        'label',
        { className: className },
        _react2.default.createElement(_reactIntl.FormattedMessage, permMessages[value]),
        _react2.default.createElement('input', {
          checked: checked,
          'data-name': recordType,
          type: 'radio',
          value: value,
          disabled: readOnly,
          onChange: this.handleRadioChange
        }),
        _react2.default.createElement('div', null)
      )
    );
  }

  renderPermRows() {
    const intl = this.context.intl;


    const perms = this.getPermsMap() || {};
    const sections = [];

    serviceTypes.forEach(serviceType => {
      const rows = [];

      this.getRecordTypeConfigs().filter(recordTypeConfig => recordTypeConfig.serviceConfig.serviceType === serviceType).sort((recordTypeConfigA, recordTypeConfigB) => {
        // Primary sort by sortOrder

        let sortOrderA = recordTypeConfigA.sortOrder;
        let sortOrderB = recordTypeConfigB.sortOrder;

        if (typeof sortOrderA !== 'number') {
          sortOrderA = Number.MAX_VALUE;
        }

        if (typeof sortOrderB !== 'number') {
          sortOrderB = Number.MAX_VALUE;
        }

        if (sortOrderA !== sortOrderB) {
          return sortOrderA > sortOrderB ? 1 : -1;
        }

        // Secondary sort by label

        const labelA = intl.formatMessage(recordTypeConfigA.messages.record.collectionName);
        const labelB = intl.formatMessage(recordTypeConfigB.messages.record.collectionName);

        // FIXME: This should be locale aware
        return labelA.localeCompare(labelB);
      }).forEach(recordTypeConfig => {
        const name = recordTypeConfig.name;
        const resourceName = recordTypeConfig.serviceConfig.servicePath;
        const nameMessage = (0, _get2.default)(recordTypeConfig, ['messages', 'record', 'collectionName']);

        const formattedName = nameMessage ? intl.formatMessage(nameMessage) : `[ ${name} ]`;

        rows.push(_react2.default.createElement(
          'div',
          { key: name },
          _react2.default.createElement(
            'div',
            null,
            formattedName
          ),
          _react2.default.createElement(
            'div',
            null,
            this.renderRadioButton(perms, name, resourceName, ''),
            this.renderRadioButton(perms, name, resourceName, 'RL'),
            this.renderRadioButton(perms, name, resourceName, 'CRUL'),
            this.renderRadioButton(perms, name, resourceName, 'CRUDL')
          )
        ));
      });

      sections.push(_react2.default.createElement(
        'section',
        { key: serviceType },
        _react2.default.createElement(
          'header',
          null,
          _react2.default.createElement(
            'h3',
            null,
            _react2.default.createElement(_reactIntl.FormattedMessage, serviceTypeMessages[serviceType])
          ),
          _react2.default.createElement(
            'ul',
            null,
            _react2.default.createElement(
              'li',
              null,
              this.renderHeaderButton(serviceType, '')
            ),
            _react2.default.createElement(
              'li',
              null,
              this.renderHeaderButton(serviceType, 'RL')
            ),
            _react2.default.createElement(
              'li',
              null,
              this.renderHeaderButton(serviceType, 'CRUL')
            ),
            _react2.default.createElement(
              'li',
              null,
              this.renderHeaderButton(serviceType, 'CRUDL')
            )
          )
        ),
        rows
      ));
    });

    return sections;
  }

  render() {
    var _props2 = this.props;
    const readOnly = _props2.readOnly,
          resourceNames = _props2.resourceNames;


    if (!resourceNames) {
      return null;
    }

    let value = this.props.value;


    if (value && !_immutable2.default.List.isList(value)) {
      value = _immutable2.default.List.of(value);
    }

    const className = readOnly ? _PermissionsInput2.default.readOnly : _PermissionsInput2.default.common;

    return _react2.default.createElement(
      'div',
      { className: className },
      this.renderPermRows()
    );
  }
}

exports.default = PermissionsInput;
PermissionsInput.propTypes = propTypes;
PermissionsInput.contextTypes = contextTypes;