<script setup lang="ts">
// 앱 공통 셸 — 좌측 사이드바(브랜드/네비/사용자) + 우측 본문 컬럼(상단 툴바 + 슬롯)
// 모든 인증 이후 화면이 이 레이아웃을 공유한다.
import { computed, onMounted, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useRoute, useRouter, RouterLink } from 'vue-router'
import { useAuthStore } from '@/stores/auth.store'
import { useNotificationStore } from '@/stores/notification.store'
import UserAvatar from '@/components/UserAvatar.vue'
import RelayMark from '@/components/RelayMark.vue'
import AgentChatPanel from '@/components/AgentChatPanel.vue'
import type { NotificationView } from '@/types/notification'

const route = useRoute()
const router = useRouter()
const authStore = useAuthStore()

// AI 채팅 패널 — 모든 화면 툴바에서 열 수 있다
const agentOpen = ref(false)

// 사이드바 접기(데스크톱) — localStorage 에 상태 보존
const collapsed = ref(localStorage.getItem('relay.sidebar.collapsed') === '1')
function toggleCollapse() {
  collapsed.value = !collapsed.value
  localStorage.setItem('relay.sidebar.collapsed', collapsed.value ? '1' : '0')
}

// 모바일 사이드바 드로어 — 화면 전환 시 자동 닫힘
const mobileOpen = ref(false)

// 현재 경로 기준으로 사이드바 네비 활성 항목을 판별
const activeNav = computed<'tasks' | 'projects'>(() =>
  route.path.startsWith('/projects') ? 'projects' : 'tasks',
)

// 현재 로그인 사용자(프로필 아바타는 UserAvatar 가 이미지/placeholder 로 표시)
const me = computed(() => authStore.user)

// --- 알림(실시간 SSE) ---
const notiStore = useNotificationStore()
const {
  views: notiViews,
  unreadCount,
  loading: notiLoading,
  loadingMore: notiLoadingMore,
  hasMore: notiHasMore,
} = storeToRefs(notiStore)

const notiOpen = ref(false)

// 화면 전환 시 모바일 드로어 닫기
watch(
  () => route.path,
  () => {
    mobileOpen.value = false
  },
)

// 알림 항목 클릭 — 읽음 처리 후 대상 화면으로 이동
async function onNotiClick(v: NotificationView) {
  notiOpen.value = false
  await notiStore.markRead(v.id)
  if (v.to) await router.push(v.to)
}

// AppShell 은 인증 이후 화면에서만 마운트되므로 여기서 스트림 연결(멱등)
// (store 싱글톤이 연결을 보유 → 화면 전환에도 끊기지 않음. 해제는 로그아웃 시에만)
onMounted(() => {
  if (authStore.isLoggedIn) void notiStore.connect()
})

// 로그아웃 → 알림 스트림 해제 후 로그인 화면으로
async function onLogout() {
  notiStore.disconnect()
  await authStore.logout()
  await router.push('/login')
}
</script>

