<template>
  <section class="system-admin-panel">
    <div class="th-section filter-section">
      <div class="th-section-header">
        <div>
          <h2 class="th-section-title">
            {{ t('systemAdmin.menus.managementTitle') }}
          </h2>
          <p class="th-muted">
            {{ t('systemAdmin.menus.managementHint') }}
          </p>
        </div>
        <div class="section-actions">
          <button
            type="button"
            class="text-button"
            data-testid="refresh-menu-tree"
            :disabled="treeRefreshDisabled"
            :title="treeDirty ? t('systemAdmin.menus.treeDirtyActionBlocked') : undefined"
            @click="refreshTree"
          >
            {{ t('common.refresh') }}
          </button>
          <button
            type="button"
            class="primary-button"
            :disabled="treeDirty"
            :title="treeDirty ? t('systemAdmin.menus.treeDirtyActionBlocked') : undefined"
            @click="startCreateMenu"
          >
            {{ t('systemAdmin.menus.createTitle') }}
          </button>
        </div>
      </div>
      <div class="filter-grid">
        <label>
          <span>{{ t('systemAdmin.common.keyword') }}</span>
          <input
            v-model="filters.keyword"
            type="search"
            :placeholder="t('systemAdmin.menus.keywordPlaceholder')"
          >
        </label>
        <label>
          <span>{{ t('systemAdmin.menus.menuStatus') }}</span>
          <select v-model="filters.menu_status">
            <option value="">{{ t('common.all') }}</option>
            <option value="ACTIVE">{{ t('systemAdmin.status.ACTIVE') }}</option>
            <option value="DISABLED">{{ t('systemAdmin.status.DISABLED') }}</option>
          </select>
        </label>
        <label class="checkbox-field">
          <input
            v-model="filters.visible_only"
            type="checkbox"
          >
          <span>{{ t('systemAdmin.menus.visibleOnly') }}</span>
        </label>
        <button
          type="button"
          class="secondary-button"
          @click="resetFilters"
        >
          {{ t('workbench.reset') }}
        </button>
      </div>
    </div>

    <section
      v-if="treeWarnings.length"
      class="th-section menu-warning-panel"
    >
      <div>
        <h2 class="th-section-title">
          {{ t('systemAdmin.menus.treeWarningTitle') }}
        </h2>
        <p class="th-muted">
          {{ t('systemAdmin.menus.treeWarningHint') }}
        </p>
      </div>
      <ul>
        <li
          v-for="warning in treeWarnings"
          :key="warning"
        >
          {{ warning }}
        </li>
      </ul>
      <p class="form-message form-message--warning">
        {{ t('systemAdmin.menus.treeWarningSaveBlocked') }}
      </p>
    </section>

    <section
      v-if="operationError || permissionLoadFailed"
      class="th-section menu-operation-panel"
    >
      <p
        v-if="operationError"
        class="form-message form-message--error"
      >
        {{ operationError }}
      </p>
      <p
        v-if="permissionLoadFailed"
        class="form-message form-message--warning"
      >
        {{ t('systemAdmin.menus.permissionLoadFailed') }}
      </p>
    </section>

    <div class="system-admin-menu-grid">
      <section class="th-section menu-tree-section">
        <div class="th-section-header">
          <div>
            <h2 class="th-section-title">
              {{ t('systemAdmin.menus.treeTitle') }}
            </h2>
            <p
              v-if="treeDirty"
              class="form-message form-message--warning"
            >
              {{ t('systemAdmin.menus.treeDirty') }}
            </p>
          </div>
          <button
            type="button"
            class="primary-button"
            data-testid="save-tree-order"
            :disabled="treeSaveDisabled"
            :title="treeStructureLocked ? t('systemAdmin.menus.treeWarningSaveBlocked') : undefined"
            @click="saveTreeOrder"
          >
            {{ treeSaving ? t('common.saving') : t('systemAdmin.menus.saveTreeOrder') }}
          </button>
        </div>

        <div
          v-if="loading"
          class="empty-state"
        >
          {{ t('common.loading') }}
        </div>
        <div
          v-else-if="treeLoadError"
          class="empty-state empty-state--error"
        >
          {{ treeLoadError }}
        </div>
        <div
          v-else-if="!visibleMenuRows.length"
          class="empty-state"
        >
          {{ t('common.noData') }}
        </div>
        <div
          v-else
          class="menu-tree"
          data-testid="menu-tree"
          @dragover.prevent
          @drop.prevent="dropToRoot"
        >
          <div
            v-for="row in visibleMenuRows"
            :key="row.node.id"
            class="menu-tree-row"
            :data-testid="`menu-row-${row.node.id}`"
            :class="{
              'menu-tree-row--selected': selectedMenu?.id === row.node.id,
              'menu-tree-row--muted': row.node.menu_status === 'DISABLED' || !row.node.visible,
              'menu-tree-row--warning': !row.node.known_route,
            }"
            :draggable="!treeStructureLocked"
            :style="{ paddingLeft: `${row.depth * 18 + 10}px` }"
            @dragstart="startDrag(row.node.id)"
            @dragend="clearDraggedMenu"
            @dragover.prevent
            @drop.stop.prevent="dropOnNode(row.node.id)"
          >
            <button
              type="button"
              class="menu-tree-main"
              :data-testid="`select-menu-${row.node.id}`"
              @click="selectMenu(row.node)"
            >
              <span class="menu-tree-branch" />
              <span class="menu-tree-name">{{ row.node.menu_name }}</span>
              <span class="th-code">{{ row.node.menu_code }}</span>
            </button>
            <span
              class="status-pill"
              :class="{ 'status-pill--active': row.node.menu_status === 'ACTIVE' }"
            >
              {{ statusLabel(row.node.menu_status) }}
            </span>
            <span class="menu-tree-meta">
              {{ row.node.visible ? t('systemAdmin.common.yes') : t('systemAdmin.common.no') }}
            </span>
            <span
              v-if="!row.node.known_route"
              class="code-chip code-chip--warning"
            >
              {{ t('systemAdmin.menus.unknownRoute') }}
            </span>
            <div class="menu-tree-actions">
              <button
                type="button"
                class="icon-button"
                :data-testid="`move-up-${row.node.id}`"
                :disabled="treeStructureLocked || !canMove(row.node.id, -1)"
                :aria-label="t('systemAdmin.menus.moveUp')"
                @click.stop="moveMenu(row.node.id, -1)"
              >
                <i
                  class="pi pi-arrow-up"
                  aria-hidden="true"
                />
              </button>
              <button
                type="button"
                class="icon-button"
                :data-testid="`move-down-${row.node.id}`"
                :disabled="treeStructureLocked || !canMove(row.node.id, 1)"
                :aria-label="t('systemAdmin.menus.moveDown')"
                @click.stop="moveMenu(row.node.id, 1)"
              >
                <i
                  class="pi pi-arrow-down"
                  aria-hidden="true"
                />
              </button>
            </div>
          </div>
        </div>
      </section>

      <aside class="th-section menu-detail-panel">
        <div class="th-section-header">
          <div>
            <h2 class="th-section-title">
              {{ showCreateForm ? t('systemAdmin.menus.createTitle') : t('systemAdmin.menus.editTitle') }}
            </h2>
            <p
              v-if="selectedMenu && !showCreateForm"
              class="th-muted"
            >
              {{ selectedMenu.menu_code }}
            </p>
          </div>
        </div>

        <form
          v-if="showCreateForm"
          class="stacked-form menu-config-form"
          @submit.prevent="createMenu"
        >
          <label>
            <span>{{ t('systemAdmin.menus.menuCode') }}</span>
            <input
              v-model="createForm.menu_code"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.menuName') }}</span>
            <input
              v-model="createForm.menu_name"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.parentMenu') }}</span>
            <select v-model="createForm.parent_id">
              <option value="">{{ t('systemAdmin.menus.rootMenu') }}</option>
              <option
                v-for="option in menuParentOptions"
                :key="option.id"
                :value="option.id"
              >
                {{ option.label }}
              </option>
            </select>
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.columns.route') }}</span>
            <input
              v-model="createForm.route_path"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.componentKey') }}</span>
            <input
              v-model="createForm.component_key"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.iconKey') }}</span>
            <input
              v-model="createForm.icon_key"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.columns.permission') }}</span>
            <select
              v-model="createForm.permission_code"
              :disabled="permissionLoadFailed"
            >
              <option value="">{{ t('common.all') }}</option>
              <option
                v-for="permission in permissions"
                :key="permission.id"
                :value="permission.permission_code"
              >
                {{ permission.permission_name }} ({{ permission.permission_code }})
              </option>
            </select>
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.columns.visible') }}</span>
            <select v-model="createForm.visible">
              <option :value="true">{{ t('systemAdmin.common.yes') }}</option>
              <option :value="false">{{ t('systemAdmin.common.no') }}</option>
            </select>
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.menuStatus') }}</span>
            <select v-model="createForm.menu_status">
              <option value="ACTIVE">{{ t('systemAdmin.status.ACTIVE') }}</option>
              <option value="DISABLED">{{ t('systemAdmin.status.DISABLED') }}</option>
            </select>
          </label>
          <div class="form-actions">
            <button
              type="button"
              class="secondary-button"
              @click="cancelPanel"
            >
              {{ t('common.cancel') }}
            </button>
            <button
              type="submit"
              class="primary-button"
              data-testid="create-menu-config"
              :disabled="configSubmitDisabled"
            >
              {{ saving ? t('common.saving') : t('systemAdmin.common.create') }}
            </button>
          </div>
        </form>

        <form
          v-else-if="selectedMenu"
          class="stacked-form menu-config-form"
          @submit.prevent="updateSelectedMenu"
        >
          <label>
            <span>{{ t('systemAdmin.menus.menuName') }}</span>
            <input
              v-model="editForm.menu_name"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.parentMenu') }}</span>
            <select v-model="editForm.parent_id">
              <option value="">{{ t('systemAdmin.menus.rootMenu') }}</option>
              <option
                v-for="option in editParentOptions"
                :key="option.id"
                :value="option.id"
              >
                {{ option.label }}
              </option>
            </select>
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.columns.route') }}</span>
            <input
              v-model="editForm.route_path"
              type="text"
            >
          </label>
          <p
            v-if="selectedMenu && !selectedMenu.known_route"
            class="form-message form-message--warning"
          >
            {{ t('systemAdmin.menus.unknownRouteHint') }}
          </p>
          <label>
            <span>{{ t('systemAdmin.menus.componentKey') }}</span>
            <input
              v-model="editForm.component_key"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.iconKey') }}</span>
            <input
              v-model="editForm.icon_key"
              type="text"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.columns.permission') }}</span>
            <select
              v-model="editForm.permission_code"
              :disabled="permissionLoadFailed"
            >
              <option value="">{{ t('common.all') }}</option>
              <option
                v-for="permission in permissions"
                :key="permission.id"
                :value="permission.permission_code"
              >
                {{ permission.permission_name }} ({{ permission.permission_code }})
              </option>
            </select>
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.columns.sort') }}</span>
            <input
              v-model.number="editForm.sort_order"
              type="number"
            >
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.columns.visible') }}</span>
            <select v-model="editForm.visible">
              <option :value="true">{{ t('systemAdmin.common.yes') }}</option>
              <option :value="false">{{ t('systemAdmin.common.no') }}</option>
            </select>
          </label>
          <label>
            <span>{{ t('systemAdmin.menus.menuStatus') }}</span>
            <select v-model="editForm.menu_status">
              <option value="ACTIVE">{{ t('systemAdmin.status.ACTIVE') }}</option>
              <option value="DISABLED">{{ t('systemAdmin.status.DISABLED') }}</option>
            </select>
          </label>
          <div class="form-actions">
            <button
              type="button"
              class="secondary-button"
              @click="cancelPanel"
            >
              {{ t('common.cancel') }}
            </button>
            <button
              type="submit"
              class="primary-button"
              data-testid="update-menu-config"
              :disabled="configSubmitDisabled"
            >
              {{ saving ? t('common.saving') : t('systemAdmin.common.save') }}
            </button>
          </div>
        </form>

        <div
          v-else
          class="empty-state empty-state--compact"
        >
          {{ t('systemAdmin.common.selectRow') }}
        </div>
      </aside>
    </div>
  </section>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'

