<script setup>
import { computed, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { boardApi } from '@/api/boardApi'
import { formatRelative } from '@/utils/datetime'
import { useAuthStore } from '@/stores/auth'
import { DEFAULT_BOARD } from '@/constants/boards'
import IconBtn from '@/components/ui/IconBtn.vue'
import UserAvatar from '@/components/UserAvatar.vue'

const route = useRoute()
const router = useRouter()
const auth = useAuthStore()
const isAdmin = computed(() => auth.user?.role === 'ADMIN')

// 비추천 20개 이상이면 블라인드 (작성자·관리자에겐 정상 표시)
const BLIND_DOWN = 20
function isBlinded(p) {
  return (p.downCount || 0) >= BLIND_DOWN
}
function showBlindMsg(p) {
  return isBlinded(p) && !isAdmin.value && auth.user?.id !== p.authorId
}

// 현재 게시판(카테고리)
const category = computed(() => route.params.category || DEFAULT_BOARD)

const posts = ref([])
const tags = ref([])
const loading = ref(false)
const error = ref(null)

const page = ref(1)
const size = ref(10)
const totalPages = ref(0)
const totalElements = ref(0)

const activeTag = ref('')
const activeKeyword = ref('')
const activeSearchType = ref('title')

// 검색 모달 상태
const searchOpen = ref(false)
const modalKeyword = ref('')
const modalSearchType = ref('title')

const SEARCH_LABELS = { title: '제목', content: '본문', comment: '댓글' }
const searchTypeLabel = computed(() => SEARCH_LABELS[activeSearchType.value] || '제목')

async function load() {
  loading.value = true
  error.value = null
  try {
    const res = await boardApi.list({
      category: category.value,
      page: page.value,
      size: size.value,
      tag: activeTag.value,
      keyword: activeKeyword.value,
      searchType: activeSearchType.value,
    })
    posts.value = res.content
    totalPages.value = res.totalPages
    totalElements.value = res.totalElements
  } catch (e) {
    error.value = e.response?.data?.message || '목록을 불러오지 못했습니다.'
  } finally {
    loading.value = false
  }
}

async function loadTags() {
  try {
    tags.value = await boardApi.tags(category.value)
  } catch {
    tags.value = []
  }
}

// URL 쿼리 → 상태 동기화 (뒤로가기 시 검색 조건/페이지 복원)
function syncFromQuery() {
  page.value = Number(route.query.page) || 1
  activeTag.value = route.query.tag || ''
  activeKeyword.value = route.query.keyword || ''
  activeSearchType.value = route.query.searchType || 'title'
}

// 상태 → URL 쿼리 반영 (빈 값은 생략). 쿼리 변경을 watch 가 감지해 load 수행
function pushQuery({ page: pg = 1, tag = '', keyword = '', searchType = 'title' }) {
  const q = {}
  if (pg && pg > 1) q.page = String(pg)
  if (tag) q.tag = tag
  if (keyword) {
    q.keyword = keyword
    q.searchType = searchType || 'title'
  }
  router.push({ query: q })
}

function filterByTag(tag) {
  const next = activeTag.value === tag ? '' : tag
  pushQuery({ page: 1, tag: next, keyword: activeKeyword.value, searchType: activeSearchType.value })
}

function goPage(p) {
  if (p < 1 || p > totalPages.value || p === page.value) return
  pushQuery({ page: p, tag: activeTag.value, keyword: activeKeyword.value, searchType: activeSearchType.value })
}

function openSearch() {
  modalKeyword.value = activeKeyword.value
  modalSearchType.value = activeSearchType.value || 'title'
  searchOpen.value = true
}

function doSearch() {
  searchOpen.value = false
  pushQuery({ page: 1, tag: activeTag.value, keyword: modalKeyword.value.trim(), searchType: modalSearchType.value })
}

function clearSearch() {
  pushQuery({ page: 1, tag: activeTag.value })
}

// 경로(게시판 전환)·쿼리(검색·페이지·뒤로가기)가 바뀌면 상태 동기화 후 재조회
watch(
  () => route.fullPath,
  () => {
    syncFromQuery()
    load()
  },
  { immediate: true },
)

// 게시판이 바뀌면 태그 필터도 그 게시판 기준으로 다시 로드
watch(category, loadTags, { immediate: true })
</script>

<template>
  <section class="board">
    <div class="list-top">
      <div class="tag-filter">
        <button
          v-for="t in tags"
          :key="t"
          type="button"
          class="tag-chip"
          :class="{ active: activeTag === t }"
          @click="filterByTag(t)"
        >{{ t }}</button>
      </div>
      <IconBtn icon="edit" title="글쓰기" variant="primary" @click="router.push(`/board/${category}/write`)" />
    </div>

    <!-- 검색 적용 표시 -->
    <div v-if="activeKeyword" class="active-search">
      <span>{{ searchTypeLabel }} 검색: "{{ activeKeyword }}"</span>
      <IconBtn icon="close" title="검색 해제" size="sm" @click="clearSearch" />
    </div>

    <p v-if="error" class="msg error">{{ error }}</p>
    <p v-if="loading" class="msg">불러오는 중...</p>

    <table v-else-if="posts.length" class="post-table">
      <thead>
        <tr>
          <th>제목</th>
          <th class="col-author">작성자</th>
          <th class="col-num">조회</th>
          <th class="col-recommend">추천</th>
          <th class="col-date">작성일</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="p in posts" :key="p.id" :class="{ 'notice-row': p.notice }" @click="router.push({ path: `/board/${category}/${p.id}`, query: route.query })">
          <td>
            <span v-if="p.notice" class="notice-badge">공지</span>
            <span v-if="p.hot" class="hot-badge">🔥 인기</span>
            <template v-if="p.blocked && !isAdmin">
              <span class="blocked-msg">🚫 관리자에 의해 제한된 게시글입니다.</span>
            </template>
            <template v-else-if="showBlindMsg(p)">
              <span class="blocked-msg">🙈 비추천이 많아 블라인드된 게시글입니다.</span>
            </template>
            <template v-else>
              <span class="title">{{ p.title }}</span>
              <span v-if="p.blocked" class="blocked-badge">제한</span>
              <span v-if="isBlinded(p)" class="blind-badge">블라인드</span>
              <span v-if="p.commentCount" class="comment-count">[{{ p.commentCount }}]</span>
            </template>
          </td>
          <td class="col-author">
            <span class="author-cell">
              <UserAvatar
                :name="p.authorName"
                :google-picture="p.authorGooglePicture"
                :profile-image="p.authorProfileImage"
                :size="16"
              />
              <span class="author-name">{{ p.authorName }}</span>
            </span>
          </td>
          <td class="col-num">{{ p.viewCount }}</td>
          <td class="col-recommend">{{ p.upCount || 0 }}</td>
          <td class="col-date">{{ formatRelative(p.createdAt) }}</td>
        </tr>
      </tbody>
    </table>
    <p v-else-if="!loading" class="msg">게시글이 없습니다.</p>

    <!-- 하단: 페이징과 같은 행에 돋보기(검색) 버튼 -->
    <div class="list-footer">
      <nav v-if="totalPages > 1" class="pagination">
        <IconBtn icon="chevronLeft" title="이전" size="sm" :disabled="page <= 1" @click="goPage(page - 1)" />
        <button
          v-for="p in totalPages"
          :key="p"
          type="button"
          class="page-num"
          :class="{ active: p === page }"
          @click="goPage(p)"
        >{{ p }}</button>
        <IconBtn icon="chevronRight" title="다음" size="sm" :disabled="page >= totalPages" @click="goPage(page + 1)" />
      </nav>
      <IconBtn icon="search" title="검색" class="search-btn" @click="openSearch" />
    </div>

    <!-- 검색 모달 -->
    <Teleport to="body">
      <Transition name="fade">
        <div v-if="searchOpen" class="modal-backdrop" @click.self="searchOpen = false">
          <div class="modal" role="dialog" aria-modal="true" aria-label="검색">
            <button class="close" type="button" aria-label="닫기" @click="searchOpen = false">×</button>
            <form class="search-form" @submit.prevent="doSearch">
              <select v-model="modalSearchType" class="search-type">
                <option value="title">제목</option>
                <option value="content">본문</option>
                <option value="comment">댓글</option>
              </select>
              <input v-model="modalKeyword" type="text" placeholder="검색어를 입력하세요" />
              <IconBtn icon="search" title="검색" variant="primary" type="submit" />
            </form>
          </div>
        </div>
      </Transition>
    </Teleport>
  </section>
</template>

<style scoped>
.board {
  max-width: 900px;
  margin: 0 auto;
}
.board-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
}
h1 {
  font-size: 1.5rem;
}
button {
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-background-soft);
  color: var(--color-text);
  cursor: pointer;
}
button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.list-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.list-top .tag-filter {
  flex: 1;
  margin-bottom: 0;
}
.list-top > .icon-btn {
  flex: none;
}
.tag-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 1rem;
}
.tag-chip {
  padding: 0.25rem 0.6rem;
  font-size: 0.85rem;
  border-radius: 999px;
}
.tag-chip.active {
  border-color: hsla(160, 100%, 37%, 1);
  color: hsla(160, 100%, 37%, 1);
}
.active-search {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  font-size: 0.9rem;
}
.active-search .clear {
  padding: 0.1rem 0.45rem;
  font-size: 0.78rem;
}
.post-table {
  width: 100%;
  border-collapse: collapse;
}
.post-table th,
.post-table td {
  padding: 0.6rem 0.5rem;
  border-bottom: 1px solid var(--color-border);
  text-align: left;
  font-size: 0.92rem;
}
.post-table tbody tr {
  cursor: pointer;
}
.post-table tbody tr:hover {
  background: var(--color-background-soft);
}
.col-num {
  width: 60px;
  text-align: center;
}
.col-recommend {
  width: 56px;
  text-align: center;
}
.col-author {
  width: 130px;
}
.author-cell {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;
}
.author-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.col-date {
  width: 110px;
  white-space: nowrap;
}
.comment-count {
  margin-left: 0.3rem;
  color: hsla(160, 100%, 37%, 1);
  font-size: 0.85rem;
}
.blocked-msg {
  color: #c0392b;
  font-size: 0.9rem;
}
.blocked-badge {
  margin-left: 0.35rem;
  padding: 0.05rem 0.35rem;
  border: 1px solid #c0392b;
  border-radius: 3px;
  color: #c0392b;
  font-size: 0.72rem;
}
.notice-badge {
  margin-right: 0.4rem;
  padding: 0.05rem 0.4rem;
  border-radius: 3px;
  background: hsla(160, 100%, 37%, 1);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  vertical-align: middle;
}
.notice-row {
  background: hsla(160, 100%, 37%, 0.06);
}
.notice-row .title {
  font-weight: 600;
}
.hot-badge {
  margin-right: 0.4rem;
  padding: 0.05rem 0.4rem;
  border-radius: 3px;
  background: #e74c3c;
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  vertical-align: middle;
}
.blind-badge {
  margin-left: 0.35rem;
  padding: 0.05rem 0.35rem;
  border: 1px solid var(--color-border);
  border-radius: 3px;
  color: var(--color-text);
  opacity: 0.6;
  font-size: 0.72rem;
}

