<template>
  <div class="page-container perm-page-container">
    <ProSplitLayout :side-width="260">
      <template #side>
        <div class="role-side">
          <div class="role-side-header">选择角色</div>
          <a-input-search
            v-model:value="roleSearch"
            placeholder="搜索角色"
            allow-clear
            class="role-search"
          />
          <div class="role-list">
            <div
              v-for="role in filteredRoles"
              :key="role.id"
              :class="['role-item', { active: selectedRole?.id === role.id }]"
              @click="selectRole(role)"
            >
              <div class="role-item-name">{{ role.role_name }}</div>
              <div class="role-item-code">{{ role.role_key }}</div>
            </div>
            <a-empty
              v-if="!filteredRoles.length"
              :image="null"
              description="无匹配角色"
              class="role-empty"
            />
          </div>
        </div>
      </template>

      <template #main>
        <div v-if="selectedRole" class="perm-main">
          <div class="perm-header">
            <div class="perm-header-left">
              <h3>{{ selectedRole.role_name }}</h3>
              <a-tag color="blue">{{ selectedRole.role_key }}</a-tag>
              <span class="perm-count">已选 {{ checkedKeys.length }} 项权限</span>
            </div>
            <div class="perm-header-right">
              <span class="perm-legend">
                <FolderOutlined /><span>目录</span>
                <FileOutlined /><span>菜单</span>
                <CheckSquareOutlined /><span>按钮</span>
              </span>
              <a-button
                type="primary"
                :loading="saving"
                :disabled="!dirty"
                @click="handleSave"
              >
                保存
              </a-button>
            </div>
          </div>

          <div class="perm-tree-wrapper">
            <a-spin :spinning="loadingDetail" class="perm-tree-spin">
              <a-tree
                v-if="treeData.length"
                v-model:checkedKeys="checkedKeys"
                :tree-data="treeData"
                :field-names="{ title: 'title', key: 'key', children: 'children' }"
                checkable
                default-expand-all
                block-node
                class="perm-tree"
              >
                <template #title="{ title, menu_type, perms }">
                  <span :class="['tree-node', `tree-node--${menu_type}`]">
                    <span class="tree-node-icon">
                      <FolderOutlined v-if="menu_type === 'M'" />
                      <FileOutlined v-else-if="menu_type === 'C'" />
                      <CheckSquareOutlined v-else />
                    </span>
                    <span class="tree-node-name">{{ title }}</span>
                    <span v-if="perms" class="tree-node-perms">{{ perms }}</span>
                    <span class="tree-node-type">{{ menu_type === 'M' ? '目录' : menu_type === 'C' ? '菜单' : '按钮' }}</span>
                  </span>
                </template>
              </a-tree>
              <a-empty v-else description="暂无菜单数据" />
            </a-spin>
          </div>
        </div>

        <div v-else class="perm-placeholder">
          <a-empty description="请从左侧选择角色" />
        </div>
      </template>
    </ProSplitLayout>
  </div>
</template>

<script setup lang="ts">
import {
  CheckSquareOutlined,
  FileOutlined,
  FolderOutlined,
} from "@antdv-next/icons";
import { message } from "antdv-next";
import { computed, onMounted, ref } from "vue";

import { getRoleAll } from "@/api/role";
import { request } from "@/utils/request";
import { getMenuTree } from "@/api/menu";
import ProSplitLayout from "@/components/Pro/ProSplitLayout/index.vue";

interface MenuItem {
  id: string;
  menu_name: string;
  parent_id: string | null;
  menu_type: "M" | "C" | "F";
  order_num: number;
  perms?: string;
  children?: MenuItem[];
}

interface RoleItem {
  id: string;
  role_name: string;
  role_key: string;
  menu_ids: string[];
}

interface TreeNode {
  key: string;
  title: string;
  menu_type: string;
  perms?: string;
  children?: TreeNode[];
}

const loadingDetail = ref(false);
const saving = ref(false);
const dirty = ref(false);
const roleSearch = ref("");

const roleList = ref<RoleItem[]>([]);
const selectedRole = ref<RoleItem | null>(null);
const checkedKeys = ref<string[]>([]);
const treeData = ref<TreeNode[]>([]);

const filteredRoles = computed(() => {
  const kw = roleSearch.value.toLowerCase();
  if (!kw) return roleList.value;
  return roleList.value.filter(
    (r) =>
      r.role_name.toLowerCase().includes(kw) ||
      r.role_key.toLowerCase().includes(kw),
  );
});

function transformTree(items: MenuItem[]): TreeNode[] {
  return items
    .sort((a, b) => a.order_num - b.order_num)
    .map((item) => ({
      key: item.id,
      title: item.menu_name,
      menu_type: item.menu_type,
      perms: item.perms || undefined,
      children: item.children ? transformTree(item.children) : undefined,
    }));
}

