import routes from './routes'
import policy from './policy'
import canDirective from './can-directive'
import { getValue } from '@/helpers/variable-handling'

const identifier = '__ibs_frontend_security_service'
/**
 * Security Service
 * users singleton design pattern
 * requires Vuex store to be set by method setStore to process state based methods
 * loggedInGetterName need to be set with the Vuex getter which is used to check if the user is logged in
 *
 * it depends on vue-router-sync library to check security on route state change
 * TODO: change this function to a Vue plugin
 * TODO: remove Store setting and use -> import {Store} from 'vuex'
 */
export const security = {
  _identifier: identifier,
  _instance: null,
  _type: 'ibsFrontendSecurityService',
  _routes: routes,
  _policy: policy,
  _store: null,
  _loggedInGetterName: null,
  _logInMutationType: null,
  _logOutMutationType: null,
  _router: null,
  _memberAuthorityGetterName: null,
  _memberAuthorityInitializedGetterName: null,
  _accessDeniedPage: '/',
  get type() {
    return this._type
  },
  // the router security guard instance
  get routes() {
    return this._routes
  },
  get policy() {
    return this._policy
  },
  set router(value) {
    this._router = value
  },
  get accessDeniedPage() {
    return this._accessDeniedPage
  },
  set accessDeniedPage(value) {
    this._accessDeniedPage = value
  },
  registerStorePlugin(
    logInMutationType,
    logOutMutationType,
    isLoggedInGetterName,
    memberAuthorityGetterName,
    memberAuthorityInitializedGetterName = null
  ) {
    let self = this

    self.logInMutationType = logInMutationType
    self.logOutMutationType = logOutMutationType
    self.loggedInGetterName = isLoggedInGetterName
    self.memberAuthorityGetterName = memberAuthorityGetterName
    self.memberAuthorityInitializedGetterName = memberAuthorityInitializedGetterName

    return store => {
      self._store = store
      store.subscribe(mutation => {
        // check security on user login and logout
        if (
          (mutation.type === logInMutationType || mutation.type === logOutMutationType) &&
          store.getters.getIsRouteDisabled === false
        ) {
          // check route security of the current route
          let routeMock = { matched: [self._store.state.route] }
          let routeTo = self.routes.check(routeMock)
          if (routeTo) {
            self._router.push(routeTo)
          }
        }
      })
    }
  },
  set loggedInGetterName(value) {
    this._loggedInGetterName = value
  },
  get loggedInGetterName() {
    return this._loggedInGetterName
  },
  set memberAuthorityGetterName(value) {
    this._memberAuthorityGetterName = value
  },
  get memberAuthorityGetterName() {
    return this._memberAuthorityGetterName
  },
  set memberAuthorityInitializedGetterName(value) {
    this._memberAuthorityInitializedGetterName = value
  },
  get memberAuthorityInitializedGetterName() {
    return this._memberAuthorityInitializedGetterName
  },
  set logInMutationType(value) {
    this._logInMutationType = value
  },
  get logInMutationType() {
    return this._logInMutationType
  },
  set logOutMutationType(value) {
    this._logOutMutationNType = value
  },
  get logOutMutationType() {
    return this._logOutMutationNType
  },
  isUserLoggedIn() {
    let store = this._store
    let getterName = this.loggedInGetterName
    return store.getters[getterName]
  },
  getMemberAuthority() {
    let store = this._store
    let getterName = this.memberAuthorityGetterName
    return store.getters[getterName]
  },
  isMemberAuthorityInitalized() {
    let store = this._store
    let getterName = this.memberAuthorityInitializedGetterName
    if (getterName !== null) {
      return store.getters[getterName]
    } else {
      return true
    }
  },
  // Vue install
  install(Vue) {
    Vue.prototype.$security = this

    Vue.directive('can', canDirective.directive())
  },
  // check for path and permissions
  check(access) {
    let hasAccess = true
    if (access.path !== undefined) {
      hasAccess = this.routes.check(access)
    }

    // loop thought the matched route to find if any of the routes require
    // policy check
    if (hasAccess === true) {
      let policy = access.matched.find(route => {
        return route.meta !== undefined && route.meta.policy !== undefined
      })

      if (policy !== undefined) {
        hasAccess = this.policy.check(policy.meta, true)

        // check for child policy
        if (getValue(access, 'meta.policy')) {
          hasAccess = this.policy.check(access.meta, true)
        }

        if (hasAccess === false) {
          hasAccess = { path: this.accessDeniedPage }
        }
      }
    }
    return hasAccess
  },
}

export const ROLE_IS_LOGGED_IN = 'ROLE_IS_LOGGED_IN'
export const ACTION_DENY = 'ACTION_DENY'
export const ACTION_ALLOW = 'ACTION_ALLOW'

// create a unique, global symbol name
// -----------------------------------

const SECURITY_KEY = Symbol.for(identifier)

const target = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : {}

// check if the target object has this symbol
// add it if it does not have the symbol, yet
// ------------------------------------------
const globalSymbols = Object.getOwnPropertySymbols(target)
const hasSecurity = globalSymbols.indexOf(SECURITY_KEY) > -1

if (!hasSecurity) {
  target[SECURITY_KEY] = security
}

export default target[SECURITY_KEY]
