<template>
  <section class="org-permission-tree-card">
    <aside class="org-tree-panel" aria-label="组织树">
      <div class="panel-head">
        <div>
          <p class="eyebrow">组织架构</p>
          <h3>组织树</h3>
        </div>
        <span class="status-pill">{{ organizationCount }} 个组织节点</span>
      </div>

      <label class="org-tree-search">
        <span aria-hidden="true">⌕</span>
        <input v-model.trim="treeSearchKeyword" type="search" placeholder="请输入组织名称" />
      </label>

      <div v-if="displayedOrganizationNodes.length" class="org-tree-list">
        <OrgTreeBranch
          v-for="node in displayedOrganizationNodes"
          :key="nodeKey(node)"
          :node="node"
          :level="0"
          :selected-id="effectiveSelectedNodeId"
          :expanded-ids="expandedNodeIds"
          @toggle="toggleNode"
          @select="selectNode"
          @open-menu="openNodeMenu"
        />
      </div>
      <p v-else class="empty-state">{{ treeSearchKeyword ? "没有匹配的组织节点" : "暂无组织节点" }}</p>
    </aside>

    <section class="employee-panel" aria-label="当前组织人员">
      <div class="panel-head employee-head">
        <div>
          <p class="eyebrow">组织人员</p>
          <h3>{{ selectedNodeLabel }}</h3>
          <p class="permission-note">当前节点人员共 {{ selectedEmployees.length }} 人</p>
        </div>
        <button
          class="primary-button"
          type="button"
          :disabled="!canAddEmployee"
          @click="addEmployee"
        >
          新增人员
        </button>
      </div>

      <label class="employee-search-field">
        <span>搜索</span>
        <input v-model="searchKeyword" type="search" placeholder="搜索人员姓名或手机号" />
      </label>

      <div class="table-wrap">
        <table class="data-table compact-table org-employee-table">
          <thead>
            <tr>
              <th>姓名</th>
              <th>电话号码 / 账号</th>
              <th>岗位</th>
              <th>角色</th>
              <th>状态</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="employee in paginatedEmployees" :key="employeeKey(employee)">
              <td>{{ employeeName(employee) }}</td>
              <td>{{ employeePhone(employee) }}</td>
              <td>{{ employeePosition(employee) }}</td>
              <td>{{ employeeRoles(employee) }}</td>
              <td>
                <StatusBadge :value="employee?.status" :text="employeeStatus(employee)" />
              </td>
              <td class="action-row">
                <button class="ghost-button" type="button" @click="authorizeEmployee(employee)">授权</button>
                <button class="ghost-button danger" type="button" @click="removeEmployee(employee)">移除</button>
              </td>
            </tr>
            <tr v-if="!filteredEmployees.length">
              <td colspan="6" class="empty-row">{{ selectedEmployees.length ? "没有符合条件的人员" : "当前节点暂无人员" }}</td>
            </tr>
          </tbody>
        </table>
      </div>

      <PaginationBar v-model:page="employeePage" v-model:page-size="employeePageSize" :total="filteredEmployees.length" />
    </section>
  </section>
</template>

<script setup>
import { computed, defineComponent, h, ref, watch } from "vue";

import PaginationBar from "./PaginationBar.vue";
import StatusBadge from "./StatusBadge.vue";
import { usePagination } from "../utils/pagination";

const props = defineProps({
  nodes: {
    type: Array,
    default: () => []
  },
  selectedNodeId: {
    type: [Number, String],
    default: ""
  }
});

const emit = defineEmits([
  "select-node",
  "node-contextmenu",
  "add-employee",
  "authorize-employee",
  "remove-employee"
]);

