<template>
  <aside class="sidebar">
    <div class="sidebar-top">
      <button
        class="profile-icon"
        type="button"
        title="Profile"
        aria-label="Profile"
        @click="handleProfile"
      >
        <span class="profile-initial">U</span>
      </button>
    </div>
    <nav class="sidebar-nav">
      <button
        v-for="item in topItems"
        :key="item.id"
        class="nav-item"
        :class="[
          item.variant ? `nav-item--${item.variant}` : '',
          { active: activeId === item.id, 'has-icon': !!getGroupIcon(item.iconId) }
        ]"
        type="button"
        :title="item.label"
        :aria-current="activeId === item.id ? 'page' : undefined"
        @click="handleItemClick(item)"
        @contextmenu.prevent="handleItemContextMenu($event, item)"
      >
        <Icon
          v-if="getGroupIcon(item.iconId)"
          class="nav-icon"
          :icon="getGroupIcon(item.iconId)"
          aria-hidden="true"
        />
        <span class="nav-text">{{ item.label }}</span>
      </button>
    </nav>
    <div class="sidebar-bottom">
      <button
        v-for="item in bottomItems"
        :key="item.id"
        class="nav-item"
        :class="{ active: activeId === item.id, 'has-icon': !!getGroupIcon(item.iconId) }"
        type="button"
        :title="item.label"
        :aria-current="activeId === item.id ? 'page' : undefined"
        @click="handleItemClick(item)"
        @contextmenu.prevent="handleItemContextMenu($event, item)"
      >
        <Icon
          v-if="getGroupIcon(item.iconId)"
          class="nav-icon"
          :icon="getGroupIcon(item.iconId)"
          aria-hidden="true"
        />
        <span class="nav-text">{{ item.label }}</span>
      </button>
    </div>
  </aside>
  <teleport to="body">
    <div
      v-if="menuState.open"
      class="sidebar-menu-overlay"
      @click.self="closeMenu"
      @contextmenu.prevent="closeMenu"
    >
      <div ref="menuRef" class="sidebar-menu" :style="menuStyle">
        <button class="sidebar-menu-item" type="button" @click="handleEdit">
          <Icon class="menu-icon" icon="lucide:pencil" aria-hidden="true" />
          编辑
        </button>
        <button
          class="sidebar-menu-item menu-danger"
          type="button"
          @click="handleRemove"
        >
          <Icon class="menu-icon" icon="lucide:trash-2" aria-hidden="true" />
          移除
        </button>
      </div>
    </div>
  </teleport>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed, watch, nextTick } from 'vue';
import { Icon } from '@iconify/vue';
import { groupIcons } from '@/config/groupIcons';
import {
  sidebarBaseItems,
  sidebarAddItem,
  sidebarBottomItems,
  type SidebarItem
} from '@/config/sidebarItems';

type NavItem = SidebarItem;

type SidebarOverride = {
  label?: string;
  iconId?: string;
};

const STORAGE_KEY = 'itab_sidebar_active';

const props = defineProps<{
  groups?: NavItem[];
  baseOverrides?: Record<string, SidebarOverride>;
  hiddenBaseIds?: string[];
}>();

const baseIdSet = new Set(sidebarBaseItems.map(item => item.id));

const resolvedBaseItems = computed(() => {
  const overrides = props.baseOverrides ?? {};
  const hidden = new Set(props.hiddenBaseIds ?? []);
  return sidebarBaseItems
    .filter(item => !hidden.has(item.id))
    .map(item => {
      const override = overrides[item.id];
      return {
        ...item,
        label: override?.label ?? item.label,
        iconId: override?.iconId ?? item.iconId,
      };
    });
});

const topItems = computed(() => [...resolvedBaseItems.value, ...(props.groups ?? []), sidebarAddItem]);
const bottomItems = computed(() => sidebarBottomItems);
const allItems = computed(() => [...topItems.value, ...bottomItems.value]);
const selectableItems = computed(() => allItems.value.filter(item => !item.action));
const groupIconMap = new Map(groupIcons.map(icon => [icon.id, icon.icon]));
const getGroupIcon = (iconId?: string) => (iconId ? groupIconMap.get(iconId) : undefined);

