<script setup lang="ts">
import type { CategoryNode } from '~/components/index/CategoryTreeNode.vue'

definePageMeta({
  layout: 'home',
})

const { $toast } = useNuxtApp()
const isMobile = useMediaQuery('(max-width: 767.98px)')
const drawerOpen = ref(false)
const sidebarMode = computed<'rail' | 'drawer'>(() => isMobile.value ? 'drawer' : 'rail')

interface CardItem {
  id: number
  type: 'text' | 'image' | 'image-text' | 'portfolio' | 'project'
  image?: string
  images?: string[]
  title: string
  description?: string
  tags?: string[]
  aspectRatio: number
}

// ── Sidebar state ──

const activeToolKey = ref<string>('home')
const activeCategoryId = ref<string>('all')

const categories = ref<CategoryNode[]>([
  {
    id: 'all',
    name: '全部灵感',
    image: 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=80&h=80&fit=crop',
    count: 156,
  },
  {
    id: 'design',
    name: '设计',
    image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=80&h=80&fit=crop',
    count: 42,
    children: [
      {
        id: 'design-graphic',
        name: '平面 / 海报',
        image: 'https://images.unsplash.com/photo-1611532736597-de2d4265fba3?w=80&h=80&fit=crop',
        count: 18,
      },
      {
        id: 'design-product',
        name: '产品 / UI',
        image: 'https://images.unsplash.com/photo-1545235617-9465d2a55698?w=80&h=80&fit=crop',
        count: 16,
      },
      {
        id: 'design-typo',
        name: '字体',
        image: 'https://images.unsplash.com/photo-1561070791-2526d30994b8?w=80&h=80&fit=crop',
        count: 8,
      },
    ],
  },
  {
    id: 'photo',
    name: '摄影',
    image: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=80&h=80&fit=crop',
    count: 38,
    children: [
      {
        id: 'photo-street',
        name: '街头',
        image: 'https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=80&h=80&fit=crop',
        count: 14,
      },
      {
        id: 'photo-portrait',
        name: '人像',
        image: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=80&h=80&fit=crop',
        count: 12,
      },
      {
        id: 'photo-landscape',
        name: '风光',
        image: 'https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=80&h=80&fit=crop',
        count: 12,
      },
    ],
  },
  {
    id: 'words',
    name: '文字摘录',
    image: 'https://images.unsplash.com/photo-1455390582262-044cdead277a?w=80&h=80&fit=crop',
    count: 24,
  },
  {
    id: 'projects',
    name: '项目档案',
    image: 'https://images.unsplash.com/photo-1467232004584-a241de8bcf5d?w=80&h=80&fit=crop',
    count: 17,
    children: [
      {
        id: 'projects-web',
        name: 'Web',
        image: 'https://images.unsplash.com/photo-1517180102446-f3ece451e9d8?w=80&h=80&fit=crop',
        count: 9,
      },
      {
        id: 'projects-mobile',
        name: 'Mobile',
        image: 'https://images.unsplash.com/photo-1512941937669-90a1b58e7e9c?w=80&h=80&fit=crop',
        count: 8,
      },
    ],
  },
  {
    id: 'inspiration',
    name: '随手收藏',
    image: 'https://images.unsplash.com/photo-1490730141103-6cac27aaab94?w=80&h=80&fit=crop',
    count: 35,
  },
])

const activeCategoryName = computed(() => {
  function find(nodes: CategoryNode[]): CategoryNode | null {
    for (const n of nodes) {
      if (n.id === activeCategoryId.value) return n
      if (n.children) {
        const r = find(n.children)
        if (r) return r
      }
    }
    return null
  }
  return find(categories.value)?.name ?? '全部灵感'
})

function onSelectTool(key: string) {
  activeToolKey.value = key
  const labelMap: Record<string, string> = {
    home: '主页',
    search: '搜索',
    tags: '标签',
    highlights: '高亮',
    archive: '归档',
  }
  $toast.info(`切换到 · ${labelMap[key] ?? key}`)
}

function onSelectCategory(id: string) {
  activeCategoryId.value = id
  if (drawerOpen.value) drawerOpen.value = false
}

function onAddCategory(parentId: string | null) {
  const newName = window.prompt(parentId ? '新建子分类名称' : '新建分类名称')
  if (!newName?.trim()) return
  const node: CategoryNode = {
    id: `cat-${Date.now()}`,
    name: newName.trim(),
    count: 0,
  }
  if (!parentId) {
    categories.value.push(node)
    $toast.success(`已创建分类「${node.name}」`)
    return
  }
  function attach(nodes: CategoryNode[]): boolean {
    for (const n of nodes) {
      if (n.id === parentId) {
        n.children = n.children ?? []
        n.children.push(node)
        return true
      }
      if (n.children && attach(n.children)) return true
    }
    return false
  }
  if (attach(categories.value)) {
    $toast.success(`已添加子分类「${node.name}」`)
  }
}