const OrgTreeBranch = defineComponent({
  name: "OrgTreeBranch",
  props: {
    node: {
      type: Object,
      required: true
    },
    level: {
      type: Number,
      default: 0
    },
    selectedId: {
      type: [Number, String],
      default: ""
    },
    expandedIds: {
      type: Object,
      required: true
    }
  },
  emits: ["toggle", "select", "open-menu"],
  setup(branchProps, { emit: branchEmit }) {
    const childOrganizations = computed(() => organizationChildren(branchProps.node));
    const currentNodeId = computed(() => getNodeId(branchProps.node));
    const hasChildren = computed(() => childOrganizations.value.length > 0);
    const isExpanded = computed(() => branchProps.expandedIds.has(String(currentNodeId.value)));
    const isSelected = computed(() => sameId(currentNodeId.value, branchProps.selectedId));

    function selectCurrent() {
      branchEmit("select", branchProps.node);
    }

    function toggleCurrent(event) {
      event.stopPropagation();
      branchEmit("toggle", branchProps.node);
    }

    function openMenu(event) {
      event.preventDefault();
      event.stopPropagation();
      branchEmit("open-menu", {
        node: branchProps.node,
        x: event.clientX,
        y: event.clientY
      });
    }

    return () =>
      h("div", { class: "org-tree-branch" }, [
        h(
          "button",
          {
            type: "button",
            class: ["org-tree-node", { active: isSelected.value }],
            style: { "--tree-level": branchProps.level },
            onClick: selectCurrent,
            onContextmenu: openMenu
          },
          [
            h(
              "span",
              {
                class: ["tree-toggle", { hidden: !hasChildren.value }],
                onClick: toggleCurrent
              },
              hasChildren.value ? (isExpanded.value ? "⌄" : "›") : ""
            ),
            h("span", { class: ["tree-node-icon", `tree-node-icon-${String(branchProps.node?.node_type || "ORG").toLowerCase()}`] }, nodeTypeShortLabel(branchProps.node?.node_type)),
            h("span", { class: "tree-node-main" }, [
              h("strong", null, nodeLabel(branchProps.node)),
              h("small", null, `${nodeTypeLabel(branchProps.node?.node_type)} · ${employeeCount(branchProps.node)} 人`)
            ])
          ]
        ),
        hasChildren.value && isExpanded.value
          ? h(
              "div",
              { class: "org-tree-children" },
              childOrganizations.value.map((child) =>
                h(OrgTreeBranch, {
                  key: nodeKey(child),
                  node: child,
                  level: branchProps.level + 1,
                  selectedId: branchProps.selectedId,
                  expandedIds: branchProps.expandedIds,
                  onToggle: (node) => branchEmit("toggle", node),
                  onSelect: (node) => branchEmit("select", node),
                  onOpenMenu: (payload) => branchEmit("open-menu", payload)
                })
              )
            )
          : null
      ]);
  }
});

const searchKeyword = ref("");
const treeSearchKeyword = ref("");
const expandedNodeIds = ref(new Set());

const organizationNodes = computed(() => (Array.isArray(props.nodes) ? props.nodes : []).filter(isOrganizationNode));
const displayedOrganizationNodes = computed(() => filterOrgTreeByKeyword(organizationNodes.value, treeSearchKeyword.value));
const flattenedOrganizations = computed(() => flattenOrganizations(organizationNodes.value));
const organizationCount = computed(() => flattenedOrganizations.value.length);
const firstRootNode = computed(() => organizationNodes.value[0] || null);
const requestedNode = computed(() => findNodeById(organizationNodes.value, props.selectedNodeId));
const selectedNode = computed(() => requestedNode.value || firstRootNode.value);
const effectiveSelectedNodeId = computed(() => getNodeId(selectedNode.value) ?? "");
const selectedNodeLabel = computed(() => selectedNode.value ? nodeLabel(selectedNode.value) : "未选择组织");
const selectedEmployees = computed(() => Array.isArray(selectedNode.value?.employees) ? selectedNode.value.employees : []);
const canAddEmployee = computed(() => ["DEPARTMENT", "GROUP"].includes(selectedNode.value?.node_type));
const filteredEmployees = computed(() => {
  const keyword = searchKeyword.value.trim().toLowerCase();
  if (!keyword) {
    return selectedEmployees.value;
  }
  return selectedEmployees.value.filter((employee) =>
    [employeeName(employee), employeePhone(employee), employee.employee_code, employee.username]
      .filter(Boolean)
      .join(" ")
      .toLowerCase()
      .includes(keyword)
  );
});