const emit = defineEmits<{
  (event: 'select', id: string): void;
  (event: 'add'): void;
  (event: 'edit-group', payload: { id: string; source: 'base' | 'group' }): void;
  (event: 'remove-group', payload: { id: string; source: 'base' | 'group' }): void;
  (event: 'settings'): void;
  (event: 'profile'): void;
}>();

const activeId = ref(
  resolvedBaseItems.value[0]?.id ??
    props.groups?.[0]?.id ??
    selectableItems.value[0]?.id ??
    sidebarAddItem.id
);

onMounted(() => {
  const saved = localStorage.getItem(STORAGE_KEY);
  const allowed = selectableItems.value.some(item => item.id === saved);
  if (saved && allowed) {
    activeId.value = saved;
  }
});

watch(selectableItems, items => {
  if (!items.length) return;
  if (!items.some(item => item.id === activeId.value)) {
    setActive(items[0].id);
  }
});

const setActive = (id: string) => {
  if (!selectableItems.value.some(item => item.id === id)) return;
  activeId.value = id;
  localStorage.setItem(STORAGE_KEY, id);
};

watch(
  () => activeId.value,
  id => {
    if (selectableItems.value.some(item => item.id === id)) {
      emit('select', id);
    }
  },
  { immediate: true }
);

const handleItemClick = (item: NavItem) => {
  if (item.action === 'add') {
    emit('add');
    return;
  }

  if (item.action === 'settings') {
    emit('settings');
    return;
  }

  setActive(item.id);
  emit('select', item.id);
};

const handleProfile = () => {
  emit('profile');
};

const menuRef = ref<HTMLElement | null>(null);
const menuPosition = ref({ x: 0, y: 0 });
const menuState = ref<{ open: boolean; itemId: string | null; source: 'base' | 'group' | null }>({
  open: false,
  itemId: null,
  source: null,
});

const viewportPadding = 8;

const menuStyle = computed(() => ({
  left: `${menuPosition.value.x}px`,
  top: `${menuPosition.value.y}px`,
}));

const clampMenuPosition = () => {
  const menuEl = menuRef.value;
  if (!menuEl) return;
  const rect = menuEl.getBoundingClientRect();
  const maxX = Math.max(viewportPadding, window.innerWidth - rect.width - viewportPadding);
  const maxY = Math.max(viewportPadding, window.innerHeight - rect.height - viewportPadding);
  const x = Math.min(Math.max(menuPosition.value.x, viewportPadding), maxX);
  const y = Math.min(Math.max(menuPosition.value.y, viewportPadding), maxY);
  menuPosition.value = { x, y };
};

const closeMenu = () => {
  menuState.value = { open: false, itemId: null, source: null };
};

const handleKeydown = (event: KeyboardEvent) => {
  if (event.key === 'Escape' && menuState.value.open) {
    event.preventDefault();
    closeMenu();
  }
};

const handleItemContextMenu = async (event: MouseEvent, item: NavItem) => {
  if (item.action) return;
  menuState.value = {
    open: true,
    itemId: item.id,
    source: baseIdSet.has(item.id) ? 'base' : 'group',
  };
  menuPosition.value = { x: event.clientX, y: event.clientY };
  await nextTick();
  clampMenuPosition();
};

const handleEdit = () => {
  if (!menuState.value.itemId || !menuState.value.source) return;
  emit('edit-group', { id: menuState.value.itemId, source: menuState.value.source });
  closeMenu();
};

const handleRemove = () => {
  if (!menuState.value.itemId || !menuState.value.source) return;
  emit('remove-group', { id: menuState.value.itemId, source: menuState.value.source });
  closeMenu();
};

watch(
  () => menuState.value.open,
  open => {
    if (!open) return;
    clampMenuPosition();
  }
);

onMounted(() => {
  window.addEventListener('resize', clampMenuPosition);
  window.addEventListener('keydown', handleKeydown);
});

onUnmounted(() => {
  window.removeEventListener('resize', clampMenuPosition);
  window.removeEventListener('keydown', handleKeydown);
});
</script>

<style lang="scss" scoped>
@import '@/styles/tokens.scss';

.sidebar {
  width: var(--sidebar-width);
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--sidebar-padding-y) 0;
  background-color: rgba(10, 10, 10, 0.5);
  backdrop-filter: blur($backdrop-filter-blur) saturate($backdrop-filter-saturation);
  border-right: 1px solid rgba(255, 255, 255, 0.08);
  position: fixed;
  left: 0;
  top: 0;
  z-index: $z-index-sidebar;
}