<template>
  <div
    class="shell"
    :class="{ collapsed, 'mobile-open': mobileOpen }"
  >
    <!-- 모바일 드로어 배경 -->
    <div
      v-if="mobileOpen"
      class="sidebar-backdrop"
      @click="mobileOpen = false"
    />

    <!-- 좌측 사이드바 -->
    <aside class="sidebar">
      <RouterLink
        to="/projects"
        class="brand"
        title="Relay"
      >
        <div class="logo">
          <RelayMark white />
        </div>
        <span class="brand-name">Relay</span>
      </RouterLink>

      <nav class="sidenav">
        <RouterLink
          to="/tasks"
          class="nav-item"
          title="내 업무"
          :class="{ active: activeNav === 'tasks' }"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2" />
            <rect
              x="9"
              y="3"
              width="6"
              height="4"
              rx="1"
            />
            <path d="M9 13l2 2 4-4" />
          </svg>
          <span>내 업무</span>
        </RouterLink>
        <RouterLink
          to="/projects"
          class="nav-item"
          title="프로젝트"
          :class="{ active: activeNav === 'projects' }"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <rect
              x="3"
              y="3"
              width="7"
              height="7"
              rx="1.5"
            />
            <rect
              x="14"
              y="3"
              width="7"
              height="7"
              rx="1.5"
            />
            <rect
              x="3"
              y="14"
              width="7"
              height="7"
              rx="1.5"
            />
            <rect
              x="14"
              y="14"
              width="7"
              height="7"
              rx="1.5"
            />
          </svg>
          <span>프로젝트</span>
        </RouterLink>
      </nav>

      <!-- 하단: 접기 토글 + 사용자 프로필 + 메뉴(위로 펼침) -->
      <div class="side-bottom">
        <button
          class="collapse-btn"
          type="button"
          :title="collapsed ? '사이드바 펼치기' : '사이드바 접기'"
          :aria-label="collapsed ? '사이드바 펼치기' : '사이드바 접기'"
          @click="toggleCollapse"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          ><path d="M15 18l-6-6 6-6" /></svg>
          <span>사이드바 접기</span>
        </button>
        <div
          class="side-user"
          :title="me?.name ?? '사용자'"
        >
          <UserAvatar
            :url="me?.avatarUrl"
            :name="me?.name"
          />
          <span class="side-me-text">
            <span class="side-me-name">{{ me?.name }}</span>
            <span class="side-me-email">{{ me?.email }}</span>
          </span>
        </div>

        <button
          class="logout-btn"
          type="button"
          title="로그아웃"
          aria-label="로그아웃"
          @click="onLogout"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          >
            <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
            <path d="M16 17l5-5-5-5" />
            <path d="M21 12H9" />
          </svg>
          <span>로그아웃</span>
        </button>
      </div>
    </aside>

    <!-- 우측 본문 컬럼 -->
    <div class="main">
      <header class="topbar">
        <!-- 모바일 사이드바 열기(데스크톱에서는 숨김) -->
        <button
          class="icon-btn hamburger"
          type="button"
          title="메뉴"
          aria-label="메뉴 열기"
          @click="mobileOpen = true"
        >
          <svg
            width="18"
            height="18"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
          >
            <path d="M3 6h18M3 12h18M3 18h18" />
          </svg>
        </button>

        <!-- 화면별 추가 액션을 끼워 넣는 슬롯 -->
        <slot name="actions" />

        <div class="topbar-right">
          <!-- AI 어시스턴트 — 모든 화면에서 상시 노출 -->
          <button
            class="icon-btn agent"
            type="button"
            title="AI 어시스턴트"
            aria-label="AI 어시스턴트"
            @click="agentOpen = true"
          >
            <svg
              width="17"
              height="17"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path d="M12 3 13.4 8.6 19 10l-5.6 1.4L12 17l-1.4-5.6L5 10l5.6-1.4Z" />
              <path d="M18 16l.7 2.3L21 19l-2.3.7L18 22l-.7-2.3L15 19l2.3-.7Z" />
            </svg>
          </button>

          <div class="noti-wrap">
            <button
              class="icon-btn noti-btn"
              type="button"
              title="알림"
              aria-label="알림"
              aria-haspopup="menu"
              :aria-expanded="notiOpen"
              @click="notiOpen = !notiOpen"
            >
              <svg
                width="17"
                height="17"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
              >
                <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />
                <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
              </svg>
              <span
                v-if="unreadCount > 0"
                class="noti-badge"
                :aria-label="`읽지 않은 알림 ${unreadCount}건`"
              >{{ unreadCount > 99 ? '99+' : unreadCount }}</span>
            </button>

            <!-- 알림 드롭다운 -->
            <div
              v-if="notiOpen"
              class="noti-panel"
              role="menu"
            >
              <div class="noti-head">
                <span class="noti-title">알림</span>
                <button
                  v-if="unreadCount > 0"
                  type="button"
                  class="noti-readall"
                  @click="notiStore.markAllRead()"
                >
                  모두 읽음
                </button>
              </div>
              <div class="noti-list">
                <div
                  v-if="notiLoading && notiViews.length === 0"
                  class="noti-empty"
                >
                  불러오는 중…
                </div>
                <div
                  v-else-if="notiViews.length === 0"
                  class="noti-empty"
                >
                  새 알림이 없습니다.
                </div>
                <template v-else>
                  <button
                    v-for="v in notiViews"
                    :key="v.id"
                    type="button"
                    class="noti-item"
                    :class="{ unread: !v.read }"
                    role="menuitem"
                    @click="onNotiClick(v)"
                  >
                    <span
                      class="noti-dot"
                      :class="`tone-${v.tone || 'none'}`"
                    />
                    <span class="noti-body">
                      <!-- noti-text 의 v-html 은 store 에서 사용자 입력을 escapeHtml 한 뒤 신뢰 태그(<b>)만 조립한 마크업이다 (XSS 위험 없음) -->
                      <!-- eslint-disable vue/no-v-html -->
                      <span
                        class="noti-text"
                        v-html="v.html"
                      />
                      <!-- eslint-enable vue/no-v-html -->
                      <span class="noti-time">{{ v.time }}</span>
                    </span>
                  </button>
                  <button
                    v-if="notiHasMore"
                    type="button"
                    class="noti-more"
                    :disabled="notiLoadingMore"
                    @click="notiStore.loadMore()"
                  >
                    {{ notiLoadingMore ? '불러오는 중…' : '더보기' }}
                  </button>
                </template>
              </div>
            </div>
            <!-- 메뉴 바깥 클릭 시 닫기 -->
            <div
              v-if="notiOpen"
              class="noti-overlay"
              @click="notiOpen = false"
            />
          </div>
        </div>
      </header>

      <slot />
    </div>

    <!-- AI 채팅 패널 — 어디서든 열림 -->
    <AgentChatPanel
      :open="agentOpen"
      @close="agentOpen = false"
    />
  </div>
