// Models
import { ScopeValue, PermissionDef, WebStorage } from '@amalto/typings';

// Constants
const ASTERISK = '*';

/**
 * User got permission.
 * e.g: reports=view
 */
export function hasPermission(
  webStorage: WebStorage,
  permission: string,
  global?: boolean,
): boolean {
  return hasAnyPermission(webStorage, [permission], global);
}

/**
 * User got all permissions.
 * e.g: ['reports=view', 'reports=read']
 */
export function hasAnyPermission(
  webStorage: WebStorage,
  permissions: string[] | PermissionDef[],
  global?: boolean,
) {
  const appInstance =
    webStorage.selectedAppInstance && webStorage.selectedAppInstance.name;

  if (!permissions || !permissions.length || !appInstance) {
    return false;
  }

  for (let i = 0; i < permissions.length; i++) {
    if (typeof permissions[i] === 'object') {
      //if the permission is required globally, the check will be only on the * instance
      const appInstanceValues = getScopeValues(
        webStorage,
        global ? ASTERISK : appInstance,
        (permissions[i] as PermissionDef).feature,
        (permissions[i] as PermissionDef).action,
      );

      if (appInstanceValues) {
        if ($.isEmptyObject(appInstanceValues)) {
          return true;
        }

        if ((appInstanceValues as string[]).length) {
          if (
            (appInstanceValues as string[]).indexOf(
              (permissions[i] as PermissionDef).requiredValue,
            ) !== -1
          ) {
            return true;
          }
        }
      }

      //check values on * instance
      if (global) {
        const asteriskValues = getScopeValues(
          webStorage,
          ASTERISK,
          (permissions[i] as PermissionDef).feature,
          (permissions[i] as PermissionDef).action,
        );

        if (asteriskValues) {
          if ($.isEmptyObject(asteriskValues)) {
            return true;
          }

          if ((asteriskValues as string[]).length) {
            if (
              (asteriskValues as string[]).indexOf(
                (permissions[i] as PermissionDef).requiredValue,
              ) !== -1
            ) {
              return true;
            }
          }
        }
      }
    } else if (typeof permissions[i] === 'string') {
      const parsedScopeString = (permissions[i] as string).split('=');

      if (parsedScopeString.length === 2) {
        if (
          hasPermissionFor(
            webStorage,
            global ? ASTERISK : appInstance,
            parsedScopeString[0],
            parsedScopeString[1],
          )
        ) {
          return true;
        }
      } else {
        console.log('Error - bad scope string value:', permissions[i]);
      }
    }
  }

  return false;
}

/**
 * Check if user has permission for a permission within a specific instance
 * e.g: Does user has the permission reports=read on instance master,
 *      where 'reports' is the feature and 'read' the action.
 */
export function hasPermissionFor(
  webStorage: WebStorage,
  appInstance: string,
  feature: string,
  action: string,
): boolean {
  return (
    hasAccessToAction(webStorage, appInstance, feature, action) ||
    hasAccessToAction(webStorage, appInstance, feature, ASTERISK) ||
    hasAccessToAction(webStorage, appInstance, ASTERISK, ASTERISK) ||
    hasAccessToAction(webStorage, ASTERISK, feature, action) ||
    hasAccessToAction(webStorage, ASTERISK, feature, ASTERISK) ||
    hasAccessToAction(webStorage, ASTERISK, ASTERISK, ASTERISK)
  );
}

/**
 * Get item values from permission.
 * e.g: reports=read('Test'), will return ['Test'].
 */
export function getScopeValues(
  webStorage: WebStorage,
  appInstance: string,
  feature: string,
  action: string,
): ScopeValue | string[] {
  const scopesTree = webStorage.scopesTree;

  return (
    scopesTree &&
    scopesTree[appInstance] &&
    scopesTree[appInstance][feature] &&
    scopesTree[appInstance][feature][action]
  );
}

/**
 * User has access to feature id.
 * e.g: reports=read, here 'reports' is the feature id.
 */
export function hasAccessToFeature(
  webStorage: WebStorage,
  appInstance: string,
  feature: string,
  global?: boolean,
): boolean {
  const scopesTree = webStorage.scopesTree;

  if (!scopesTree || !appInstance || !feature) {
    return false;
  }

  const featuresMap = scopesTree[global ? ASTERISK : appInstance];

  for (let _feature in featuresMap) {
    if (_feature === feature || _feature === ASTERISK) {
      return true;
    }
  }

  //if we don't find an access to the feature on the specific instance, we can check on the * one
  if (!global) {
    return hasAccessToFeature(webStorage, appInstance, feature, true);
  }

  return false;
}

/**
 * User has access to feature action.
 * e.g: reports=read, here 'read' is the action.
 */
function hasAccessToAction(
  webStorage: WebStorage,
  appInstance: any,
  feature: string,
  action: string,
) {
  if (!appInstance || !feature || !action) {
    return false;
  }

  if (hasAccessToFeature(webStorage, appInstance, feature)) {
    return (
      getScopeValues(webStorage, appInstance, feature, action) !== undefined
    );
  } else {
    return false;
  }
}

/**
 * Action has filter on it.
 */
export function hasFilterOn(
  webStorage: WebStorage,
  feature: string,
  viewFilter: string,
  filterKey: string,
): boolean {
  const appInstance =
    webStorage.selectedAppInstance && webStorage.selectedAppInstance.name;

  //filter by key value are made via the keyword 'allow'

  const appInstanceValues = appInstance
    ? (getScopeValues(webStorage, appInstance, feature, 'allow') as ScopeValue)
    : undefined;
  const asteriskValues = getScopeValues(
    webStorage,
    ASTERISK,
    feature,
    'allow',
  ) as ScopeValue;

  if (appInstanceValues && appInstanceValues[viewFilter]) {
    if (
      appInstanceValues[viewFilter].filter(
        (scopeFilter) => scopeFilter.searchable === filterKey,
      ).length > 0
    ) {
      return true;
    }
  }

  if (asteriskValues && asteriskValues[viewFilter]) {
    if (
      asteriskValues[viewFilter].filter(
        (scopeFilter) => scopeFilter.searchable === filterKey,
      ).length > 0
    ) {
      return true;
    }
  }

  return false;
}

/**
 * True if only one requiredActions is contain within the authorizedActions.
 */
export function canPerformAnyAction(
  requiredActions: string[],
  authorizedActions: string[],
): boolean {
  if (!requiredActions || !requiredActions.length || !authorizedActions) {
    return false;
  }

  //if AT LEAST ONE requiredActions is in the authorized ones it is OK

  for (let i = 0; i < requiredActions.length; i++) {
    if (authorizedActions.indexOf(requiredActions[i]) !== -1) {
      return true;
    }
  }

  return false;
}