const {
  page: employeePage,
  pageSize: employeePageSize,
  rows: paginatedEmployees
} = usePagination(filteredEmployees);

watch(
  organizationNodes,
  (nodes) => {
    expandedNodeIds.value = new Set(flattenOrganizations(nodes).map((node) => String(getNodeId(node))));
  },
  { immediate: true }
);

watch(effectiveSelectedNodeId, () => {
  searchKeyword.value = "";
});

function organizationChildren(node) {
  return (Array.isArray(node?.children) ? node.children : []).filter(isOrganizationNode);
}

function filterOrgTreeByKeyword(nodes, keyword) {
  const normalizedKeyword = String(keyword || "").trim().toLowerCase();
  if (!normalizedKeyword) {
    return nodes;
  }
  return (nodes || [])
    .map((node) => {
      const children = filterOrgTreeByKeyword(organizationChildren(node), normalizedKeyword);
      const text = [nodeLabel(node), nodeTypeLabel(node?.node_type), node?.dept_code, node?.manager_name, ...(node?.manager_names || [])]
        .filter(Boolean)
        .join(" ")
        .toLowerCase();
      if (text.includes(normalizedKeyword) || children.length) {
        return {
          ...node,
          children
        };
      }
      return null;
    })
    .filter(Boolean);
}

function isOrganizationNode(node) {
  return Boolean(node) && node.node_type !== "EMPLOYEE" && !node.employee_id;
}

function flattenOrganizations(nodes) {
  return (nodes || []).flatMap((node) => [node, ...flattenOrganizations(organizationChildren(node))]);
}

function findNodeById(nodes, id) {
  if (id === "" || id === null || id === undefined) {
    return null;
  }
  for (const node of nodes || []) {
    if (sameId(getNodeId(node), id)) {
      return node;
    }
    const matchedChild = findNodeById(organizationChildren(node), id);
    if (matchedChild) {
      return matchedChild;
    }
  }
  return null;
}

function sameId(left, right) {
  return String(left ?? "") === String(right ?? "");
}

function getNodeId(node) {
  return node?.node_id ?? node?.id ?? node?.value ?? "";
}

function nodeKey(node) {
  return getNodeId(node) || nodeLabel(node);
}

function nodeLabel(node) {
  return node?.node_label || node?.label || node?.name || "未命名组织";
}

function nodeTypeLabel(type) {
  return {
    COMPANY: "总公司",
    BRANCH: "分公司",
    DEPARTMENT: "部门",
    GROUP: "小组"
  }[type] || "组织";
}

function nodeTypeShortLabel(type) {
  return {
    COMPANY: "总",
    BRANCH: "分",
    DEPARTMENT: "部",
    GROUP: "组"
  }[type] || "组";
}

function employeeCount(node) {
  const employeeKeys = new Set();
  collectEmployeeKeys(node, employeeKeys);
  return employeeKeys.size;
}

function collectEmployeeKeys(node, employeeKeys) {
  if (!node) {
    return;
  }
  for (const employee of Array.isArray(node.employees) ? node.employees : []) {
    employeeKeys.add(String(employeeKey(employee)));
  }
  for (const child of Array.isArray(node.children) ? node.children : []) {
    if (isOrganizationNode(child)) {
      collectEmployeeKeys(child, employeeKeys);
    } else if (child?.employee_id) {
      employeeKeys.add(String(employeeKey(child)));
    }
  }
}

function toggleNode(node) {
  const targetId = String(getNodeId(node));
  const next = new Set(expandedNodeIds.value);
  if (next.has(targetId)) {
    next.delete(targetId);
  } else {
    next.add(targetId);
  }
  expandedNodeIds.value = next;
}

function selectNode(node) {
  emit("select-node", node);
}

function openNodeMenu(payload) {
  emit("node-contextmenu", payload);
}

function employeeName(employee) {
  return employee?.employee_name || employee?.display_name || employee?.name || employee?.username || "未命名人员";
}