function findNode(nodes: CategoryNode[], id: string): CategoryNode | null {
  for (const n of nodes) {
    if (n.id === id) return n
    if (n.children) {
      const r = findNode(n.children, id)
      if (r) return r
    }
  }
  return null
}

function onRenameCategory(id: string) {
  const node = findNode(categories.value, id)
  if (!node) return
  const next = window.prompt('重命名分类', node.name)
  if (!next?.trim() || next.trim() === node.name) return
  node.name = next.trim()
  $toast.success('已重命名')
}

function onDeleteCategory(id: string) {
  const node = findNode(categories.value, id)
  if (!node) return
  if (!window.confirm(`确定删除「${node.name}」吗？`)) return
  function remove(nodes: CategoryNode[]): CategoryNode[] {
    return nodes
      .filter(n => n.id !== id)
      .map(n => ({ ...n, children: n.children ? remove(n.children) : undefined }))
  }
  categories.value = remove(categories.value)
  if (activeCategoryId.value === id) activeCategoryId.value = 'all'
  $toast.success('已删除分类')
}

function onMoveCategory(_id: string) {
  $toast.info('移动功能即将上线')
}

function onChangeCover(_id: string) {
  $toast.info('封面更换即将上线')
}

// ── Right panel waterfall ──

const sentinel = ref<HTMLElement | null>(null)
const mainRef = ref<HTMLElement | null>(null)
const allItems = ref<CardItem[]>([])
const page = ref(1)
const hasMore = ref(true)
const loading = ref(false)
const initLoading = ref(true)

const columnCount = ref(1)
const columns = ref<CardItem[][]>([[]])
const columnHeights = ref<number[]>([0])

function containerWidth(): number {
  return mainRef.value?.clientWidth ?? 800
}

function getColumnWidth(): number {
  const padding = 48
  const w = containerWidth() - padding
  const gap = 18
  return (w - gap * (columnCount.value - 1)) / columnCount.value
}

function estimateCardHeight(item: CardItem): number {
  const colWidth = getColumnWidth()
  if (item.type === 'text') {
    const textWidth = colWidth - 48
    const charsPerLine = Math.max(1, Math.floor(textWidth / 13.5))
    const charCount = (item.description || '').length
    const lines = Math.ceil(charCount / charsPerLine)
    return 109 + lines * 24
  }
  return colWidth / item.aspectRatio
}

function addToShortestColumn(item: CardItem) {
  let minIdx = 0
  for (let i = 1; i < columnCount.value; i++) {
    if (columnHeights.value[i]! < columnHeights.value[minIdx]!) minIdx = i
  }
  columns.value[minIdx]!.push(item)
  columnHeights.value[minIdx]! += estimateCardHeight(item)
}

function distributeAll() {
  const n = columnCount.value
  columns.value = Array.from({ length: n }, () => [])
  columnHeights.value = new Array(n).fill(0)
  for (const item of allItems.value) addToShortestColumn(item)
}

function decideColumnCount(): number {
  if (isMobile.value) return 1
  const w = containerWidth()
  if (w < 900) return 2
  if (w < 1200) return 3
  return 4
}

let resizeTimer: ReturnType<typeof setTimeout> | null = null
function onContainerResize() {
  if (resizeTimer) clearTimeout(resizeTimer)
  resizeTimer = setTimeout(() => {
    const n = decideColumnCount()
    if (n !== columnCount.value) {
      columnCount.value = n
      distributeAll()
    }
  }, 120)
}

async function loadMore() {
  if (loading.value || !hasMore.value) return
  loading.value = true
  try {
    const data = await $fetch<{ items: CardItem[], hasMore: boolean }>('/api/cards', {
      query: { page: page.value, pageSize: 12 },
    })
    for (const item of data.items) {
      allItems.value.push(item)
      addToShortestColumn(item)
    }
    hasMore.value = data.hasMore
    page.value++
  } catch {
    $toast.error('加载失败，请稍后重试')
  } finally {
    loading.value = false
    initLoading.value = false
  }
}

// Reset when category switches (mock: just reshuffle existing items)
watch(activeCategoryId, () => {
  page.value = 1
  hasMore.value = true
  allItems.value = []
  columns.value = Array.from({ length: columnCount.value }, () => [])
  columnHeights.value = new Array(columnCount.value).fill(0)
  initLoading.value = true
  loadMore()
})

watch(isMobile, () => {
  const n = decideColumnCount()
  if (n !== columnCount.value) {
    columnCount.value = n
    distributeAll()
  }
})

let observer: IntersectionObserver | null = null
let resizeObserver: ResizeObserver | null = null