</template>

<style scoped>
.shell {
  display: flex;
  align-items: flex-start;
  min-height: 100vh;
}

/* ===== 좌측 사이드바 ===== */
.sidebar {
  width: 14.5rem;
  flex-shrink: 0;
  position: sticky;
  top: 0;
  height: 100vh;
  background: var(--panel);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 0.875rem 0;
  z-index: 50;
  transition: width 0.18s ease;
}
/* 접힘(아이콘 레일) */
.shell.collapsed .sidebar {
  width: 3.75rem;
}
.shell.collapsed .brand {
  padding-left: 0;
  padding-right: 0;
  justify-content: center;
}
.shell.collapsed .brand-name {
  display: none;
}
.shell.collapsed .sidenav {
  padding: 0 0.5rem;
}
.shell.collapsed .nav-item {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}
.shell.collapsed .nav-item span {
  display: none;
}
.shell.collapsed .side-user {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}
.shell.collapsed .side-me-text {
  display: none;
}
.shell.collapsed .logout-btn {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}
.shell.collapsed .logout-btn span {
  display: none;
}
.shell.collapsed .collapse-btn {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}
.shell.collapsed .collapse-btn span {
  display: none;
}
.shell.collapsed .collapse-btn svg {
  transform: rotate(180deg);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  font-weight: 700;
  font-size: 0.9375rem;
  letter-spacing: -0.0125rem;
  color: var(--text);
  text-decoration: none;
  padding: 0.25rem 1.125rem 0.875rem;
}
.brand .logo {
  width: 1.625rem;
  height: 1.625rem;
  border-radius: 0.4375rem;
  background: linear-gradient(135deg, #5b54f0, #4338ca);
  display: grid;
  place-items: center;
  color: #fff;
  font-size: 0.9375rem;
  font-weight: 800;
  box-shadow: 0 2px 6px rgba(79, 70, 229, 0.35);
}

.sidenav {
  display: flex;
  flex-direction: column;
  gap: 0.1875rem;
  padding: 0 0.625rem;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  padding: 0.5625rem 0.75rem;
  border-radius: var(--radius);
  color: var(--text-2);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}
.nav-item svg {
  width: 1.125rem;
  height: 1.125rem;
  flex-shrink: 0;
}
.nav-item:hover {
  background: #f1f2f4;
  color: var(--text);
}
.nav-item.active {
  background: var(--accent-weak);
  color: var(--accent);
  font-weight: 600;
}

/* 사이드바 하단: 접기 토글 + 사용자 프로필 */
.side-bottom {
  margin-top: auto;
  position: relative;
  padding: 0.625rem;
  border-top: 1px solid var(--border);
}
.collapse-btn {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  width: 100%;
  padding: 0.5rem 0.75rem;
  margin-bottom: 0.25rem;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  color: var(--text-3);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.781rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}
.collapse-btn:hover {
  background: #f1f2f4;
  color: var(--text);
}
.collapse-btn svg {
  width: 1.125rem;
  height: 1.125rem;
  flex-shrink: 0;
  transition: transform 0.18s ease;
}
.side-user {
  display: flex;
  align-items: center;
  gap: 0.5625rem;
  width: 100%;
  padding: 0.4375rem 0.5rem;
}
.side-user > :first-child {
  width: 1.875rem;
  height: 1.875rem;
  flex-shrink: 0;
}
/* 로그아웃 — 항상 노출되는 버튼 */
.logout-btn {
  display: flex;
  align-items: center;
  gap: 0.6875rem;
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.5rem 0.75rem;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  color: var(--text-2);
  cursor: pointer;
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}
.logout-btn:hover {
  background: var(--red-weak);
  color: var(--red);
}
.logout-btn svg {
  width: 1.125rem;
  height: 1.125rem;
  flex-shrink: 0;
}
.side-me-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.0625rem;
}
.side-me-name {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.side-me-email {
  font-size: 0.6875rem;
  color: var(--text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ===== 우측 본문 컬럼 ===== */
.main {
  flex: 1;
  min-width: 0;
}

.topbar {
  height: 3.25rem;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1.25rem;
  position: sticky;
  top: 0;
  z-index: 40;
}
.topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.icon-btn {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--text-2);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.icon-btn:hover {
  background: #f1f2f4;
  color: var(--text);
}
/* AI 어시스턴트 버튼 강조 */
.icon-btn.agent {
  color: var(--accent);
}
.icon-btn.agent:hover {
  background: var(--accent-weak);
  color: var(--accent-hover);
}
/* 알림 종 + 드롭다운 */
.noti-wrap {
  position: relative;
}
.noti-btn {
  position: relative;
}
.noti-badge {
  position: absolute;
  top: -0.125rem;
  right: -0.125rem;
  min-width: 1rem;
  height: 1rem;
  padding: 0 0.25rem;
  border-radius: 0.5rem;
  background: var(--red);
  color: #fff;
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 1rem;
  text-align: center;
  box-shadow: 0 0 0 2px var(--panel);
}
.noti-panel {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  z-index: 60;
  width: 22rem;
  max-width: 90vw;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  overflow: hidden;
}
.noti-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.6875rem 0.875rem;
  border-bottom: 1px solid var(--border);
}
.noti-title {
  font-size: 0.844rem;
  font-weight: 700;
  color: var(--text);
}
.noti-readall {
  border: none;
  background: transparent;
  color: var(--accent);
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0.125rem 0.25rem;
  border-radius: var(--radius-sm);
}
.noti-readall:hover {
  background: var(--accent-weak);
}
.noti-list {
  max-height: 24rem;
  overflow-y: auto;
}
.noti-empty {
  padding: 1.75rem 0.875rem;
  text-align: center;
  font-size: 0.781rem;
  color: var(--text-3);
}
.noti-item {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  width: 100%;
  padding: 0.6875rem 0.875rem;
  border: none;
  border-top: 1px solid var(--border);
  background: transparent;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.noti-item:first-child {
  border-top: none;
}
.noti-item:hover {
  background: #fafbfc;
}
.noti-item.unread {
  background: var(--accent-weak);
}
.noti-item.unread:hover {
  background: #eceafd;
}
.noti-dot {
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  margin-top: 0.375rem;
  flex-shrink: 0;
  background: var(--text-3);
}
.noti-dot.tone-blue {
  background: var(--blue);
}
.noti-dot.tone-amber {
  background: var(--amber);
}
.noti-dot.tone-green {
  background: var(--green);
}
.noti-dot.tone-red {
  background: var(--red);
}
.noti-dot.tone-accent {
  background: var(--accent);
}
.noti-body {
  flex: 1;
  min-width: 0;
}
.noti-text {
  display: block;
  font-size: 0.8125rem;
  color: var(--text);
  line-height: 1.45;
}
.noti-text :deep(b) {
  font-weight: 600;
}
.noti-time {
  display: block;
  margin-top: 0.1875rem;
  font-size: 0.6875rem;
  color: var(--text-3);
}
.noti-more {
  width: 100%;
  padding: 0.625rem;
  border: none;
  border-top: 1px solid var(--border);
  background: transparent;
  font-family: inherit;
  font-size: 0.781rem;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
}
.noti-more:hover {
  background: #fafbfc;
}
.noti-more:disabled {
  color: var(--text-3);
  cursor: default;
}
.noti-overlay {
  position: fixed;
  inset: 0;
  z-index: 55;
}

/* 햄버거(모바일 전용) / 드로어 배경 — 데스크톱에서는 숨김 */
.hamburger {
  display: none;
}
.sidebar-backdrop {
  display: none;
}

/* ===== 모바일(≤48rem) — 사이드바를 오버레이 드로어로 ===== */
@media (max-width: 48rem) {
  .hamburger {
    display: grid;
  }
  .sidebar {
    position: fixed;
    left: 0;
    top: 0;
    width: 15rem;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 0 8px 30px rgba(20, 24, 33, 0.18);
  }
  /* 모바일에서는 접힘 상태를 무시(항상 전체 폭 드로어) */
  .shell.collapsed .sidebar {
    width: 15rem;
  }
  .shell.mobile-open .sidebar {
    transform: translateX(0);
  }
  /* 접기 토글은 모바일에서 불필요 */
  .collapse-btn {
    display: none;
  }
  /* 접힘 축소 규칙 무효화(드로어는 항상 펼친 모양) */
  .shell.collapsed .brand,
  .shell.collapsed .nav-item,
  .shell.collapsed .side-user,
  .shell.collapsed .logout-btn,
  .shell.collapsed .collapse-btn {
    justify-content: flex-start;
    padding-left: 0.75rem;
    padding-right: 0.75rem;
  }
  .shell.collapsed .brand-name,
  .shell.collapsed .nav-item span,
  .shell.collapsed .logout-btn span {
    display: inline;
  }
  .shell.collapsed .side-me-text {
    display: flex;
  }
  .shell.collapsed .brand {
    padding-left: 1.125rem;
  }
  .shell.collapsed .side-user {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(20, 24, 33, 0.45);
    z-index: 49;
  }
}
</style>