import {
  createAdminMenu,
  fetchAdminMenuTree,
  fetchAdminPermissions,
  updateAdminMenu,
  updateAdminMenuTreeOrder,
} from '@/services/systemAdminService'
import type {
  AdminMenuCreateRequest,
  AdminMenuTreeNodeResult,
  AdminMenuTreeOrderItemRequest,
  AdminMenuUpdateRequest,
  AdminPermissionResult,
} from '@/types/systemAdmin'

interface MenuRow {
  node: AdminMenuTreeNodeResult
  depth: number
}

interface MenuParentOption {
  id: string
  label: string
}

const { t } = useI18n()
const loading = ref(false)
const saving = ref(false)
const treeSaving = ref(false)
const treeLoadError = ref('')
const operationError = ref('')
const permissionLoadFailed = ref(false)
const treeDirty = ref(false)
const menuTree = ref<AdminMenuTreeNodeResult[]>([])
const treeWarnings = ref<string[]>([])
const selectedMenu = ref<AdminMenuTreeNodeResult | null>(null)
const showCreateForm = ref(false)
const permissions = ref<AdminPermissionResult[]>([])
const draggedMenuId = ref<string | null>(null)
const filters = reactive({
  keyword: '',
  menu_status: '',
  visible_only: false,
})
const createForm = reactive<AdminMenuCreateRequest>({
  parent_id: '',
  menu_code: '',
  menu_name: '',
  menu_type: 'PAGE',
  route_path: '',
  component_key: '',
  icon_key: 'pi pi-circle',
  permission_code: '',
  sort_order: 100,
  visible: false,
  menu_status: 'DISABLED',
})
const editForm = reactive<AdminMenuUpdateRequest>({
  parent_id: '',
  menu_name: '',
  menu_type: 'PAGE',
  route_path: '',
  component_key: '',
  icon_key: '',
  permission_code: '',
  sort_order: 0,
  visible: false,
  menu_status: 'DISABLED',
})
let requestSequence = 0