watch(sentinel, (el) => {
  observer?.disconnect()
  if (!el) return
  observer = new IntersectionObserver(
    ([entry]) => {
      if (entry!.isIntersecting) loadMore()
    },
    { rootMargin: '320px' },
  )
  observer.observe(el)
})

onMounted(() => {
  if (mainRef.value) {
    columnCount.value = decideColumnCount()
    distributeAll()
    resizeObserver = new ResizeObserver(onContainerResize)
    resizeObserver.observe(mainRef.value)
  }
  loadMore()
})

onUnmounted(() => {
  observer?.disconnect()
  resizeObserver?.disconnect()
  if (resizeTimer) clearTimeout(resizeTimer)
})
</script>

<template>
  <div class="home-layout">
    <IndexLeftSidebar
      v-if="sidebarMode === 'rail'"
      class="home-sidebar"
      :mode="sidebarMode"
      :categories="categories"
      :active-tool-key="activeToolKey"
      :active-category-id="activeCategoryId"
      @select-tool="onSelectTool"
      @select-category="onSelectCategory"
      @add-category="onAddCategory"
      @rename-category="onRenameCategory"
      @delete-category="onDeleteCategory"
      @move-category="onMoveCategory"
      @change-cover="onChangeCover"
    />

    <IndexLeftSidebar
      v-else
      :mode="sidebarMode"
      :drawer-open="drawerOpen"
      :categories="categories"
      :active-tool-key="activeToolKey"
      :active-category-id="activeCategoryId"
      @update:drawer-open="(v: boolean) => drawerOpen = v"
      @select-tool="onSelectTool"
      @select-category="onSelectCategory"
      @add-category="onAddCategory"
      @rename-category="onRenameCategory"
      @delete-category="onDeleteCategory"
      @move-category="onMoveCategory"
      @change-cover="onChangeCover"
    />

    <main ref="mainRef" class="home-main">
      <header class="main-header">
        <!-- Desktop / tablet layout (>= 768px) -->
        <template v-if="!isMobile">
          <div class="header-left">
            <span class="header-eyebrow">分类</span>
            <h1 class="header-title">{{ activeCategoryName }}</h1>
          </div>
          <div class="header-actions">
            <button type="button" class="action-btn" @click="$toast.info('视图切换即将上线')">
              <Icon name="lucide:layout-grid" />
            </button>
            <button type="button" class="action-btn" @click="$toast.info('筛选即将上线')">
              <Icon name="lucide:sliders-horizontal" />
            </button>
            <button type="button" class="action-btn primary" @click="$toast.info('上传即将上线')">
              <Icon name="lucide:plus" />
              <span>新增</span>
            </button>
          </div>
        </template>

        <!-- Mobile layout (< 768px) -->
        <template v-else>
          <div class="header-row header-row--top">
            <span class="header-eyebrow">分类</span>
            <div class="header-actions">
              <button type="button" class="action-btn action-btn--icon" aria-label="视图" @click="$toast.info('视图切换即将上线')">
                <Icon name="lucide:layout-grid" />
              </button>
              <button type="button" class="action-btn action-btn--icon" aria-label="筛选" @click="$toast.info('筛选即将上线')">
                <Icon name="lucide:sliders-horizontal" />
              </button>
              <button type="button" class="action-btn action-btn--icon action-btn--primary" aria-label="新增" @click="$toast.info('上传即将上线')">
                <Icon name="lucide:plus" />
              </button>
            </div>
          </div>

          <h1 class="header-title">{{ activeCategoryName }}</h1>

          <div class="header-row header-row--bottom">
            <button
              type="button"
              class="drawer-trigger"
              aria-label="打开分类抽屉"
              @click="drawerOpen = true"
            >
              <Icon name="lucide:menu" />
              <span>分类</span>
            </button>
            <span class="category-chip">{{ activeCategoryName }}</span>
          </div>
        </template>
      </header>

      <div class="masonry">
        <div
          v-for="(col, ci) in columns"
          :key="ci"
          class="masonry-col"
        >
          <template v-for="(item, ri) in col" :key="item.id">
            <div
              class="card-reveal"
              :style="{ '--enter-delay': `${ci * 80 + ri * 60}ms` }"
            >
              <IndexWaterfallCard
                v-if="item.type === 'image-text'"
                :image="item.image!"
                :title="item.title"
                :description="item.description!"
                :aspect-ratio="item.aspectRatio"
              />
              <IndexWaterfallTextCard
                v-else-if="item.type === 'text'"
                :title="item.title"
                :description="item.description!"
                :aspect-ratio="item.aspectRatio"
              />
              <IndexWaterfallImageCard
                v-else-if="item.type === 'image'"
                :image="item.image!"
                :title="item.title"
                :aspect-ratio="item.aspectRatio"
              />
              <IndexWaterfallPortfolioCard
                v-else-if="item.type === 'portfolio'"
                :images="item.images!"
                :title="item.title"
                :description="item.description!"
                :aspect-ratio="item.aspectRatio"
              />
              <IndexWaterfallProjectCard
                v-else
                :image="item.image!"
                :title="item.title"
                :description="item.description!"
                :tags="item.tags!"
                :aspect-ratio="item.aspectRatio"
              />
            </div>
          </template>
        </div>
      </div>

      <div ref="sentinel" class="sentinel">
        <template v-if="initLoading || loading">
          <div class="loading-bars">
            <span v-for="i in 3" :key="i" class="bar" :style="{ animationDelay: `${(i - 1) * 0.15}s` }" />
          </div>
        </template>
        <span v-else-if="hasMore" class="sentinel-text">继续滚动查看更多</span>
        <span v-else class="sentinel-text end">—— 已经到底了 ——</span>
      </div>
    </main>
  </div>
