const READONLY_INVENTORY_ROLES = require('../constant/chemical/readonly-roles-inventory.constant');
const ROLES = require('../constant/core/roles.constant');
const {
  EDGE,
  EDGE_TYPE,
  NODE: { GLOBAL },
} = require('../constant/relationship/label.constant');
const AnalyticsHelper = require('./analytics.helper');
const RelationshipHelper = require('./relationship.helper');

module.exports.permissionError = (message) => Object.assign(new Error(message || 'no permission'), { status: 403 });

module.exports.hasRole = (user, role, criteria = {}) =>
  user.roles &&
  user.roles.some(
    (r) =>
      r.role === role &&
      (criteria.tenantCode && criteria.tenantCode !== 'ALL' && r.tenantCode !== 'ALL'
        ? r.tenantCode === criteria.tenantCode
        : true) &&
      (criteria.campusCode && criteria.campusCode !== 'ALL' && r.campusCode !== 'ALL'
        ? r.campusCode === criteria.campusCode
        : true) &&
      (criteria.types ? criteria.types.some((type) => r.type && r.type === type) : true),
  );

module.exports.hasRelationship = (user, relationship, criteria = {}) => {
  let hasRelation = Array.isArray(user.relationships) && user.relationships.some((r) => r.edge.label === relationship);
  if (criteria.campusCode) {
    hasRelation =
      hasRelation && RelationshipHelper.hasRelationshipWithRole(criteria.campusCode, user.relationships, relationship);
  }
  if (criteria.inventoryId) {
    hasRelation =
      hasRelation && RelationshipHelper.hasRelationshipWithRole(criteria.inventoryId, user.relationships, relationship);
  }
  if (criteria.groups) {
    hasRelation =
      hasRelation &&
      criteria.groups.some((groupId) =>
        RelationshipHelper.hasRelationshipWithRole(groupId, user.relationships, relationship),
      );
  }
  if (criteria.tenantCode) {
    hasRelation =
      hasRelation && RelationshipHelper.hasRelationshipWithRole(criteria.tenantCode, user.relationships, relationship);
  }
  return hasRelation;
};

module.exports.isAnalyticsAdmin = ({ relationships }, isNeptune) =>
  //  # TODO: modify after mongo => neptune migration
  (relationships &&
    isNeptune &&
    relationships.some(({ edge: { label, type } }) => type === EDGE_TYPE.ROLE && label === EDGE.ANALYTICS_ADMIN)) ||
  (relationships && relationships.some(({ role }) => role === EDGE.ANALYTICS_ADMIN));

module.exports.isClientAdmin = (user) =>
  RelationshipHelper.hasRelationshipWithRole(GLOBAL, user.relationships, EDGE.CLIENT_ADMIN);

module.exports.isInventorySpecialist = (campusCode, user) =>
  module.exports.hasRelationship(user, EDGE.INVENTORY_SPECIALIST, { campusCode });

module.exports.isChemAdmin = (campusCode, user) =>
  module.exports.hasRole(user, ROLES.CHEMICAL_ADMIN, { campusCode }) ||
  module.exports.hasRelationship(user, EDGE.CHEMICAL_ADMIN, { campusCode });

module.exports.isChemAdminReadOnly = (campusCode, user) =>
  module.exports.hasRole(user, ROLES.CHEMICAL_ADMIN_READ_ONLY, { campusCode }) ||
  module.exports.hasRelationship(user, EDGE.CHEMICAL_ADMIN_READ_ONLY, { campusCode });

module.exports.isChemInfo = (user) =>
  module.exports.hasRole(user, ROLES.CHEMICAL_INFORMATICS) || module.exports.hasRelationship(user, EDGE.CHEM_INFO);

module.exports.hasChemAdminRead = (campusCode, user) =>
  module.exports.isChemAdminReadOnly(campusCode, user) || module.exports.isChemAdmin(campusCode, user);

module.exports.isEHSAdmin = (campusCode, user) =>
  module.exports.hasRole(user, ROLES.EHS_ADMIN, { campusCode }) ||
  module.exports.hasRelationship(user, EDGE.EHS_DEPARTMENT_ADMIN, { campusCode });

module.exports.isControlAreaAdmin = (user, campusCode) =>
  RelationshipHelper.hasRelationshipWithRole(campusCode, user.relationships, EDGE.CONTROL_AREA_ADMIN);

module.exports.isSystemControlAreaAdmin = (user, tenantCode) =>
  RelationshipHelper.hasRelationshipWithRole(tenantCode, user.relationships, EDGE.SYSTEM_CONTROL_AREA_ADMIN);

module.exports.hasRoleWithReadAccessToInventory = (inventory, user) => {
  const inventoryGroupIds = inventory.groups.map((group) => group.id);
  return (
    (!module.exports.isMemberOrColleagueOfInventory(inventory, user) &&
      user.roles
        .filter((role) => READONLY_INVENTORY_ROLES.includes(role.role))
        .reduce((acc, curr) => acc.concat(curr.groupIds), [])
        .some((groupId) => inventoryGroupIds.includes(groupId))) ||
    module.exports.hasChemAdminRead(inventory.campusCode, user) ||
    (user.relationships &&
      user.relationships.some((r) => READONLY_INVENTORY_ROLES.includes(r.role) && inventoryGroupIds.includes(r.nodeId)))
  );
};

