<template>
  <layout class="row" :right-style="{ paddingTop: '0px' }" :isleftshow="false">
    <template #page_left>
      <div style="padding: 10px">
        <ElInput
          v-model="roleName"
          placeholder="角色名称"
          size="small"
          clearable
          @change="searchRole"
        ></ElInput>
      </div>
      <ElScrollbar style="height: calc(100% - 40px)">
        <ElCollapse v-model="activeNames">
          <ElCollapseItem
            v-for="(group, key) in showGroups"
            :key="key"
            :name="`${key + 1}`"
            :title="group.label"
          >
            <div
              v-for="(item, index) in group.list"
              :key="index"
              class="group_item"
              :class="{ active: activeRoleId === item.id }"
              @click="setActiveRole(item)"
            >
              <span>{{ item.name }}</span>
            </div>
          </ElCollapseItem>
        </ElCollapse>
      </ElScrollbar>
    </template>
    <template #page_right>
      <div class="page_content">
        <div
          style="
            height: 40px;
            display: flex;
            box-sizing: border-box;
            justify-content: space-between;
            padding: 10px 10px 0;
          "
        >
          <div v-if="currentRole.name" style="line-height: 40px">
            <span style="font-weight: bold; font-size: 18px"> 角色名称： </span>
            <span style="font-weight: bold; font-size: 20px; color: red">
              {{ currentRole.name }}
            </span>
          </div>
          <div>
            <ElButton
              v-if="activeRoleId != '1'"
              :disabled="!currentRole.name"
              type="primary"
              @click="editRole()"
            >
              编辑
            </ElButton>
            <ElButton
              v-if="activeRoleId != '1'"
              :disabled="!currentRole.name"
              type="success"
              @click="copyRole()"
            >
              复制
            </ElButton>
            <ElButton
              v-if="activeRoleId != '1'"
              :disabled="!currentRole.name"
              type="danger"
              @click="removeRole()"
            >
              删除
            </ElButton>
            <ElButton type="primary" @click="addRole()"> 添加 </ElButton>
            <ElButton
              :disabled="!currentRole.name"
              type="success"
              @click="saveSelect()"
            >
              保存
            </ElButton>
          </div>
        </div>
        <div v-if="!currentRole.name" class="no_role_tip">
          <span style="color: red; font-weight: bold; font-size: 18px"
            >请先添加角色吧！</span
          >
        </div>
        <ElTabs v-else v-model="tabActive" @tab-click="tabClick">
          <ElTabPane label="功能权限" name="second">
            <div
              style="
                padding: 5px 10px 2px;
                height: 100%;
                box-sizing: border-box;
              "
            >
              <Linkage
                :menu-list="menuList"
                :is-active="tabActive === 'second'"
              >
                <template #default="{ children, name, id, indeter }">
                  <ElCheckboxGroup v-model="menuCheckList" style="height: 100%">
                    <div data-level="一级" class="level_one">
                      <div class="menu_name">
                        <ElCheckbox
                          :value="id"
                          :indeterminate="indeter"
                          @change="
                          (v: boolean | string | number) =>
                            checkChange(
                              { id, indeter, children } as unknown as MenuTreeNode,
                              v,
                            )
                        "
                        >
                          {{ name }}
                        </ElCheckbox>
                      </div>
                      <div class="menu_content" style="flex: 1">
                        <div
                          v-for="item1 in (children as MenuTreeNode[] | undefined) || []"
                          :key="item1.id"
                          class="level_two"
                          data-level="二级"
                        >
                          <div class="menu_name">
                            <ElCheckbox
                              :value="item1.id"
                              :indeterminate="item1.indeter"
                              @change="
                              (v: boolean | string | number) =>
                                checkChange(item1, v)
                            "
                            >
                              {{ item1.name }}
                            </ElCheckbox>
                          </div>
                          <div
                            v-if="item1.children && item1.children.length > 0"
                            class="menu_content"
                            style="flex: 1"
                          >
                            <div
                              v-for="item2 in item1.children"
                              :key="item2.id"
                              data-level="三级"
                              :class="{
                                level_three: item2.type == 0,
                              }"
                              :style="{
                                display: item2.type == 1 ? 'inline' : 'flex',
                              }"
                            >
                              <div
                                class="menu_name"
                                :class="item2.type === 2 ? 'status-bar' : ''"
                              >
                                <ElCheckbox
                                  :value="item2.id"
                                  :indeterminate="item2.indeter"
                                  @change="
                                  (v: boolean | string | number) =>
                                    checkChange(item2, v)
                                "
                                >
                                  {{ item2.name }}
                                </ElCheckbox>
                              </div>
                              <div
                                v-if="
                                  item2.children && item2.children.length > 0
                                "
                                class="menu_content"
                                style="flex: 1"
                              >
                                <div
                                  v-for="item3 in item2.children"
                                  :key="item3.id"
                                  data-level="四级"
                                  :style="{
                                    display:
                                      item3.children &&
                                      item3.children[0] &&
                                      item3.children[0].type == 1
                                        ? 'flex'
                                        : 'inline',
                                  }"
                                >
                                  <div
                                    class="menu_name"
                                    :class="
                                      item3.type === 2 ? 'status-bar' : ''
                                    "
                                  >
                                    <ElCheckbox
                                      :value="item3.id"
                                      :indeterminate="false"
                                      @change="
                                      (v: boolean | string | number) =>
                                        checkChange(item3, v)
                                    "
                                    >
                                      {{ item3.name }}
                                    </ElCheckbox>
                                  </div>
                                </div>
                              </div>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </ElCheckboxGroup>
                </template>
              </Linkage>
            </div>
          </ElTabPane>
          <ElTabPane label="角色用户" name="third">
            <ul class="user_wrap">
              <li v-for="(item, index) in userLists" :key="index">
                <span style="color: #333">{{ item.account }}</span>
              </li>
            </ul>
          </ElTabPane>
        </ElTabs>
      </div>
    </template>
  </layout>
  <ElDialog
    v-model="editVisible"
    :title="isEdit ? '编辑角色' : '添加角色'"
    width="500px"
    :close-on-click-modal="false"
    @closed="onRoleDialogClosed"
  >
    <ElForm
      ref="editFormRef"
      :model="editForm"
      :rules="roleFormRules"
      label-width="90px"
    >
      <ElFormItem label="角色名称" prop="name">
        <ElInput
          v-model="editForm.name"
          placeholder="请输入角色名称"
          maxlength="15"
          show-word-limit
          clearable
        />
      </ElFormItem>
      <ElFormItem label="角色分组">
        <ElInput
          v-model="editForm.roleGroup"
          placeholder="请输入角色分组"
          maxlength="15"
          show-word-limit
          clearable
        />
      </ElFormItem>
    </ElForm>
    <template #footer>
      <div style="text-align: right">
        <ElButton @click="editVisible = false">取消</ElButton>
        <ElButton type="primary" :loading="saveLoading" @click="saveRole">
          保存
        </ElButton>
      </div>
    </template>
  </ElDialog>