const allMenuRows = computed(() => flattenMenuRows(menuTree.value))
const visibleMenuRows = computed(() => allMenuRows.value.filter((row) => matchesFilters(row.node)))
const menuParentOptions = computed(() => allMenuRows.value.map((row) => toParentOption(row)))
const treeStructureLocked = computed(() => treeWarnings.value.length > 0)
const treeSaveDisabled = computed(() => !treeDirty.value || saving.value || treeSaving.value || treeStructureLocked.value)
const treeRefreshDisabled = computed(() => loading.value || treeSaving.value || treeDirty.value)
const configSubmitDisabled = computed(() => saving.value || treeDirty.value || permissionLoadFailed.value)
const editParentOptions = computed(() => {
  if (!selectedMenu.value) {
    return menuParentOptions.value
  }
  const blockedIds = collectSubtreeIds(selectedMenu.value)
  return allMenuRows.value.filter((row) => !blockedIds.has(row.node.id)).map((row) => toParentOption(row))
})

onMounted(() => {
  void loadMenuTree()
  void loadPermissions()
})

async function loadMenuTree(selectMenuId?: string): Promise<void> {
  const currentRequest = ++requestSequence
  loading.value = true
  treeLoadError.value = ''
  try {
    const result = await fetchAdminMenuTree()
    if (currentRequest !== requestSequence) {
      return
    }
    menuTree.value = result.items
    treeWarnings.value = result.warnings
    treeDirty.value = false
    const nextSelection = selectMenuId ? findMenuById(result.items, selectMenuId) : findFirstMenu(result.items)
    if (nextSelection) {
      selectMenu(nextSelection)
    } else {
      selectedMenu.value = null
    }
  } catch (error) {
    if (currentRequest !== requestSequence) {
      return
    }
    menuTree.value = []
    treeWarnings.value = []
    selectedMenu.value = null
    treeLoadError.value = error instanceof Error ? error.message : String(error)
  } finally {
    if (currentRequest === requestSequence) {
      loading.value = false
    }
  }
}

