<script setup>
import { buildNavigationEditorTree } from '~/lib/navigation-editor-tree.js'

definePageMeta({
  layout: 'admin'
})

const saving = ref(false)
const activeTab = ref('primary')
const { toast, showToast, clearToast } = useAdminToast()

const { data: navigationItems } = await useFetch('/admin/api/navigation', {
  default: () => []
})

const items = ref(navigationItems.value.map((item) => ({
  ...item,
  parentId: item.parentId ?? null,
  isFolder: Boolean(item.isFolder)
})))

const navDraggingId = ref('')
const navDragParentKey = ref('')
const navDragOverId = ref('')

const footerDraggingId = ref('')
const footerDragOverId = ref('')

/**
 * 네비게이션 항목을 저장 API와 동일한 형태로 직렬화한다.
 * @param {Array<Object>} list - 항목 목록
 * @returns {string} 비교용 JSON 문자열
 */
const serializeNavigationItems = (list) => JSON.stringify(
  [...list]
    .sort((a, b) => {
      if (a.location !== b.location) {
        return String(a.location).localeCompare(String(b.location))
      }
      return (a.sortOrder || 0) - (b.sortOrder || 0)
    })
    .map((item) => ({
      id: String(item.id || '').trim(),
      label: String(item.label || '').trim(),
      url: String(item.url || '').trim(),
      location: item.location,
      sortOrder: Number(item.sortOrder || 0),
      isVisible: Boolean(item.isVisible),
      isFolder: Boolean(item.isFolder),
      parentId: item.parentId ? String(item.parentId).trim() : null
    }))
)

/** 서버에서 불러온 네비게이션 직렬화 스냅샷 */
const navigationBaseline = ref(serializeNavigationItems(items.value))

/**
 * 현재 편집본이 서버 스냅샷과 다른지 여부
 * @returns {boolean} 변경 여부
 */
const isNavigationDirty = computed(() => serializeNavigationItems(items.value) !== navigationBaseline.value)

const primaryTree = computed(() => buildNavigationEditorTree(items.value, 'primary'))

const footerItemsSorted = computed(() =>
  items.value
    .filter((item) => item.location === 'footer' && !item.parentId)
    .sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
)

/**
 * 하위 항목 id를 모두 모은 뒤 삭제한다.
 * @param {string} rootId - 루트 id
 * @returns {void}
 */
const removeItemCascade = (rootId) => {
  const toRemove = new Set([String(rootId)])
  let growing = true
  while (growing) {
    growing = false
    for (const it of items.value) {
      const pid = it.parentId ? String(it.parentId) : ''
      if (pid && toRemove.has(pid) && !toRemove.has(String(it.id))) {
        toRemove.add(String(it.id))
        growing = true
      }
    }
  }
  items.value = items.value.filter((i) => !toRemove.has(String(i.id)))
}

/**
 * 상단 메뉴 동일 부모 형제 사이에서 순서만 바꾼다.
 * @param {string|null} parentId - 부모 id
 * @param {string} sourceId - 이동할 id
 * @param {string} targetId - 놓인 위치 기준 id
 * @returns {void}
 */
const reorderPrimarySiblings = (parentId, sourceId, targetId) => {
  const pid = parentId || null
  const siblings = items.value
    .filter((i) => i.location === 'primary' && (pid ? i.parentId === pid : !i.parentId))
    .sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
  const ids = siblings.map((s) => String(s.id))
  const si = ids.indexOf(String(sourceId))
  const ti = ids.indexOf(String(targetId))
  if (si < 0 || ti < 0 || si === ti) {
    return
  }
  const nextOrder = [...siblings]
  const [mv] = nextOrder.splice(si, 1)
  nextOrder.splice(ti, 0, mv)
  nextOrder.forEach((row, idx) => {
    row.sortOrder = (idx + 1) * 10
  })
}

/**
 * 하단 메뉴 순서 변경
 * @param {string} sourceId - 이동할 id
 * @param {string} targetId - 기준 id
 * @returns {void}
 */