function employeePhone(employee) {
  return employee?.mobile || employee?.phone || employee?.username || "未设置电话号码";
}

function employeePosition(employee) {
  return employee?.job_title || employee?.position || employee?.post_name || employee?.title || "未设置岗位";
}

function employeeRoles(employee) {
  if (Array.isArray(employee?.role_names) && employee.role_names.length) {
    return employee.role_names.join("、");
  }
  if (Array.isArray(employee?.roles) && employee.roles.length) {
    return employee.roles.map((role) => role.role_name || role.name || role.role_code || role).join("、");
  }
  return employee?.role_name || employee?.role || "未设置角色";
}

function employeeStatus(employee) {
  if (employee?.status === "ACTIVE") {
    return "启用";
  }
  if (employee?.status === "INACTIVE") {
    return "停用";
  }
  return "未设置状态";
}

function employeeKey(employee) {
  return employee?.employee_id || employee?.user_id || employee?.username || employeeName(employee);
}

function employeeWithParent(employee) {
  return {
    ...employee,
    parent_node_id: getNodeId(selectedNode.value),
    parent_node_label: nodeLabel(selectedNode.value)
  };
}

function addEmployee() {
  if (canAddEmployee.value) {
    emit("add-employee", selectedNode.value);
  }
}

function authorizeEmployee(employee) {
  emit("authorize-employee", employeeWithParent(employee));
}

function removeEmployee(employee) {
  emit("remove-employee", employeeWithParent(employee));
}
</script>

<style scoped>
.org-permission-tree-card {
  display: grid;
  grid-template-columns: minmax(300px, 0.32fr) minmax(680px, 1fr);
  gap: 16px;
  width: 100%;
  min-width: 1040px;
  min-height: min(620px, calc(100vh - 270px));
}

.org-tree-panel,
.employee-panel {
  border: 1px solid #dbe3ef;
  border-radius: 18px;
  background: #ffffff;
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06);
}

.org-tree-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 14px 10px 14px 12px;
}

.employee-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  padding: 18px;
}

.panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.employee-head {
  align-items: center;
}

.eyebrow {
  margin: 0 0 6px;
  color: #2563eb;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.panel-head h3 {
  margin: 0;
  color: #0f172a;
  font-size: 18px;
}

.permission-note,
.empty-state {
  margin: 6px 0 0;
  color: #64748b;
  font-size: 13px;
}