async function loadPermissions(): Promise<void> {
  permissionLoadFailed.value = false
  try {
    permissions.value = await fetchAdminPermissions()
  } catch {
    permissions.value = []
    permissionLoadFailed.value = true
  }
}

function refreshTree(): void {
  if (!canReplaceTree()) {
    return
  }
  void loadMenuTree(selectedMenu.value?.id)
}

async function createMenu(): Promise<void> {
  if (!canSubmitConfig()) {
    return
  }
  saving.value = true
  operationError.value = ''
  try {
    const result = await createAdminMenu(normalizeMenuRequest(createForm))
    resetCreateForm()
    showCreateForm.value = false
    await loadMenuTree(result.id)
  } catch (error) {
    setOperationError(error)
  } finally {
    saving.value = false
  }
}

async function updateSelectedMenu(): Promise<void> {
  if (!selectedMenu.value) {
    return
  }
  if (!canSubmitConfig()) {
    return
  }
  saving.value = true
  operationError.value = ''
  try {
    const result = await updateAdminMenu(selectedMenu.value.id, normalizeMenuRequest(editForm))
    await loadMenuTree(result.id)
  } catch (error) {
    setOperationError(error)
  } finally {
    saving.value = false
  }
}

async function saveTreeOrder(): Promise<void> {
  if (treeSaveDisabled.value) {
    return
  }
  treeSaving.value = true
  operationError.value = ''
  try {
    const result = await updateAdminMenuTreeOrder({
      items: flattenTreeOrder(menuTree.value),
    })
    menuTree.value = result.items
    treeWarnings.value = result.warnings
    treeDirty.value = false
    if (selectedMenu.value) {
      const updatedSelection = findMenuById(result.items, selectedMenu.value.id)
      if (updatedSelection) {
        selectMenu(updatedSelection)
      }
    }
  } catch (error) {
    setOperationError(error)
  } finally {
    treeSaving.value = false
  }
}

