import { DirectiveBinding } from 'vue'
import { useUserStore } from '@/store'

type PermissionValue = string | string[] | { value?: string | string[]; mode?: 'remove' | 'disable' }

function toList(value: PermissionValue): string[] {
  if (Array.isArray(value)) return value
  if (typeof value === 'string') return [value]
  if (value && typeof value === 'object') {
    if (Array.isArray(value.value)) return value.value
    if (typeof value.value === 'string') return [value.value]
  }
  return []
}

function collectPermissionCodes(input: any, out: Set<string>) {
  if (!input) return
  if (typeof input === 'string') {
    out.add(input)
    return
  }
  if (Array.isArray(input)) {
    input.forEach((item) => collectPermissionCodes(item, out))
    return
  }
  if (typeof input === 'object') {
    const code = input.code || input.permission || input.auth || input.value
    if (typeof code === 'string') out.add(code)
    collectPermissionCodes(input.children, out)
    collectPermissionCodes(input.permissions, out)
    collectPermissionCodes(input.pmn_application, out)
  }
}

function hasPermission(value: PermissionValue) {
  const expected = toList(value)
  if (expected.length === 0) {
    throw new Error(`need permissions! Like v-permission="['admin','system:user:create']"`)
  }

  const userStore = useUserStore()
  const role = userStore.role || userStore.userInfo?.role || ''
  if (role === '*' || expected.includes('*') || expected.includes(role)) return true

  const codes = new Set<string>()
  collectPermissionCodes(userStore.userInfo?.permissions, codes)
  collectPermissionCodes(userStore.userInfo?.pmn_application, codes)
  collectPermissionCodes(userStore.userInfo?.roles, codes)
  return expected.some((item) => codes.has(item))
}

function checkPermission(el: HTMLElement, binding: DirectiveBinding) {
  const value = binding.value as PermissionValue
  const mode = typeof value === 'object' && !Array.isArray(value) ? value.mode || 'remove' : 'remove'
  if (!hasPermission(value)) {
    if (mode === 'disable') {
      el.setAttribute('disabled', 'true')
      el.classList.add('is-disabled')
      return
    }
    if (el.parentNode) {
      el.parentNode.removeChild(el)
    }
  }
}

export default {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    checkPermission(el, binding)
  },
  updated(el: HTMLElement, binding: DirectiveBinding) {
    checkPermission(el, binding)
  },
}