module.exports.isMemberOfInventory = (inventory, user) =>
  module.exports.hasManageAccessToInventory(inventory, user) ||
  user.groups.some((group) => inventory.groups.map((item) => item.id).includes(group.id)) ||
  module.exports.hasRelationship(user, EDGE.MEMBER, { inventoryId: inventory._id.toString() });

module.exports.isMemberOrColleagueOfInventory = (inventory, user) =>
  module.exports.isMemberOfInventory(inventory, user) ||
  !!(inventory.colleagues || []).find(
    (colleague) =>
      user.inventories &&
      user.inventories.some((inv) =>
        colleague.id ? inv._id.toString() === colleague.id.toString() : inv._id.toString() === colleague._id.toString(),
      ),
  );

module.exports.hasManageAccessToInventory = (inventory, user) => {
  if (inventory && user) {
    return (
      module.exports.isChemAdmin(inventory.campusCode, user) ||
      module.exports.isManagerOfInventory(inventory, user) ||
      module.exports.isGroupOwner(inventory, user) ||
      module.exports.isGroupDelegate(inventory, user) ||
      module.exports.isInventorySpecialist(inventory, user)
    );
  }
  return false;
};

module.exports.isManagerOfInventory = (inventory, user) =>
  (inventory.owner || {}).userId === user.userId ||
  (inventory.permissions || []).find((p) => p.type === 'MANAGE' && p.userId === user.userId) ||
  user.relationships.some(
    (relationship) => relationship.edge.label === 'OWNER' && relationship.node.id === inventory._id,
  );

module.exports.isGroupOwner = (inventory, user) =>
  (inventory.groups || []).some((group) => (group.owner || {}).userId === user.userId) ||
  module.exports.hasRelationship(user, EDGE.OWNER, { groups: inventory.groups.map((g) => g.id) });

module.exports.isGroupDelegate = (inventory, user) => {
  const inventoryGroupIds = inventory.groups.map((group) => group.id);
  return (
    user.groups.filter((group) => inventoryGroupIds.includes(group.id)).some((group) => group.isDelegate) ||
    module.exports.hasRelationship(user, EDGE.DELEGATE, { groups: inventoryGroupIds })
  );
};

// chemical
module.exports.hasWriteAccessToFamily = (family, user) =>
  (family.group &&
    (user.groups.map((group) => group.id).includes(family.group.id) ||
      module.exports.hasRelationship(user, null, { groups: [family.group.id] }))) ||
  module.exports.isChemInfo(user);

module.exports.hasReadAccessToInventory = (inventory, user) =>
  module.exports.hasChemAdminRead(inventory.campusCode, user) ||
  module.exports.isMemberOrColleagueOfInventory(inventory, user) ||
  module.exports.hasRoleWithReadAccessToInventory(inventory, user);

module.exports.hasReadAccessToContainer = (container, user) =>
  module.exports.hasChemAdminRead(container.inventory.campusCode, user) ||
  module.exports.hasWriteAccessToContainer(container, user) ||
  (!container.isPrivate &&
    user.inventories
      .reduce(
        (acc, inventory) =>
          acc.concat(
            inventory.colleagues.reduce(
              (inventoryAcc, colleague) =>
                inventoryAcc.concat(colleague._id ? colleague._id.toString() : colleague.id.toString()),
              [],
            ),
          ),
        [],
      )
      .includes(container.inventory._id.toString())) ||
  module.exports.hasRelationship(user, null, { inventoryId: container.inventory._id.toString() });

module.exports.hasWriteAccessToContainer = (container, user) =>
  (user.inventories || [{}]).some(
    (inventory) => inventory._id && inventory._id.toString() === container.inventory._id.toString(),
  ) ||
  module.exports.isChemAdmin(container.inventory.campusCode, user) ||
  module.exports.hasRelationship(user, null, { inventoryId: container.inventory._id.toString() });

module.exports.hasReadAccessToLibrary = (user) =>
  module.exports.isChemAdmin(user.campusCode, user) ||
  module.exports.hasChemAdminRead(user.campusCode, user) ||
  module.exports.isChemInfo(user) ||
  module.exports.isControlAreaAdmin(user, user.campusCode);

module.exports.hasWriteAccessToLibrary = (user) => module.exports.isChemInfo(user);

module.exports.hasReadAccessToControlArea = (user, controlArea) =>
  module.exports.isChemAdmin(user.campusCode, user) ||
  module.exports.isSystemControlAreaAdmin(user, controlArea.tenantCode) ||
  module.exports.isControlAreaAdmin(user, controlArea.campusCode);

module.exports.hasWriteAccessToControlArea = (user, controlArea) =>
  module.exports.isSystemControlAreaAdmin(user, controlArea.tenantCode) ||
  module.exports.isControlAreaAdmin(user, user.campusCode);

module.exports.hasWriteAccessToFacility = (user, facility) =>
  user.campusCode === facility.campusCode && module.exports.isChemAdmin(facility.campusCode, user);

module.exports.hasLabHazardAdminRole = (user) =>
  user.relationships.length > 0 && user.relationships.some((relation) => relation.role === EDGE.LAB_HAZARD_ADMIN);

//  # TODO: modify after mongo => neptune migration
module.exports.hasInternalToolsAccess = (user, isNeptune) => AnalyticsHelper.isAnalyticsAdmin(user, isNeptune);