const reorderFooter = (sourceId, targetId) => {
  const list = footerItemsSorted.value.map((i) => i)
  const ids = list.map((s) => String(s.id))
  const si = ids.indexOf(String(sourceId))
  const ti = ids.indexOf(String(targetId))
  if (si < 0 || ti < 0 || si === ti) {
    return
  }
  const [mv] = list.splice(si, 1)
  list.splice(ti, 0, mv)
  list.forEach((row, idx) => {
    row.sortOrder = (idx + 1) * 10
  })
}

/**
 * 상단 트리 드래그 시작
 * @param {{ parentKey: string, itemId: string }} payload - 부모 키와 항목 id
 * @returns {void}
 */
const onPrimaryDragStart = ({ parentKey, itemId }) => {
  navDragParentKey.value = parentKey
  navDraggingId.value = itemId
}

/**
 * 상단 트리 드래그 오버
 * @param {string} itemId - 항목 id
 * @returns {void}
 */
const onPrimaryDragOver = (itemId) => {
  navDragOverId.value = itemId
}

/**
 * 상단 트리 드래그 종료
 * @returns {void}
 */
const onPrimaryDragEnd = () => {
  navDraggingId.value = ''
  navDragParentKey.value = ''
  navDragOverId.value = ''
}

/**
 * 상단 트리 드롭
 * @param {{ parentKey: string, targetId: string }} payload - 부모 키와 대상 id
 * @returns {void}
 */
const onPrimaryDrop = ({ parentKey, targetId }) => {
  if (navDragParentKey.value !== parentKey) {
    showToast('error', '같은 단계의 메뉴 안에서만 순서를 바꿀 수 있습니다.')
    onPrimaryDragEnd()
    return
  }
  const parentId = parentKey === 'root' ? null : parentKey
  reorderPrimarySiblings(parentId, navDraggingId.value, targetId)
  onPrimaryDragEnd()
}

/**
 * 하단 행 드래그 시작
 * @param {DragEvent} event - 이벤트
 * @param {string} id - 항목 id
 * @returns {void}
 */
const onFooterDragStart = (event, id) => {
  if (!event.dataTransfer) {
    return
  }
  footerDraggingId.value = id
  event.dataTransfer.effectAllowed = 'move'
}

/**
 * 하단 행 드래그 오버
 * @param {DragEvent} event - 이벤트
 * @param {string} id - 항목 id
 * @returns {void}
 */
const onFooterDragOver = (event, id) => {
  event.preventDefault()
  footerDragOverId.value = id
}

/**
 * 하단 행 드롭
 * @param {DragEvent} event - 이벤트
 * @param {string} targetId - 대상 id
 * @returns {void}
 */
const onFooterDrop = (event, targetId) => {
  event.preventDefault()
  if (!footerDraggingId.value) {
    return
  }
  reorderFooter(footerDraggingId.value, targetId)
  footerDraggingId.value = ''
  footerDragOverId.value = ''
}

/**
 * 하단 드래그 종료
 * @returns {void}
 */
const onFooterDragEnd = () => {
  footerDraggingId.value = ''
  footerDragOverId.value = ''
}

/**
 * 상단 루트 항목 추가
 * @returns {void}
 */
const addPrimaryRoot = () => {
  const roots = items.value.filter((i) => i.location === 'primary' && !i.parentId)
  const maxOrder = Math.max(0, ...roots.map((r) => r.sortOrder || 0))
  items.value.push({
    id: crypto.randomUUID(),
    label: '새 메뉴',
    url: '/',
    location: 'primary',
    parentId: null,
    sortOrder: maxOrder + 10,
    isVisible: true,
    isFolder: false
  })
}

/**
 * 상단 특정 항목의 하위 추가
 * @param {string} parentId - 부모 id
 * @returns {void}
 */