.profile-icon {
  width: var(--sidebar-profile-size);
  height: var(--sidebar-profile-size);
  border-radius: 50%;
  background-color: $color-accent;
  margin-bottom: var(--sidebar-profile-margin-bottom);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: $color-text-primary;
  border: none;
  cursor: pointer;
  transition: transform $motion-duration-sm $motion-easing-standard,
    box-shadow $motion-duration-sm $motion-easing-standard;
}

.profile-icon:hover {
  box-shadow: $shadow-sm;
}

.profile-icon:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.35);
  outline-offset: 2px;
}

.profile-initial {
  font-weight: 600;
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sidebar-nav-gap);
  width: 100%;
}

.nav-item {
  width: var(--sidebar-nav-item-size);
  height: var(--sidebar-nav-item-size);
  border-radius: $border-radius-medium;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  color: $color-text-secondary;
  background: transparent;
  border: 1px solid transparent;
  transition: background-color $motion-duration-sm $motion-easing-standard,
    color $motion-duration-sm $motion-easing-standard,
    transform $motion-duration-sm $motion-easing-standard,
    box-shadow $motion-duration-sm $motion-easing-standard,
    border-color $motion-duration-sm $motion-easing-standard;
  position: relative;
  cursor: pointer;
}

.nav-item::before {
  content: '';
  position: absolute;
  left: var(--sidebar-active-indicator-offset);
  top: 50%;
  transform: translateY(-50%) scaleY(0.6);
  width: var(--sidebar-active-indicator-width);
  height: var(--sidebar-active-indicator-height);
  background-color: $color-accent;
  border-radius: 2px;
  opacity: 0;
  transition: opacity $motion-duration-sm $motion-easing-standard,
    transform $motion-duration-sm $motion-easing-standard;
}

.nav-item:hover { 
  background-color: rgba(255, 255, 255, 0.1);
  color: $color-text-primary;
  box-shadow: $shadow-sm;
}

.nav-item:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.35);
  outline-offset: 2px;
}

.nav-item.active {
  background-color: rgba(255, 255, 255, 0.14);
  color: $color-text-primary;
}

.nav-item.active::before {
  opacity: 1;
  transform: translateY(-50%) scaleY(1.5);
}

.nav-icon {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  margin-bottom: var(--sidebar-icon-margin-bottom, 4px);
}

.nav-item.has-icon .nav-text {
  font-size: 10px;
  line-height: 1;
}

.nav-text {
  font-size: 12px;
  line-height: 1.1;
  display: var(--sidebar-label-display, block);
}

.nav-item--add {
  margin-top: var(--sidebar-add-margin-top);
  border: 1px dashed rgba(255, 255, 255, 0.2);
  color: rgba(255, 255, 255, 0.6);
}

.nav-item--add::before {
  opacity: 0;
}

.nav-item--add:hover {
  border-color: rgba(255, 255, 255, 0.6);
  color: $color-text-primary;
  background-color: rgba(255, 255, 255, 0.05);
  box-shadow: none;
}

.sidebar-bottom {
  margin-top: auto;
}

.sidebar-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: $z-index-menu - 1;
}

.sidebar-menu {
  position: absolute;
  min-width: 140px;
  background-color: $color-surface-2;
  backdrop-filter: blur($backdrop-filter-blur) saturate($backdrop-filter-saturation);
  border-radius: $border-radius-medium;
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: $shadow-lg;
  z-index: $z-index-menu;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  animation: sidebar-menu-pop 110ms cubic-bezier(0.16, 1, 0.3, 1);
}

.sidebar-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: $border-radius-small;
  background: transparent;
  border: none;
  color: $color-text-primary;
  font-size: 13px;
  cursor: pointer;
  transition: background-color $motion-duration-sm $motion-easing-standard;
}

.sidebar-menu-item:hover {
  background-color: rgba(255, 255, 255, 0.1);
}

.sidebar-menu-item.menu-danger {
  color: #ff5c5c;
}

.sidebar-menu-item.menu-danger .menu-icon {
  color: #ff5c5c;
}

.menu-icon {
  width: 16px;
  height: 16px;
}

@keyframes sidebar-menu-pop {
  0% {
    opacity: 0;
    transform: scale(0.92);
  }
  100% {
    transform: scale(1);
  }
}
</style>