</template>

<script setup lang="ts">
import layout from '@/components/leftRightLayout.vue'
import { computed, onMounted, reactive, ref } from 'vue'
import {
  addRoleApi,
  copyRoleApi,
  deleteRoleApi,
  getRoleListApi,
  getRolePermsApi,
  getRoleUsersApi,
  getSysMenusTreeApi,
  saveRoleAuthApi,
  updateRoleApi,
} from '@/api/role'
import useMenuTreeStore from '@/store/menuTree'
import { showConfirm } from '@/utils/ui'
import type {
  RoleItem,
  RoleFormPayload,
  RoleGroup,
  RoleUser,
  MenuTreeNode,
} from '@/types/api/role'
import type { FormInstance, FormRules } from 'element-plus'
import Linkage from './components/linkage.vue'

const roleName = ref('')
const activeNames = ref<string>('')
const activeRoleId = ref<string | number>('')
const tabActive = ref<'second' | 'third'>('second')
const currentRole = ref<RoleItem>({} as RoleItem)
const menuCheckList = ref<number[]>([])
const groupLists = ref<RoleItem[]>([])
const showGroups = ref<RoleGroup[]>([])
const userLists = ref<RoleUser[]>([])

const menuTreeStore = useMenuTreeStore()
const menuList = computed<MenuTreeNode[]>(() => menuTreeStore.menusTree)

