<template>
  <div class="vm-eps-perm">
    <div class="vm-eps-perm__top">
      <vm-group-cascader
        class="vm-eps-perm__cascader"
        :model-value="modelValue ?? ''"
        :tree="epsTree"
        multiple
        :placeholder="placeholder ?? crudLabel('pickPerm')"
        :clearable="true"
        :editable="false"
        @update:model-value="(v) => emit('update:modelValue', v)"
      />
      <div v-if="$slots.suffix" class="vm-eps-perm__suffix">
        <slot name="suffix" />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { crudLabel } from '../crud/style'
import { ref, computed, watch, onMounted } from 'vue'
import { api } from '../api/client'
import type { EpsSide } from '../lib/eps'
import type {
  EpsApiItem,
  EpsEntity,
  EpsModuleMap,
} from '../../../typings/admin/comm/eps'
import type { GroupCascaderNode } from '../../../typings/admin/comm/cascader'

defineOptions({ name: 'vm-eps-perm-picker' })

const props = withDefaults(
  defineProps<{
    modelValue?: string
    placeholder?: string
    /** EPS 来源：admin 后台 / app 前端 */
    side?: EpsSide
  }>(),
  {
    placeholder: undefined,
    side: 'admin',
  },
)

const emit = defineEmits<{
  'update:modelValue': [v: string]
}>()

const eps = ref<EpsModuleMap>({})

function controllerLabel(ent: EpsEntity) {
  return (
    ent.info?.type?.name ||
    ent.prefix.split('/').filter(Boolean).pop() ||
    ent.prefix
  )
}

function permOf(api: EpsApiItem) {
  return api.perms?.[0] || `${api.method}:${api.path}`
}

const epsTree = computed<GroupCascaderNode[]>(() => {
  return Object.keys(eps.value)
    .sort()
    .map((mod) => ({
      label: mod,
      value: `mod:${mod}`,
      isLeaf: false,
      children: (eps.value[mod] || []).map((ent) => ({
        label: controllerLabel(ent),
        value: `ent:${ent.prefix}`,
        isLeaf: false,
        children: (ent.api || []).map((api) => {
          const perm = permOf(api)
          return {
            label: api.summary || api.path || perm,
            value: perm,
            isLeaf: true,
          }
        }),
      })),
    }))
})

async function loadSideEps() {
  try {
    eps.value = (await loadEps(false, props.side)) || {}
  } catch {
    eps.value = {}
  }
}

onMounted(() => void loadSideEps())
watch(() => props.side, () => void loadSideEps())
</script>

<style lang="scss" scoped>
.vm-eps-perm {
  width: 100%;
}

.vm-eps-perm__top {
  display: flex;
  min-width: 0;
  align-items: flex-start;
  gap: 8px;
}

.vm-eps-perm__cascader {
  flex: 1 1 auto;
  min-width: 0;
}

.vm-eps-perm__suffix {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
}
</style>