const addPrimaryChild = (parentId) => {
  const pid = String(parentId)
  const siblings = items.value.filter((i) => i.location === 'primary' && String(i.parentId || '') === pid)
  const maxOrder = Math.max(0, ...siblings.map((r) => r.sortOrder || 0))
  items.value.push({
    id: crypto.randomUUID(),
    label: '새 하위 메뉴',
    url: '#',
    location: 'primary',
    parentId: pid,
    sortOrder: maxOrder + 10,
    isVisible: true,
    isFolder: false
  })
}

/**
 * 하단 항목 추가
 * @returns {void}
 */
const addFooterItem = () => {
  const list = items.value.filter((i) => i.location === 'footer' && !i.parentId)
  const maxOrder = Math.max(0, ...list.map((r) => r.sortOrder || 0))
  items.value.push({
    id: crypto.randomUUID(),
    label: '',
    url: '/',
    location: 'footer',
    parentId: null,
    sortOrder: maxOrder + 10,
    isVisible: true,
    isFolder: false
  })
}

/**
 * 네비게이션 항목 목록 저장
 * @returns {Promise<void>} 저장 결과
 */
const saveNavigation = async () => {
  if (saving.value || !isNavigationDirty.value) {
    return
  }

  saving.value = true
  clearToast()
  showToast('info', '네비게이션을 저장하는 중입니다.')

  try {
    const savedItems = await $fetch('/admin/api/navigation', {
      method: 'PUT',
      body: {
        items: items.value.map((item) => ({
          id: item.id,
          label: item.label,
          url: item.url,
          location: item.location,
          sortOrder: Number(item.sortOrder || 0),
          isVisible: Boolean(item.isVisible),
          parentId: item.parentId ?? null,
          isFolder: Boolean(item.isFolder)
        }))
      }
    })

    items.value = savedItems.map((item) => ({
      ...item,
      parentId: item.parentId ?? null,
      isFolder: Boolean(item.isFolder)
    }))
    navigationBaseline.value = serializeNavigationItems(items.value)
    showToast('success', '네비게이션이 저장되었습니다.')
  } catch (error) {
    showToast('error', error?.data?.message || '네비게이션을 저장하지 못했습니다.')
  } finally {
    saving.value = false
  }
}
</script>