// 新增/编辑角色弹窗
const editVisible = ref(false)
const isEdit = ref(false)
const saveLoading = ref(false)
const editFormRef = ref<FormInstance>()
const editForm = reactive<RoleFormPayload>({
  id: undefined,
  name: '',
  roleGroup: '',
})

const roleFormRules = reactive<FormRules<RoleFormPayload>>({
  name: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
})

function addRole() {
  isEdit.value = false
  editForm.id = undefined
  editForm.name = ''
  editForm.roleGroup = ''
  editVisible.value = true
}

function editRole() {
  isEdit.value = true
  editForm.id = currentRole.value.id
  editForm.name = currentRole.value.name || ''
  editForm.roleGroup = currentRole.value.roleGroup || ''
  editVisible.value = true
}

async function saveRole() {
  if (!editFormRef.value) return
  try {
    await editFormRef.value.validate()
  } catch {
    return
  }
  saveLoading.value = true
  try {
    const payload: RoleFormPayload = {
      name: editForm.name.trim(),
      ...(editForm.roleGroup ? { roleGroup: editForm.roleGroup.trim() } : {}),
    }
    if (isEdit.value && editForm.id !== undefined) {
      payload.id = editForm.id
      await updateRoleApi(payload)
      ElMessage({
        type: 'success',
        message: '编辑成功!',
        offset: window.innerHeight / 2,
      })
    } else {
      await addRoleApi(payload)
      ElMessage({
        type: 'success',
        message: '添加成功!',
        offset: window.innerHeight / 2,
      })
    }
    editVisible.value = false
    getList()
  } catch (e) {
    console.log(e)
  } finally {
    saveLoading.value = false
  }
}

function onRoleDialogClosed() {
  editFormRef.value?.clearValidate()
}

