import type { App, Directive, DirectiveBinding } from 'vue'
import router from '@/router'

import { pinia, usePermissionStore } from '@/store'
import type { PermissionDirectiveValue } from '@/types/permission'

type PermissionHTMLElement = HTMLElement & {
	__permissionDisplay?: string
}

// 支持三种写法：v-permission="'add'"、v-permission="['menu', 'add']"、对象形式。
const resolvePermissionBinding = (binding: DirectiveBinding<PermissionDirectiveValue>) => {
	const currentMenuCode =
		typeof router.currentRoute.value.meta.menuCode === 'string'
			? router.currentRoute.value.meta.menuCode
			: undefined

	if (typeof binding.value === 'string') {
		return {
			menuCode: currentMenuCode,
			buttonCode: binding.value
		}
	}

	if (Array.isArray(binding.value)) {
		return {
			menuCode: binding.value[0],
			buttonCode: binding.value[1]
		}
	}

	return {
		menuCode: binding.value?.menuCode || currentMenuCode,
		buttonCode: binding.value?.buttonCode
	}
}

const updatePermissionVisibility = async (
	el: PermissionHTMLElement,
	binding: DirectiveBinding<PermissionDirectiveValue>
) => {
	if (el.__permissionDisplay === undefined) {
		el.__permissionDisplay = el.style.display
	}

	const { menuCode, buttonCode } = resolvePermissionBinding(binding)
	if (!menuCode || !buttonCode) {
		el.style.display = 'none'
		return
	}

	const permissionStore = usePermissionStore(pinia)
	await permissionStore.ensureButtonPermissions(menuCode)

	const allowed = permissionStore.hasButtonAccess(menuCode, buttonCode)
	// 自定义指令只负责隐藏无权限按钮，不改变按钮原有业务逻辑。
	el.style.display = allowed ? el.__permissionDisplay || '' : 'none'
}

const permissionDirective: Directive<PermissionHTMLElement, PermissionDirectiveValue> = {
	mounted(el, binding) {
		void updatePermissionVisibility(el, binding)
	},
	updated(el, binding) {
		void updatePermissionVisibility(el, binding)
	}
}

export const installPermissionDirective = (app: App) => {
	app.directive('permission', permissionDirective)
}