function startCreateMenu(): void {
  if (treeDirty.value) {
    operationError.value = t('systemAdmin.menus.treeDirtyActionBlocked')
    return
  }
  resetCreateForm()
  showCreateForm.value = true
  selectedMenu.value = null
}

function cancelPanel(): void {
  showCreateForm.value = false
  if (selectedMenu.value) {
    populateEditForm(selectedMenu.value)
  }
}

function selectMenu(menu: AdminMenuTreeNodeResult): void {
  selectedMenu.value = menu
  showCreateForm.value = false
  populateEditForm(menu)
}

function resetFilters(): void {
  filters.keyword = ''
  filters.menu_status = ''
  filters.visible_only = false
}

function resetCreateForm(): void {
  createForm.parent_id = ''
  createForm.menu_code = ''
  createForm.menu_name = ''
  createForm.menu_type = 'PAGE'
  createForm.route_path = ''
  createForm.component_key = ''
  createForm.icon_key = 'pi pi-circle'
  createForm.permission_code = ''
  createForm.sort_order = 100
  createForm.visible = false
  createForm.menu_status = 'DISABLED'
}

function populateEditForm(menu: AdminMenuTreeNodeResult): void {
  editForm.parent_id = menu.parent_id || ''
  editForm.menu_name = menu.menu_name
  editForm.menu_type = menu.menu_type
  editForm.route_path = menu.route_path || ''
  editForm.component_key = menu.component_key || ''
  editForm.icon_key = menu.icon_key || ''
  editForm.permission_code = menu.permission_code || ''
  editForm.sort_order = menu.sort_order ?? 0
  editForm.visible = menu.visible
  editForm.menu_status = menu.menu_status
}

function moveMenu(menuId: string, direction: -1 | 1): void {
  if (treeStructureLocked.value) {
    return
  }
  const siblings = findSiblingList(menuTree.value, menuId)
  if (!siblings) {
    return
  }
  const index = siblings.findIndex((menu) => menu.id === menuId)
  const nextIndex = index + direction
  if (index < 0 || nextIndex < 0 || nextIndex >= siblings.length) {
    return
  }
  const [moved] = siblings.splice(index, 1)
  if (moved) {
    siblings.splice(nextIndex, 0, moved)
    treeDirty.value = true
  }
}

function canMove(menuId: string, direction: -1 | 1): boolean {
  const siblings = findSiblingList(menuTree.value, menuId)
  if (!siblings) {
    return false
  }
  const index = siblings.findIndex((menu) => menu.id === menuId)
  const nextIndex = index + direction
  return index >= 0 && nextIndex >= 0 && nextIndex < siblings.length
}

function startDrag(menuId: string): void {
  if (treeStructureLocked.value) {
    return
  }
  draggedMenuId.value = menuId
}

function dropOnNode(targetMenuId: string): void {
  if (treeStructureLocked.value) {
    clearDraggedMenu()
    return
  }
  const sourceMenuId = draggedMenuId.value
  clearDraggedMenu()
  if (!sourceMenuId || sourceMenuId === targetMenuId || isDescendant(sourceMenuId, targetMenuId)) {
    return
  }
  const moved = detachMenu(menuTree.value, sourceMenuId)
  const target = findMenuById(menuTree.value, targetMenuId)
  if (!moved || !target) {
    return
  }
  moved.parent_id = target.id
  target.children.push(moved)
  refreshSelectedMenuForm(moved)
  treeDirty.value = true
}

function dropToRoot(): void {
  if (treeStructureLocked.value) {
    clearDraggedMenu()
    return
  }
  const sourceMenuId = draggedMenuId.value
  clearDraggedMenu()
  if (!sourceMenuId) {
    return
  }
  const moved = detachMenu(menuTree.value, sourceMenuId)
  if (!moved) {
    return
  }
  moved.parent_id = null
  menuTree.value.push(moved)
  refreshSelectedMenuForm(moved)
  treeDirty.value = true
}