</template>

<style scoped>
.home-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 280px 1fr;
  align-items: start;
  gap: 0;
}

.home-sidebar {
  position: sticky;
  top: 64px;
  height: calc(100vh - 64px);
  align-self: start;
}

.home-main {
  min-width: 0;
  padding: 28px 24px 48px;
  display: flex;
  flex-direction: column;
}

/* ── Header ── */

.main-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 24px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--color-hairline);
}

.header-left {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.header-eyebrow {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--color-primary);
}

.header-title {
  font-family: var(--font-display);
  font-size: clamp(28px, 4vw, 44px);
  font-weight: 400;
  color: var(--color-ink);
  letter-spacing: -0.8px;
  line-height: 1.05;
  margin: 0;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.action-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--color-hairline);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--color-body);
  cursor: pointer;
  transition: all 0.15s ease;
}

.action-btn :deep(svg) {
  width: 16px;
  height: 16px;
}

.action-btn:hover {
  border-color: var(--color-ink);
  color: var(--color-ink);
}

.action-btn.primary {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
  padding: 0 16px;
}

.action-btn.primary:hover {
  background: var(--color-primary-active);
  border-color: var(--color-primary-active);
}

/* ── Masonry ── */

.masonry {
  display: flex;
  gap: 18px;
  align-items: flex-start;
}

.masonry-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.card-reveal {
  animation: card-enter 0.6s var(--ease-out-expo) both;
  animation-delay: var(--enter-delay, 0ms);
}

@keyframes card-enter {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ── Sentinel ── */

.sentinel {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 40px 0 24px;
  min-height: 56px;
}

.sentinel-text {
  font-size: 13px;
  color: var(--color-muted-soft);
}

.sentinel-text.end {
  color: var(--color-hairline);
}

.loading-bars {
  display: flex;
  gap: 5px;
  align-items: center;
}

.bar {
  width: 4px;
  height: 18px;
  background: var(--color-primary);
  border-radius: 2px;
  opacity: 0.5;
  animation: pulse 0.9s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 0.3; transform: scaleY(0.6); }
  50% { opacity: 0.8; transform: scaleY(1); }
}

/* ── Responsive (DESIGN.md breakpoints) ── */

@media (max-width: 767.98px) {
  .home-layout {
    grid-template-columns: 1fr;
  }

  .home-sidebar {
    display: none; /* rail 节点在移动端被 v-if 隐藏；drawer 由 BoDrawer 渲染到 body */
  }

  .home-main {
    padding: 16px 16px 32px;
  }

  .main-header {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    padding-bottom: 16px;
    margin-bottom: 16px;
  }

  .header-title {
    font-size: clamp(22px, 7vw, 30px);
  }
}

@media (min-width: 768px) and (max-width: 1023.98px) {
  .home-layout {
    grid-template-columns: 240px 1fr;
  }

  .home-main {
    padding: 20px 16px 32px;
  }
}

/* ── Mobile header rows ── */

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

.header-row--top {
  justify-content: space-between;
}

.header-row--bottom {
  flex-wrap: wrap;
  gap: 8px;
}

.action-btn--icon {
  width: 36px;
  height: 36px;
  padding: 0;
  justify-content: center;
}

.action-btn--icon.action-btn--primary {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
}

.drawer-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 16px;
  border-radius: var(--rounded-md, 8px);
  background: transparent;
  border: 1px solid var(--color-hairline);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  color: var(--color-ink);
  cursor: pointer;
  min-width: 44px;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.drawer-trigger:hover {
  border-color: var(--color-ink);
}

.drawer-trigger :deep(svg) {
  width: 18px;
  height: 18px;
}

.category-chip {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 14px;
  border-radius: 9999px;
  background: var(--color-surface-card);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
}
</style>