async function removeRole() {
  try {
    await showConfirm('确定删除选中的信息？', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }
  try {
    const res = await deleteRoleApi(currentRole.value.id)
    ElMessage({
      type: 'success',
      message: '删除成功!',
      offset: window.innerHeight / 2,
    })
    getList()
    return res
  } catch (e) {
    return e
  }
}

async function copyRole() {
  try {
    const res = await copyRoleApi(currentRole.value.id)
    if (res.code === 200) {
      ElMessage.success(res.message as string)
      getList(res.data)
    }
    return res
  } catch (e) {
    return e
  }
}

function buildGroups(list: RoleItem[]): RoleGroup[] {
  const arr: RoleGroup[] = []
  for (const iterator of list) {
    const group = arr.find((v) => v.label === iterator.roleGroup)
    if (group) {
      group.list.push(iterator)
    } else {
      arr.push({ label: iterator.roleGroup, list: [iterator] })
    }
  }
  return arr
}

function searchRole(value: string) {
  const filtered = groupLists.value.filter(
    (item: RoleItem) => String(item.name).indexOf(value) !== -1,
  )
  showGroups.value = buildGroups(filtered)
}

function tabClick() {
  if (tabActive.value === 'third') {
    getUsers()
  }
}

async function getUsers() {
  try {
    const res = await getRoleUsersApi(activeRoleId.value)
    userLists.value = (res.data || []) as RoleUser[]
  } catch (err) {
    console.log(err)
  }
}

function setActiveRole(item: RoleItem) {
  activeRoleId.value = item.id
  currentRole.value = item
  getActiveData()
}

function syncNodeCheckState(node: MenuTreeNode) {
  const children = node.children || []
  if (children.length === 0) return
  const childSelectedCount = children.filter((c) =>
    menuCheckList.value.includes(c.id),
  ).length
  const allSelected = children.every((c) => menuCheckList.value.includes(c.id))
  const anyIndeter = children.some((c) => c.indeter)
  node.indeter = (childSelectedCount > 0 && !allSelected) || anyIndeter
  const idx = menuCheckList.value.indexOf(node.id)
  if (childSelectedCount > 0) {
    // 只要有任一子节点被选中，父节点 id 也应进入 checklist（视觉上仍由 indeter 决定半/全勾）
    if (idx === -1) menuCheckList.value.push(node.id)
  } else {
    // 没有任何子节点被选中，移除父节点 id
    if (idx !== -1) menuCheckList.value.splice(idx, 1)
    node.indeter = false
  }
}

function findMenuNode(id: number | string): MenuTreeNode | undefined {
  const walk = (list?: MenuTreeNode[]): MenuTreeNode | undefined => {
    if (!list) return undefined
    for (const node of list) {
      if (node.id === id) return node
      const found = walk(node.children)
      if (found) return found
    }
    return undefined
  }
  return walk(menuList.value)
}

function updateParentsIndeter(
  list: MenuTreeNode[] | undefined,
  target: MenuTreeNode,
  parent?: MenuTreeNode,
) {
  if (!list) return
  for (const node of list) {
    if (node.id === target.id) {
      if (parent) syncNodeCheckState(parent)
      return
    }
    if (node.children && node.children.length > 0) {
      updateParentsIndeter(node.children, target, node)
      syncNodeCheckState(node)
    }
  }
}

function selectChecked(item: MenuTreeNode) {
  const checkList = menuCheckList.value
  const walk = (list?: MenuTreeNode[]) => {
    if (!list) return
    for (const node of list) {
      node.indeter = false
      if (!checkList.includes(node.id)) {
        checkList.push(node.id)
      }
      if (node.children && node.children.length > 0) walk(node.children)
    }
  }
  walk(item.children)
  // 同步当前节点的 id 和 indeterminate
  syncNodeCheckState(item)
  // 更新父级 indeterminate
  updateParentsIndeter(menuList.value, item)
}

function clearChecked(item: MenuTreeNode) {
  const checkList = menuCheckList.value
  const walk = (list?: MenuTreeNode[]) => {
    if (!list) return
    for (const node of list) {
      node.indeter = false
      const index = checkList.indexOf(node.id)
      if (index !== -1) checkList.splice(index, 1)
      if (node.children && node.children.length > 0) walk(node.children)
    }
  }
  walk(item.children)
  // 同步当前节点的 id 和 indeterminate
  syncNodeCheckState(item)
  updateParentsIndeter(menuList.value, item)
}

function checkChange(item: MenuTreeNode, value: boolean | string | number) {
  // 一级传入的是合成对象，需查找真实树节点，避免只改到临时对象的 indeter
  const target = findMenuNode(item.id) ?? item
  if (value || target.indeter) {
    selectChecked(target)
  } else {
    clearChecked(target)
  }
  target.indeter = false
}

async function saveSelect() {
  try {
    await showConfirm('确认保存？', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }
  try {
    const res = await saveRoleAuthApi({
      roleId: activeRoleId.value,
      menuIds: menuCheckList.value,
    })
    ElMessage.success(res.message as string)
    return res
  } catch (err) {
    console.log(err)
    return err
  }
}

async function getActiveData() {
  try {
    const res = await getRolePermsApi(activeRoleId.value)
    if (res.code == 200) {
      menuCheckList.value = (res.data?.menuIds as number[]) || []
      checkMenus(menuList.value)
    }
  } catch (e) {
    console.log(e)
  }
  await getUsers()
}

function checkMenus(menulist: MenuTreeNode[]) {
  const check = (list: MenuTreeNode[]) => {
    list.forEach((item) => {
      if (item.children && item.children.length > 0) {
        check(item.children)
        syncNodeCheckState(item)
      }
    })
  }
  check(menulist)
}

async function getSysMenus() {
  try {
    const res = await getSysMenusTreeApi()
    if (res.code == 200) {
      menuTreeStore.setMenuTree((res.data || []) as MenuTreeNode[])
    } else {
      await ElMessageBox.alert(res.message as string, '错误提示', {
        dangerouslyUseHTMLString: true,
      }).catch(() => {})
    }
  } catch (err) {
    console.log(err)
  }
}

async function getList(role: RoleItem | null = null) {
  try {
    const res = await getRoleListApi()
    const noSuperAdmin = (res.data || []).filter(
      (item: RoleItem) => item.name !== '超级管理员',
    )
    groupLists.value = noSuperAdmin
    showGroups.value = buildGroups(groupLists.value)

    if (groupLists.value.length > 0) {
      if (role) {
        activeRoleId.value = role.id
        currentRole.value = role
      } else {
        activeRoleId.value = groupLists.value[0].id
        currentRole.value = groupLists.value[0]
      }
      const currentActiveIndex = showGroups.value.findIndex(
        (item: RoleGroup) => item.label == currentRole.value.roleGroup,
      )
      activeNames.value =
        currentActiveIndex >= 0 ? String(currentActiveIndex + 1) : '1'
      await getActiveData()
    }
    if (groupLists.value.length === 0) {
      activeRoleId.value = ''
      currentRole.value = {} as RoleItem
      menuCheckList.value = []
      userLists.value = []
    }
    return res
  } catch (err) {
    console.log(err)
    return err
  }
}

onMounted(() => {
  getList()
  getSysMenus()
})
</script>

<style scoped>
.group_item {
  height: 40px;
  line-height: 40px;
  padding: 0 10px;
  background: #fff;
}
.group_item:hover,
.group_item.active {
  color: #333333;
  cursor: pointer;
  user-select: none;
}
.group_item:hover {
  background: #f5f7fa;
}
.group_item.active {
  background: #409eff;
}

.row :deep .el-collapse-item__header {
  padding-left: 10px;
  background: #efefef;
  color: #333;
  font-weight: bold;
}
.row :deep .el-collapse-item__content {
  padding-bottom: 0px;
}
.row :deep .el-tabs--top .el-tabs__item.is-top:nth-child(2),
.el-tabs--top .el-tabs__item.is-bottom:nth-child(2),
.el-tabs--bottom .el-tabs__item.is-top:nth-child(2),
.el-tabs--bottom .el-tabs__item.is-bottom:nth-child(2) {
  padding-left: 10px;
}
.page_content {
  height: 100%;
}
.page_content :deep .el-tabs {
  height: calc(100% - 40px);
}
.page_content :deep .el-tabs__content {
  height: calc(100% - 50px);
}
.page_content :deep .el-tab-pane {
  height: 100%;
}
.menu_content {
  flex: 1;
  border-left: 1px solid #efefef;
}
.menu_wrap {
  height: 100%;
  display: flex;
}
.no_role_tip {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.menu_name {
  display: inline-block;
  min-width: 160px;
  padding-left: 10px;
  height: 30px;
  line-height: 30px;
}
.level_one {
  display: flex;
  align-items: center;
}
.level_one + .level_one {
  border-top: 1px solid #efefef;
}
.level_two {
  display: flex;
  align-items: center;
}
.level_two + .level_two {
  border-top: 1px solid #efefef;
}
.level_three + .level_three {
  border-top: 1px solid #efefef;
}
.menu_module:hover,
.menu_module.active {
  background: #ddd;
}
.menu_module + .menu_module {
  border-top: 1px solid #ddd;
}
.data_gorup > div {
  display: inline-block;
  width: 160px;
  padding-left: 10px;
}
.data_gorup :deep .el-checkbox {
  display: block;
  line-height: 30px;
}
.user_wrap {
  height: 100%;
  overflow: auto;
}
.user_wrap li {
  display: inline-block;
  width: 240px;
  height: 50px;
  margin: 5px 15px;
  border-radius: 5px;
  border: 1px solid #ddd;
  padding: 12px 15px;
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* 悬停效果 */
.user_wrap li:hover {
  background-color: #f0f7ff;
  border-color: #b3d8ff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
  transform: translateY(-1px);
}
.status-bar :deep .el-checkbox__label {
  color: #67c23a;
  font-weight: bold;
}

.status-bar :deep .el-checkbox__input.is-checked + span.el-checkbox__label {
  color: #f56c6c !important;
}

.status-bar :deep .el-checkbox__input.is-checked .el-checkbox__inner {
  background-color: #f56c6c;
  border-color: #f56c6c;
}
</style>