/* 하단 영역: 페이징(가운데) + 돋보기(오른쪽) 같은 행 */
.list-footer {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.4rem;
  margin-top: 1.5rem;
}
.pagination {
  display: flex;
  gap: 0.3rem;
}
.page-num.active {
  border-color: hsla(160, 100%, 37%, 1);
  color: hsla(160, 100%, 37%, 1);
}
.search-btn {
  position: absolute;
  right: 0;
}
.msg {
  margin: 1rem 0;
}
.msg.error {
  color: #c0392b;
}

/* 검색 모달 */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  padding: 1rem;
}
.modal {
  position: relative;
  width: 100%;
  max-width: 420px;
  padding: 2.5rem 1.5rem 1.5rem;
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
}
.modal .close {
  position: absolute;
  top: 0.5rem;
  right: 0.6rem;
  width: 2rem;
  height: 2rem;
  border: 0;
  background: transparent;
  font-size: 1.5rem;
  line-height: 1;
}
.search-form {
  display: flex;
  gap: 0.5rem;
}
.search-type {
  flex: none;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-background-soft);
  color: var(--color-text);
}
.search-form > .icon-btn {
  flex: none;
}
.search-form input {
  flex: 1;
  min-width: 0; /* flex 자식이 내용 최소폭 아래로 줄어들 수 있게 — 검색 버튼이 모달 밖으로 밀리는 문제 방지 */
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-background-soft);
  color: var(--color-text);
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.18s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/* ===== 모바일: 표를 2줄(제목 / 메타) 카드로 ===== */
@media (max-width: 768px) {
  .board {
    max-width: 100%;
  }
  .post-table thead {
    display: none;
  }
  .post-table,
  .post-table tbody,
  .post-table tr,
  .post-table td {
    display: block;
  }
  .post-table tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.5rem;
    row-gap: 0.15rem;
    padding: 0.7rem 0.2rem;
    border-bottom: 1px solid var(--color-border);
  }
  .post-table td {
    padding: 0;
    border: 0;
    width: auto !important;
    text-align: left !important;
    font-size: 0.8rem;
    opacity: 0.7;
  }
  /* 제목(첫 번째 셀): 첫 줄 전체 */
  .post-table td:nth-child(1) {
    order: 0;
    width: 100% !important;
    font-size: 0.97rem;
    opacity: 1;
    font-weight: 500;
  }
  .post-table .col-author {
    order: 2;
  }
  .post-table .col-num {
    order: 3;
  }
  .post-table .col-num::before {
    content: '· 조회 ';
  }
  .post-table .col-recommend {
    order: 4;
  }
  .post-table .col-recommend::before {
    content: '· 추천 ';
  }
  .post-table .col-date {
    order: 5;
  }
  .post-table .col-date::before {
    content: '· ';
  }
}
</style>