async function loadRoles() {
  const res: any = await getRoleAll();
  if (res.code === 1) {
    roleList.value = res.data || [];
  }
}

async function selectRole(role: RoleItem) {
  if (dirty.value && selectedRole.value && selectedRole.value.id !== role.id) {
    message.warning("当前角色有未保存的更改，请先保存");
    return;
  }
  selectedRole.value = role;
  dirty.value = false;
  loadingDetail.value = true;
  try {
    const res: any = await request.get("/sys/role/detail", {
      params: { id: role.id },
    });
    if (res.code === 1 && res.data) {
      checkedKeys.value = res.data.menu_ids || [];
    }
  } catch {
    message.error("加载角色权限失败");
  } finally {
    loadingDetail.value = false;
  }
}

async function handleSave() {
  if (!selectedRole.value) return;
  saving.value = true;
  try {
    await request.post("/sys/role/save", {
      id: selectedRole.value.id,
      role_name: selectedRole.value.role_name,
      role_key: selectedRole.value.role_key,
      menu_ids: checkedKeys.value,
    });
    dirty.value = false;
    message.success("保存成功");
  } catch {
    message.error("保存失败");
  } finally {
    saving.value = false;
  }
}

onMounted(async () => {
  const [treeRes] = await Promise.all([getMenuTree(), loadRoles()]);
  if ((treeRes as any).code === 1) {
    treeData.value = transformTree((treeRes as any).data || []);
  }
});
</script>

<style lang="scss">
/* 权限管理页面专用：修复ProSplitLayout overflow:hidden导致滚动条被裁剪 */
.perm-page-container .pro-split-main {
  overflow: visible !important;
}
</style>

<style scoped lang="scss">
.role-side {
  display: flex;
  flex-direction: column;
  height: 100%;

  .role-side-header {
    font-size: 15px;
    font-weight: 600;
    padding: 12px 16px 8px;
    color: var(--ant-text-color, #333);
  }

  .role-search {
    padding: 0 12px 8px;
  }

  .role-list {
    flex: 1;
    overflow-y: auto;
    padding: 0 8px;
  }

  .role-item {
    display: flex;
    flex-direction: column;
    padding: 10px 12px;
    margin-bottom: 4px;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.2s;

    &:hover {
      background: var(--ant-primary-1, #e6f7ff);
    }

    &.active {
      background: var(--ant-primary-color, #1890ff);
      color: #fff;

      .role-item-code {
        color: rgba(255, 255, 255, 0.75);
      }
    }

    .role-item-name {
      font-weight: 500;
      font-size: 13px;
    }

    .role-item-code {
      font-size: 11px;
      color: var(--ant-text-color-secondary, #999);
      margin-top: 2px;
    }
  }

  .role-empty {
    margin-top: 40px;
  }
}

.perm-main {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;

  .perm-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 0 12px;
    border-bottom: 1px solid var(--ant-border-color, #f0f0f0);
    margin-bottom: 12px;
    flex-shrink: 0;
  }

  .perm-header-left {
    display: flex;
    align-items: center;
    gap: 10px;

    h3 {
      margin: 0;
      font-size: 16px;
    }

    .perm-count {
      font-size: 12px;
      color: var(--ant-text-color-secondary, #999);
    }
  }

  .perm-header-right {
    display: flex;
    align-items: center;
    gap: 12px;
  }

  .perm-legend {
    display: inline-flex;
    align-items: center;
    gap: 4px 12px;
    font-size: 12px;
    color: var(--ant-text-color-secondary, #999);
    flex-wrap: wrap;

    .anticon {
      font-size: 12px;
      opacity: 0.65;
    }
  }

  .perm-tree-wrapper {
    flex: 1;
    min-height: 0;
    overflow: hidden;
  }

  .perm-tree-spin {
    height: 100%;
    overflow-y: auto;

    :deep(.ant-spin-container),
    :deep(.ant-spin-nested-loading) {
      height: 100%;
    }
  }

  .perm-tree {
    min-height: 100%;
  }
}

.tree-node {
  display: inline-flex;
  align-items: center;
  gap: 6px;

  .tree-node-icon {
    font-size: 14px;
    opacity: 0.65;
  }

  .tree-node--M & {
    font-weight: 600;
  }

  .tree-node--C & {
    font-weight: 500;
  }

  .tree-node-name {
    white-space: nowrap;
  }

  .tree-node-perms {
    font-size: 11px;
    color: var(--ant-text-color-secondary, #999);
    font-family: monospace;
    margin-left: 4px;
  }

  .tree-node-type {
    font-size: 10px;
    color: var(--ant-text-color-tertiary, #bbb);
    margin-left: 4px;
    padding: 0 4px;
    border: 1px solid var(--ant-border-color, #f0f0f0);
    border-radius: 3px;
  }
}

.perm-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}
</style>
