/**
 * 按钮级权限控制
 *
 * 参考 mp-vue/src/utils/permission.ts
 * 当前项目未引入 Pinia 的 user store，因此改为从 Nuxt state / localStorage 读取。
 */
import type { App } from 'vue'
import { useState } from '#imports'
import type { User } from '@/api/system/user/model'
import type { Role } from '@/api/system/role/model'
import type { Menu } from '@/api/system/menu/model'

type AuthzState = {
  roles: string[]
  authorities: string[]
}

const AUTHZ_STORAGE_KEY = 'Authz'

function uniqNonEmpty(values: Array<string | undefined | null>) {
  const seen = new Set<string>()
  for (const v of values) {
    const s = typeof v === 'string' ? v.trim() : ''
    if (!s || s === 'null' || s === 'undefined') continue
    seen.add(s)
  }
  return Array.from(seen)
}

function safeJsonParse(value: string): unknown {
  try {
    return JSON.parse(value)
  } catch {
    return undefined
  }
}

function normalizeStringArray(value: unknown): string[] {
  if (Array.isArray(value)) {
    return uniqNonEmpty(value.filter((v): v is string => typeof v === 'string'))
  }
  if (typeof value === 'string') {
    return uniqNonEmpty(
      value
        .split(',')
        .map((v) => v.trim())
        .filter(Boolean)
    )
  }
  return []
}

function readAuthzFromStorage(): AuthzState {
  if (!import.meta.client) return { roles: [], authorities: [] }
  try {
    const raw = localStorage.getItem(AUTHZ_STORAGE_KEY)
    if (!raw) return { roles: [], authorities: [] }
    const parsed = safeJsonParse(raw)
    if (!parsed || typeof parsed !== 'object') return { roles: [], authorities: [] }
    const obj = parsed as Record<string, unknown>
    return {
      roles: normalizeStringArray(obj.roles),
      authorities: normalizeStringArray(obj.authorities)
    }
  } catch {
    return { roles: [], authorities: [] }
  }
}

function writeAuthzToStorage(next: AuthzState) {
  if (!import.meta.client) return
  try {
    localStorage.setItem(AUTHZ_STORAGE_KEY, JSON.stringify(next))
  } catch {
    // ignore
  }
}

function getAuthzStateRef() {
  try {
    return useState<AuthzState>('authz', () => readAuthzFromStorage())
  } catch {
    return null
  }
}

function getAuthzSnapshot(): AuthzState {
  const state = getAuthzStateRef()
  return state?.value ?? readAuthzFromStorage()
}

export function setAuthz(next: Partial<AuthzState>) {
  const current = getAuthzSnapshot()
  const merged: AuthzState = {
    roles: normalizeStringArray(next.roles ?? current.roles),
    authorities: normalizeStringArray(next.authorities ?? current.authorities)
  }

  const state = getAuthzStateRef()
  if (state) state.value = merged
  writeAuthzToStorage(merged)
}

export function clearAuthz() {
  const state = getAuthzStateRef()
  if (state) state.value = { roles: [], authorities: [] }
  if (!import.meta.client) return
  try {
    localStorage.removeItem(AUTHZ_STORAGE_KEY)
  } catch {
    // ignore
  }
}

function getRoleCodesFromUser(user?: User | null) {
  const roleCodes: string[] = []

  const fromUserRoleCode = normalizeStringArray(user?.roleCode)
  roleCodes.push(...fromUserRoleCode)

  const roles: Role[] | undefined = user?.roles
  if (Array.isArray(roles)) {
    for (const role of roles) {
      if (!role) continue
      roleCodes.push(role.roleCode)
    }
  }

  return uniqNonEmpty(roleCodes)
}

function getAuthoritiesFromUser(user?: User | null) {
  const authorities: string[] = []
  const list: Menu[] | undefined = user?.authorities
  if (Array.isArray(list)) {
    for (const item of list) {
      if (!item) continue
      authorities.push(item.authority)
    }
  }
  return uniqNonEmpty(authorities)
}

export function setAuthzFromUser(user?: User | null) {
  setAuthz({
    roles: getRoleCodesFromUser(user),
    authorities: getAuthoritiesFromUser(user)
  })
}

/* 判断数组是否有某些值（全包含） */
function normalizeNeedles(value: string | string[]) {
  if (Array.isArray(value)) return uniqNonEmpty(value)
  const s = typeof value === 'string' ? value.trim() : ''
  return s ? [s] : []
}

function normalizeHaystack(array: (string | undefined)[]) {
  return uniqNonEmpty(array)
}

function arrayHas(array: (string | undefined)[], value: string | string[]): boolean {
  if (!value) return true
  if (!array) return false
  const needles = normalizeNeedles(value)
  if (needles.length === 0) return true
  const haystack = new Set(normalizeHaystack(array))
  for (let i = 0; i < needles.length; i++) {
    if (!haystack.has(needles[i])) return false
  }
  return true
}

/* 判断数组是否有任意值（任一包含） */
function arrayHasAny(array: (string | undefined)[], value: string | string[]): boolean {
  if (!value) return true
  if (!array) return false
  const needles = normalizeNeedles(value)
  if (needles.length === 0) return true
  const haystack = new Set(normalizeHaystack(array))
  for (let i = 0; i < needles.length; i++) {
    if (haystack.has(needles[i])) return true
  }
  return false
}

/**
 * 是否有某些角色
 * @param value 角色字符或字符数组
 */
export function hasRole(value: string | string[]): boolean {
  const { roles } = getAuthzSnapshot()
  return arrayHas(roles, value)
}

/**
 * 是否有任意角色
 * @param value 角色字符或字符数组
 */
export function hasAnyRole(value: string | string[]): boolean {
  const { roles } = getAuthzSnapshot()
  return arrayHasAny(roles, value)
}

/**
 * 是否有某些权限
 * @param value 权限字符或字符数组
 */
export function hasPermission(value: string | string[]): boolean {
  const { authorities } = getAuthzSnapshot()
  return arrayHas(authorities, value)
}

/**
 * 是否有任意权限
 * @param value 权限字符或字符数组
 */
export function hasAnyPermission(value: string | string[]): boolean {
  const { authorities } = getAuthzSnapshot()
  return arrayHasAny(authorities, value)
}

export default {
  install(app: App) {
    // 添加自定义指令
    app.directive('role', {
      mounted: (el, binding) => {
        if (!hasRole(binding.value)) el.parentNode?.removeChild(el)
      }
    })
    app.directive('any-role', {
      mounted: (el, binding) => {
        if (!hasAnyRole(binding.value)) el.parentNode?.removeChild(el)
      }
    })
    app.directive('permission', {
      mounted: (el, binding) => {
        if (!hasPermission(binding.value)) el.parentNode?.removeChild(el)
      }
    })
    app.directive('any-permission', {
      mounted: (el, binding) => {
        if (!hasAnyPermission(binding.value)) el.parentNode?.removeChild(el)
      }
    })
  }
}