function matchesFilters(menu: AdminMenuTreeNodeResult): boolean {
  const keyword = filters.keyword.trim().toLowerCase()
  const matchesKeyword = !keyword || [
    menu.menu_code,
    menu.menu_name,
    menu.route_path,
    menu.permission_code,
  ].some((value) => (value || '').toLowerCase().includes(keyword))
  const matchesStatus = !filters.menu_status || menu.menu_status === filters.menu_status
  const matchesVisible = !filters.visible_only || menu.visible
  return matchesKeyword && matchesStatus && matchesVisible
}

function flattenMenuRows(nodes: AdminMenuTreeNodeResult[], depth = 0): MenuRow[] {
  return nodes.flatMap((node) => [
    {
      node,
      depth,
    },
    ...flattenMenuRows(node.children, depth + 1),
  ])
}

function flattenTreeOrder(nodes: AdminMenuTreeNodeResult[], parentId: string | null = null): AdminMenuTreeOrderItemRequest[] {
  return nodes.flatMap((node, index) => [
    {
      menu_id: node.id,
      parent_id: parentId,
      sort_order: (index + 1) * 100,
    },
    ...flattenTreeOrder(node.children, node.id),
  ])
}

function toParentOption(row: MenuRow): MenuParentOption {
  return {
    id: row.node.id,
    label: `${'— '.repeat(row.depth)}${row.node.menu_name}`,
  }
}

function findFirstMenu(nodes: AdminMenuTreeNodeResult[]): AdminMenuTreeNodeResult | null {
  return nodes[0] ?? null
}

function findMenuById(nodes: AdminMenuTreeNodeResult[], menuId: string): AdminMenuTreeNodeResult | null {
  for (const node of nodes) {
    if (node.id === menuId) {
      return node
    }
    const child = findMenuById(node.children, menuId)
    if (child) {
      return child
    }
  }
  return null
}

function findSiblingList(nodes: AdminMenuTreeNodeResult[], menuId: string): AdminMenuTreeNodeResult[] | null {
  if (nodes.some((node) => node.id === menuId)) {
    return nodes
  }
  for (const node of nodes) {
    const siblings = findSiblingList(node.children, menuId)
    if (siblings) {
      return siblings
    }
  }
  return null
}

function detachMenu(nodes: AdminMenuTreeNodeResult[], menuId: string): AdminMenuTreeNodeResult | null {
  const index = nodes.findIndex((node) => node.id === menuId)
  if (index >= 0) {
    return nodes.splice(index, 1)[0] ?? null
  }
  for (const node of nodes) {
    const removed = detachMenu(node.children, menuId)
    if (removed) {
      return removed
    }
  }
  return null
}

function isDescendant(menuId: string, possibleDescendantId: string): boolean {
  const menu = findMenuById(menuTree.value, menuId)
  if (!menu) {
    return false
  }
  return collectSubtreeIds(menu).has(possibleDescendantId)
}

function collectSubtreeIds(menu: AdminMenuTreeNodeResult): Set<string> {
  const ids = new Set<string>([menu.id])
  menu.children.forEach((child) => {
    collectSubtreeIds(child).forEach((id) => ids.add(id))
  })
  return ids
}

function refreshSelectedMenuForm(menu: AdminMenuTreeNodeResult): void {
  if (selectedMenu.value?.id === menu.id) {
    selectedMenu.value = menu
    populateEditForm(menu)
  }
}

function clearDraggedMenu(): void {
  draggedMenuId.value = null
}

function canReplaceTree(): boolean {
  if (treeDirty.value) {
    operationError.value = t('systemAdmin.menus.treeDirtyActionBlocked')
    return false
  }
  return true
}

function canSubmitConfig(): boolean {
  if (treeDirty.value) {
    operationError.value = t('systemAdmin.menus.treeDirtyActionBlocked')
    return false
  }
  if (permissionLoadFailed.value) {
    operationError.value = t('systemAdmin.menus.permissionLoadFailed')
    return false
  }
  return true
}

function normalizeMenuRequest<T extends AdminMenuCreateRequest | AdminMenuUpdateRequest>(request: T): T {
  return {
    ...request,
    parent_id: request.parent_id || undefined,
    route_path: request.route_path || undefined,
    component_key: request.component_key || undefined,
    icon_key: request.icon_key || undefined,
    permission_code: request.permission_code || undefined,
  }
}

function statusLabel(status: string): string {
  return t(`systemAdmin.status.${status}`)
}

function setOperationError(error: unknown): void {
  operationError.value = error instanceof Error ? error.message : String(error)
}
</script>

<style scoped>
@import './system-admin.css';
</style>