.status-pill {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  border-radius: 999px;
  padding: 6px 10px;
  background: #dbeafe;
  color: #1d4ed8;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}

.org-tree-search {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  align-items: center;
  gap: 6px;
  min-height: 36px;
  margin-bottom: 12px;
  padding: 0 10px;
  border: 1px solid #d8e0eb;
  border-radius: 6px;
  background: #ffffff;
  color: #8b97a8;
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

.org-tree-search:focus-within {
  border-color: #409eff;
  box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.12);
}

.org-tree-search span {
  display: inline-grid;
  place-items: center;
  color: #9aa7b8;
  font-size: 17px;
  line-height: 1;
}

.org-tree-search input {
  width: 100%;
  min-width: 0;
  border: 0;
  background: transparent;
  color: #303847;
  font-size: 14px;
  outline: none;
}

.org-tree-search input::placeholder {
  color: #a8b1bf;
}

.org-tree-list {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
  min-height: 0;
  overflow-y: auto;
  padding: 2px 2px 8px 0;
  border-radius: 8px;
  background: #ffffff;
}

.org-tree-branch {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.org-tree-node {
  display: grid;
  grid-template-columns: 18px 24px minmax(0, 1fr);
  align-items: center;
  gap: 6px;
  width: 100%;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 7px 8px 7px calc(6px + var(--tree-level, 0) * 22px);
  background: transparent;
  color: #4b5565;
  text-align: left;
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease, box-shadow 0.14s ease;
}

.org-tree-node:hover,
.org-tree-node.active {
  border-color: transparent;
  background: #eaf4ff;
  color: #1f6fbd;
  box-shadow: inset 3px 0 0 #409eff;
}

.tree-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  padding: 0;
  background: transparent;
  color: #9098a8;
  font-size: 18px;
  font-weight: 800;
  line-height: 1;
}

.tree-toggle.hidden {
  visibility: hidden;
}

.tree-node-icon {
  display: inline-grid;
  place-items: center;
  width: 23px;
  height: 23px;
  border-radius: 5px;
  background: #f1f5f9;
  color: #607086;
  font-size: 12px;
  font-weight: 850;
}

.org-tree-node.active .tree-node-icon,
.org-tree-node:hover .tree-node-icon {
  background: #409eff;
  color: #ffffff;
}

.tree-node-main {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}

.tree-node-main strong,
.tree-node-main small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tree-node-main strong {
  font-size: 14px;
  font-weight: 700;
}

.tree-node-main small {
  color: #8b97a8;
  font-size: 11px;
}

.org-tree-children {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.employee-search-field {
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
}

.employee-search-field span {
  color: #475569;
  font-size: 13px;
  font-weight: 800;
}

.employee-search-field input {
  width: 100%;
  border: 1px solid rgba(15, 23, 42, 0.1);
  border-radius: 16px;
  padding: 11px 14px;
  background: #ffffff;
  color: #0f172a;
  font-size: 14px;
  outline: none;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.employee-search-field input:focus {
  border-color: rgba(37, 99, 235, 0.62);
  box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.1);
}

.primary-button,
.ghost-button {
  border: 0;
  border-radius: 999px;
  padding: 10px 16px;
  font-weight: 700;
  cursor: pointer;
}

.primary-button {
  background: #2563eb;
  color: #ffffff;
}

.primary-button:disabled {
  cursor: not-allowed;
  opacity: 0.48;
}

.ghost-button {
  background: #eff6ff;
  color: #1d4ed8;
}

.ghost-button.danger {
  background: #fef2f2;
  color: #b91c1c;
}

.action-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.empty-row {
  color: #64748b;
  text-align: center;
}

.org-employee-table {
  min-width: 0;
  table-layout: fixed;
}

:deep(.org-tree-branch) {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

:deep(.org-tree-node) {
  display: grid;
  grid-template-columns: 18px 24px minmax(0, 1fr);
  align-items: center;
  gap: 6px;
  width: 100%;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 7px 8px 7px calc(6px + var(--tree-level, 0) * 22px);
  background: transparent;
  color: #4b5565;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.14s ease, color 0.14s ease, box-shadow 0.14s ease;
}

:deep(.org-tree-node:hover),
:deep(.org-tree-node.active) {
  border-color: transparent;
  background: #eaf4ff;
  color: #1f6fbd;
  box-shadow: inset 3px 0 0 #409eff;
}

:deep(.tree-toggle) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  padding: 0;
  background: transparent;
  color: #9098a8;
  font-size: 18px;
  font-weight: 800;
  line-height: 1;
}

:deep(.tree-toggle.hidden) {
  visibility: hidden;
}

:deep(.tree-node-icon) {
  display: inline-grid;
  place-items: center;
  width: 23px;
  height: 23px;
  border-radius: 5px;
  background: #f1f5f9;
  color: #607086;
  font-size: 12px;
  font-weight: 850;
}

:deep(.org-tree-node.active .tree-node-icon),
:deep(.org-tree-node:hover .tree-node-icon) {
  background: #409eff;
  color: #ffffff;
}

:deep(.tree-node-main) {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}

:deep(.tree-node-main strong),
:deep(.tree-node-main small) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

:deep(.tree-node-main strong) {
  font-size: 14px;
  font-weight: 700;
}

:deep(.tree-node-main small) {
  color: #8b97a8;
  font-size: 11px;
}

:deep(.org-tree-children) {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

@media (max-width: 980px) {
  .org-permission-tree-card {
    grid-template-columns: minmax(300px, 0.32fr) minmax(680px, 1fr);
  }

  .org-tree-panel {
    min-height: 320px;
  }

  .panel-head,
  .employee-head {
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