<template>
  <section class="admin-navigation bg-paper p-6">
    <div class="admin-navigation__header mb-6 flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
      <div>
        <p class="admin-navigation__eyebrow text-xs font-semibold uppercase text-muted">
          Navigation
        </p>
        <h1 class="admin-navigation__title mt-2 text-3xl font-semibold">
          메뉴 관리
        </h1>
        <p class="admin-navigation__lead mt-2 max-w-xl text-sm text-muted">
          상단·하단을 탭으로 나누어 편집합니다. 상단은 드래그(::)로 같은 단계끼리 순서를 바꿀 수 있고, 폴더·하위 메뉴를 둘 수 있습니다. 하단은 한 줄 목록만 지원합니다.
        </p>
      </div>
      <div class="admin-navigation__header-actions flex flex-wrap gap-2">
        <button
          class="admin-navigation__submit rounded bg-[#15171a] px-4 py-2 text-sm font-semibold text-white disabled:opacity-50"
          type="button"
          :disabled="saving || !isNavigationDirty"
          @click="saveNavigation"
        >
          {{ saving ? '저장 중' : '메뉴 저장' }}
        </button>
      </div>
    </div>

    <div class="admin-navigation__tabs mb-6 flex gap-2 border-b border-line">
      <button
        class="admin-navigation__tab -mb-px border-b-2 px-4 py-2 text-sm font-semibold transition-colors"
        :class="activeTab === 'primary' ? 'border-ink text-ink' : 'border-transparent text-muted hover:text-ink'"
        type="button"
        @click="activeTab = 'primary'"
      >
        상단 네비게이션
      </button>
      <button
        class="admin-navigation__tab -mb-px border-b-2 px-4 py-2 text-sm font-semibold transition-colors"
        :class="activeTab === 'footer' ? 'border-ink text-ink' : 'border-transparent text-muted hover:text-ink'"
        type="button"
        @click="activeTab = 'footer'"
      >
        하단 네비게이션
      </button>
    </div>

    <div v-show="activeTab === 'primary'" class="admin-navigation__panel-primary space-y-4">
      <div class="flex flex-wrap gap-2">
        <button
          class="admin-navigation__add rounded border border-line bg-white px-4 py-2 text-sm font-semibold"
          type="button"
          @click="addPrimaryRoot"
        >
          상단 메뉴 추가
        </button>
      </div>

      <div v-if="primaryTree.length === 0" class="admin-navigation__empty rounded border border-dashed border-line bg-white p-8 text-center text-sm text-muted">
        상단 메뉴가 없습니다. 위 버튼으로 항목을 추가하세요.
      </div>

      <AdminNavPrimaryBranch
        v-else
        :wraps="primaryTree"
        parent-key="root"
        :dragging-id="navDraggingId"
        :drag-over-id="navDragOverId"
        @drag-start="onPrimaryDragStart"
        @drag-over="onPrimaryDragOver"
        @drag-end="onPrimaryDragEnd"
        @drop="onPrimaryDrop"
        @add-child="addPrimaryChild"
        @remove="removeItemCascade"
      />
    </div>

    <div v-show="activeTab === 'footer'" class="admin-navigation__panel-footer space-y-4">
      <div class="flex flex-wrap gap-2">
        <button
          class="admin-navigation__add rounded border border-line bg-white px-4 py-2 text-sm font-semibold"
          type="button"
          @click="addFooterItem"
        >
          하단 메뉴 추가
        </button>
      </div>

      <div v-if="footerItemsSorted.length === 0" class="admin-navigation__empty rounded border border-dashed border-line bg-white p-8 text-center text-sm text-muted">
        하단 메뉴가 없습니다.
      </div>

      <ul v-else class="admin-navigation__footer-list space-y-2 rounded border border-line bg-white p-4">
        <li
          v-for="item in footerItemsSorted"
          :key="item.id"
          class="admin-navigation__footer-row flex flex-wrap items-center gap-2 rounded border border-transparent px-2 py-2 transition-colors"
          :class="footerDragOverId === item.id ? 'border-ink/20 bg-[#f5f5f2]' : ''"
        >
          <span
            class="admin-navigation__footer-handle cursor-grab select-none text-muted active:cursor-grabbing"
            draggable="true"
            title="드래그하여 순서 변경"
            @dragstart="onFooterDragStart($event, item.id)"
            @dragover="onFooterDragOver($event, item.id)"
            @drop="onFooterDrop($event, item.id)"
            @dragend="onFooterDragEnd"
          >
            ::
          </span>
          <label class="flex items-center gap-1 text-xs text-muted">
            <input v-model="item.isVisible" class="h-4 w-4" type="checkbox">
            표시
          </label>
          <input
            v-model="item.label"
            class="min-w-[100px] flex-1 rounded border border-line px-2 py-1.5 text-sm"
            type="text"
            placeholder="라벨"
            required
          >
          <input
            v-model="item.url"
            class="min-w-[140px] flex-1 rounded border border-line px-2 py-1.5 text-sm font-mono"
            type="text"
            placeholder="URL"
            required
          >
          <button
            class="rounded border border-red-200 px-2 py-1 text-xs font-semibold text-red-700"
            type="button"
            @click="removeItemCascade(item.id)"
          >
            삭제
          </button>
        </li>
      </ul>
    </div>

    <div
      v-if="toast"
      class="admin-navigation__toast fixed right-5 top-5 z-[100] max-w-sm rounded border px-4 py-3 text-sm font-semibold shadow-lg"
      :class="{
        'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
        'border-red-200 bg-red-50 text-red-800': toast.type === 'error',
        'border-line bg-white text-ink': toast.type === 'info'
      }"
      role="status"
    >
      {{ toast.message }}
    </div>
  </section>
</template>
