<script setup>
definePageMeta({
  layout: 'admin'
})

const memberSearchQuery = ref('')
const isFilterOpen = ref(false)
const filterDrafts = ref([
  {
    id: 1,
    field: 'email',
    operator: 'contains',
    value: ''
  }
])
const activeFilters = ref([])
let nextFilterId = 2

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

const dateFilterFields = ['lastSeenAt', 'createdAt']

const filterFieldOptions = [
  { value: 'name', label: '이름' },
  { value: 'email', label: '이메일' },
  { value: 'label', label: '레이블' },
  { value: 'status', label: '상태' },
  { value: 'lastSeenAt', label: '최근 접속' },
  { value: 'createdAt', label: '가입일' }
]

const operatorOptionsByFieldType = {
  text: [
    { value: 'contains', label: '포함' },
    { value: 'notContains', label: '포함하지 않음' },
    { value: 'is', label: '일치' },
    { value: 'startsWith', label: '시작' },
    { value: 'endsWith', label: '끝남' }
  ],
  status: [
    { value: 'is', label: '일치' },
    { value: 'isNot', label: '일치하지 않음' }
  ],
  date: [
    { value: 'before', label: '이전' },
    { value: 'after', label: '이후' },
    { value: 'empty', label: '없음' },
    { value: 'notEmpty', label: '있음' }
  ]
}

const statusFilterOptions = [
  { value: '활성', label: '활성' },
  { value: '비활성', label: '비활성' }
]

/**
 * 필터 필드 타입을 반환한다.
 * @param {string} field - 필터 필드
 * @returns {'text'|'status'|'date'} 필드 타입
 */
const getFilterFieldType = (field) => {
  if (field === 'status') {
    return 'status'
  }

  if (dateFilterFields.includes(field)) {
    return 'date'
  }

  return 'text'
}

/**
 * 필터 필드에 맞는 연산자 목록을 반환한다.
 * @param {string} field - 필터 필드
 * @returns {Array<{ value: string, label: string }>} 연산자 목록
 */
const getFilterOperatorOptions = (field) => operatorOptionsByFieldType[getFilterFieldType(field)]

/**
 * 필터 대상 회원 값을 반환한다.
 * @param {Object} member - 회원 정보
 * @param {string} field - 필터 필드
 * @returns {string} 필터 대상 값
 */
const getFilterMemberValue = (member, field) => {
  if (field === 'name') {
    return member.username || ''
  }

  if (field === 'label') {
    return Array.isArray(member.labels) ? member.labels.join(', ') : ''
  }

  if (field === 'status') {
    return member.activityStatus || '비활성'
  }

  return member[field] || ''
}

/**
 * 텍스트 필터 조건을 검사한다.
 * @param {string} source - 원본 값
 * @param {string} operator - 연산자
 * @param {string} target - 비교 값
 * @returns {boolean} 조건 충족 여부
 */
const matchesTextFilter = (source, operator, target) => {
  const sourceText = source.toLowerCase()
  const targetText = target.toLowerCase()

  if (!targetText) {
    return true
  }

  if (operator === 'is') {
    return sourceText === targetText
  }

  if (operator === 'notContains') {
    return !sourceText.includes(targetText)
  }

  if (operator === 'startsWith') {
    return sourceText.startsWith(targetText)
  }

  if (operator === 'endsWith') {
    return sourceText.endsWith(targetText)
  }

  return sourceText.includes(targetText)
}

/**
 * 날짜 필터 조건을 검사한다.
 * @param {string | null} value - ISO 날짜 문자열
 * @param {string} operator - 연산자
 * @param {string} target - yyyy-mm-dd 비교 값
 * @returns {boolean} 조건 충족 여부
 */
const matchesDateFilter = (value, operator, target) => {
  if (operator === 'empty') {
    return !value
  }

  if (operator === 'notEmpty') {
    return Boolean(value)
  }

  if (!value || !target) {
    return true
  }

  const sourceTime = new Date(value).getTime()
  const targetTime = new Date(`${target}T00:00:00`).getTime()

  if (Number.isNaN(sourceTime) || Number.isNaN(targetTime)) {
    return true
  }

  return operator === 'before'
    ? sourceTime < targetTime
    : sourceTime >= targetTime
}

/**
 * 회원이 단일 필터 조건을 만족하는지 확인한다.
 * @param {Object} member - 회원 정보
 * @param {Object} filter - 필터 조건
 * @returns {boolean} 조건 충족 여부
 */
const matchesMemberFilter = (member, filter) => {
  const fieldType = getFilterFieldType(filter.field)
  const source = String(getFilterMemberValue(member, filter.field) || '')

  if (fieldType === 'date') {
    return matchesDateFilter(source, filter.operator, filter.value)
  }

  if (fieldType === 'status') {
    return filter.operator === 'isNot'
      ? source !== filter.value
      : source === filter.value
  }

  return matchesTextFilter(source, filter.operator, filter.value)
}

/**
 * 필터 조건 필드를 변경한다.
 * @param {Object} filter - 필터 조건
 * @returns {void}
 */
const changeFilterField = (filter) => {
  const fieldType = getFilterFieldType(filter.field)
  filter.operator = getFilterOperatorOptions(filter.field)[0].value
  filter.value = fieldType === 'status' ? '활성' : ''
}

/**
 * 새 필터 조건을 추가한다.
 * @returns {void}
 */
const addFilter = () => {
  filterDrafts.value.push({
    id: nextFilterId,
    field: 'email',
    operator: 'contains',
    value: ''
  })
  nextFilterId += 1
}

/**
 * 필터 조건을 삭제한다.
 * @param {number} id - 필터 ID
 * @returns {void}
 */
const removeFilter = (id) => {
  filterDrafts.value = filterDrafts.value.filter((filter) => filter.id !== id)

  if (!filterDrafts.value.length) {
    addFilter()
  }
}

/**
 * 필터 조건을 적용한다.
 * @returns {void}
 */
const applyFilters = () => {
  activeFilters.value = filterDrafts.value
    .filter((filter) => ['empty', 'notEmpty'].includes(filter.operator) || String(filter.value || '').trim())
    .map((filter) => ({ ...filter }))
  isFilterOpen.value = false
}

/**
 * 모든 필터 조건을 초기화한다.
 * @returns {void}
 */
const resetFilters = () => {
  filterDrafts.value = [
    {
      id: nextFilterId,
      field: 'email',
      operator: 'contains',
      value: ''
    }
  ]
  nextFilterId += 1
  activeFilters.value = []
}

const filteredMembers = computed(() => {
  const query = memberSearchQuery.value.trim().toLowerCase()
  const searchedMembers = !query
    ? members.value
    : members.value.filter((member) => [
      member.username,
      member.email,
      member.lastSeenIp,
      member.activityStatus
    ].some((value) => String(value || '').toLowerCase().includes(query)))

  if (!activeFilters.value.length) {
    return searchedMembers
  }

  return searchedMembers.filter((member) => activeFilters.value.every((filter) => matchesMemberFilter(member, filter)))
})

const memberCountLabel = computed(() => {
  const count = filteredMembers.value.length
  return `${count}명`
})

const activeFilterCount = computed(() => activeFilters.value.length)

/**
 * 회원 상세 화면으로 이동한다.
 * @param {Object} member - 회원 정보
 * @returns {Promise<void>} 이동 처리
 */
const navigateToMember = async (member) => {
  if (!member?.id) {
    return
  }

  await navigateTo(`/admin/members/${member.id}`)
}

/**
 * 회원 이니셜을 반환한다.
 * @param {Object} member - 회원 정보
 * @returns {string} 이니셜
 */
const getMemberInitial = (member) => String(member?.username || member?.email || '?').slice(0, 1).toUpperCase()

/**
 * 날짜 표시 형식 변환
 * @param {string | null} value - ISO 날짜 문자열
 * @returns {string} 화면 표시 날짜
 */
const formatDate = (value) => {
  if (!value) {
    return '-'
  }

  const date = new Date(value)
  if (Number.isNaN(date.getTime())) {
    return '-'
  }

  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')

  return `${year}.${month}.${day}`
}

/**
 * 최근 활동 시각을 상대 시간으로 표시한다.
 * @param {string | null} value - ISO 시각
 * @returns {string} 상대 시간
 */
const formatRelativeTime = (value) => {
  if (!value) {
    return '최근 활동 없음'
  }

  const date = new Date(value)
  if (Number.isNaN(date.getTime())) {
    return '최근 활동 없음'
  }

  const diffMs = Date.now() - date.getTime()
  const minute = 1000 * 60
  const hour = minute * 60
  const day = hour * 24

  if (diffMs < minute) {
    return '방금 전'
  }

  if (diffMs < hour) {
    return `${Math.floor(diffMs / minute)}분 전`
  }

  if (diffMs < day) {
    return `${Math.floor(diffMs / hour)}시간 전`
  }

  if (diffMs < day * 30) {
    return `${Math.floor(diffMs / day)}일 전`
  }

  return formatDate(value)
}
</script>

<template>
  <section class="admin-members bg-paper p-6">
    <div class="admin-members__header flex flex-col gap-5 pb-6 xl:flex-row xl:items-center xl:justify-between">
      <div>
        <p class="admin-members__eyebrow text-xs font-semibold uppercase text-muted">
          Members
        </p>
        <h1 class="admin-members__title mt-2 text-3xl font-semibold tracking-[-0.01em] text-[#15171a]">
          멤버
        </h1>
      </div>
      <div class="admin-members__actions flex flex-col gap-3 sm:flex-row sm:items-center">
        <label class="admin-members__search relative block w-full sm:w-[290px]">
          <span class="sr-only">멤버 검색</span>
          <svg class="admin-members__search-icon pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[#9aa4b2]" viewBox="0 0 24 24" aria-hidden="true">
            <path d="M23.245 23.996a.743.743 0 01-.53-.22L16.2 17.26a9.824 9.824 0 01-2.553 1.579 9.766 9.766 0 01-7.51.069 9.745 9.745 0 01-5.359-5.262c-1.025-2.412-1.05-5.08-.069-7.51S3.558 1.802 5.97.777a9.744 9.744 0 017.51-.069 9.745 9.745 0 015.359 5.262 9.748 9.748 0 01.069 7.51 9.807 9.807 0 01-1.649 2.718l6.517 6.518a.75.75 0 01-.531 1.28zM9.807 1.49a8.259 8.259 0 00-3.25.667 8.26 8.26 0 00-4.458 4.54 8.26 8.26 0 00.058 6.362 8.26 8.26 0 004.54 4.458 8.259 8.259 0 006.362-.059 8.285 8.285 0 002.594-1.736.365.365 0 01.077-.076 8.245 8.245 0 001.786-2.728 8.255 8.255 0 00-.059-6.362 8.257 8.257 0 00-4.54-4.458 8.28 8.28 0 00-3.11-.608z" fill="currentColor" />
          </svg>
          <input
            v-model="memberSearchQuery"
            class="admin-members__search-input h-11 w-full rounded-md border border-transparent bg-[#eef1f4] pl-10 pr-3 text-sm text-[#15171a] outline-none transition focus:border-[#c5ccd5] focus:bg-white focus:ring-2 focus:ring-[#dce2e8]"
            type="search"
            placeholder="멤버 검색..."
            aria-label="멤버 검색"
          >
        </label>
        <button
          class="admin-members__filter inline-flex h-11 items-center justify-center gap-2 rounded-md border border-line bg-white px-4 text-sm font-semibold text-[#394047] transition hover:bg-[#eff1f2] hover:text-black"
          type="button"
          :aria-expanded="isFilterOpen"
          @click="isFilterOpen = !isFilterOpen"
        >
          <svg class="h-4 w-4" viewBox="0 0 18 18" fill="none" aria-hidden="true">
            <path d="M6.5 13.502H12M4 9.004L14 9M1.688 4.502h14.624" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
          </svg>
          <span>필터</span>
          <span v-if="activeFilterCount" class="admin-members__filter-count rounded-full bg-[#15171a] px-1.5 py-0.5 text-[11px] leading-none text-white">
            {{ activeFilterCount }}
          </span>
        </button>
        <NuxtLink
          class="admin-members__new inline-flex h-11 items-center rounded-md bg-[#15171a] px-4 text-sm font-semibold text-white transition hover:bg-[#2b2f35] focus-visible:outline focus-visible:ring-2 focus-visible:ring-[#8e9cac] focus-visible:ring-offset-2"
          to="/admin/members/new"
        >
          멤버 추가
        </NuxtLink>
      </div>
    </div>

    <section v-if="isFilterOpen" class="admin-members__filter-panel mt-2 rounded-lg border border-line bg-white p-6 shadow-xl">
      <h2 class="admin-members__filter-title text-2xl font-semibold tracking-[-0.01em] text-[#15171a]">
        필터 목록
      </h2>
      <div class="admin-members__filter-body mt-6 rounded-md bg-[#eef1f4] p-5">
        <div class="admin-members__filter-rules grid gap-3">
          <div
            v-for="filter in filterDrafts"
            :key="filter.id"
            class="admin-members__filter-row grid gap-2 lg:grid-cols-[minmax(0,1fr)_220px_minmax(0,1fr)_36px]"
          >
            <label class="admin-members__filter-field">
              <span class="sr-only">필터 항목</span>
              <select
                v-model="filter.field"
                class="admin-members__filter-select h-11 w-full rounded-md border border-[#d7dce0] bg-white px-3 text-sm text-[#15171a] outline-none focus:border-[#8e9cac] focus:ring-2 focus:ring-[#dce2e8]"
                @change="changeFilterField(filter)"
              >
                <option v-for="option in filterFieldOptions" :key="option.value" :value="option.value">
                  {{ option.label }}
                </option>
              </select>
            </label>
            <label class="admin-members__filter-operator">
              <span class="sr-only">필터 조건</span>
              <select
                v-model="filter.operator"
                class="admin-members__filter-select h-11 w-full rounded-md border border-[#d7dce0] bg-white px-3 text-sm text-[#15171a] outline-none focus:border-[#8e9cac] focus:ring-2 focus:ring-[#dce2e8]"
              >
                <option v-for="option in getFilterOperatorOptions(filter.field)" :key="option.value" :value="option.value">
                  {{ option.label }}
                </option>
              </select>
            </label>
            <label class="admin-members__filter-value">
              <span class="sr-only">필터 값</span>
              <select
                v-if="getFilterFieldType(filter.field) === 'status'"
                v-model="filter.value"
                class="admin-members__filter-select h-11 w-full rounded-md border border-[#d7dce0] bg-white px-3 text-sm text-[#15171a] outline-none focus:border-[#8e9cac] focus:ring-2 focus:ring-[#dce2e8]"
              >
                <option v-for="option in statusFilterOptions" :key="option.value" :value="option.value">
                  {{ option.label }}
                </option>
              </select>
              <input
                v-else-if="getFilterFieldType(filter.field) === 'date' && !['empty', 'notEmpty'].includes(filter.operator)"
                v-model="filter.value"
                class="admin-members__filter-input h-11 w-full rounded-md border border-[#d7dce0] bg-white px-3 text-sm text-[#15171a] outline-none focus:border-[#8e9cac] focus:ring-2 focus:ring-[#dce2e8]"
                type="date"
              >
              <input
                v-else-if="getFilterFieldType(filter.field) !== 'date'"
                v-model="filter.value"
                class="admin-members__filter-input h-11 w-full rounded-md border border-[#d7dce0] bg-white px-3 text-sm text-[#15171a] outline-none focus:border-[#8e9cac] focus:ring-2 focus:ring-[#dce2e8]"
                type="text"
                placeholder="값 입력"
              >
              <span v-else class="admin-members__filter-empty-value flex h-11 items-center rounded-md border border-[#d7dce0] bg-white px-3 text-sm text-[#8a95a5]">
                값 필요 없음
              </span>
            </label>
            <button
              class="admin-members__filter-delete grid h-11 w-9 place-items-center rounded-md text-[#657080] transition hover:bg-white hover:text-[#15171a]"
              type="button"
              aria-label="필터 삭제"
              @click="removeFilter(filter.id)"
            >
              <svg class="h-4 w-4" viewBox="0 0 24 24" aria-hidden="true">
                <path d="M12.707 12L23.854.854a.5.5 0 00-.707-.707L12 11.293.854.146a.5.5 0 00-.707.707L11.293 12 .146 23.146a.5.5 0 00.708.708L12 12.707l11.146 11.146a.5.5 0 10.708-.706L12.707 12z" fill="currentColor" />
              </svg>
            </button>
          </div>
        </div>
        <button
          class="admin-members__filter-add mt-5 inline-flex items-center gap-2 text-sm font-semibold text-[#17a62b]"
          type="button"
          @click="addFilter"
        >
          <svg class="h-3.5 w-3.5" viewBox="0 0 16 16" fill="none" aria-hidden="true">
            <path d="M8 1v14M1 8h14" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
          </svg>
          <span>필터 추가</span>
        </button>
      </div>
      <div class="admin-members__filter-footer mt-5 flex items-center justify-between">
        <button class="admin-members__filter-reset h-10 rounded-md border border-line bg-white px-4 text-sm font-semibold text-[#394047] transition hover:bg-[#eff1f2]" type="button" @click="resetFilters">
          모두 초기화
        </button>
        <button class="admin-members__filter-apply h-10 rounded-md bg-[#15171a] px-4 text-sm font-semibold text-white transition hover:bg-black" type="button" @click="applyFilters">
          필터 적용
        </button>
      </div>
    </section>

    <div class="admin-members__table mt-7 overflow-x-auto">
      <table class="admin-members__table-inner min-w-[920px] w-full border-collapse text-left text-sm">
        <thead class="admin-members__table-head border-b border-line text-xs uppercase tracking-[0.02em] text-[#15171a]">
          <tr>
            <th class="admin-members__cell w-[32%] py-4 pr-5 font-semibold">{{ memberCountLabel }}</th>
            <th class="admin-members__cell w-[15%] px-5 py-4 font-semibold">등급/상태</th>
            <th class="admin-members__cell w-[16%] px-5 py-4 font-semibold">댓글 작성</th>
            <th class="admin-members__cell w-[17%] px-5 py-4 font-semibold">접속 IP</th>
            <th class="admin-members__cell w-[20%] py-4 pl-5 font-semibold">가입일</th>
          </tr>
        </thead>
        <tbody class="admin-members__table-body divide-y divide-line/70 bg-paper">
          <tr
            v-for="member in filteredMembers"
            :key="member.id"
            class="admin-members__row cursor-pointer align-middle transition-colors hover:bg-[#f7f8fa] focus-within:bg-[#f7f8fa]"
            tabindex="0"
            @click="navigateToMember(member)"
            @keydown.enter.prevent="navigateToMember(member)"
          >
            <td class="admin-members__cell py-5 pr-5">
              <div class="admin-members__profile flex min-w-0 items-center gap-3">
                <img
                  v-if="member.avatarUrl"
                  :src="member.avatarUrl"
                  :alt="member.username"
                  class="admin-members__avatar h-11 w-11 shrink-0 rounded-full object-cover"
                >
                <span v-else class="admin-members__avatar grid h-11 w-11 shrink-0 place-items-center rounded-full bg-[#15171a] text-sm font-semibold text-white">
                  {{ getMemberInitial(member) }}
                </span>
                <span class="admin-members__identity min-w-0">
                  <span class="admin-members__name block truncate text-base font-semibold text-[#15171a]">
                    {{ member.username || '이름 없음' }}
                  </span>
                  <span class="admin-members__email mt-0.5 block truncate text-sm text-[#657080]">
                    {{ member.email }}
                  </span>
                </span>
              </div>
            </td>
            <td class="admin-members__cell px-5 py-5 text-[#2f343b]">
              <span class="admin-members__role block text-sm font-semibold text-[#15171a]">
                {{ member.role || '멤버' }}
              </span>
              <span
                v-if="member.activityStatus !== '활성'"
                class="admin-members__status mt-1 block text-xs font-medium text-red-300"
              >
                {{ member.activityStatus }}
              </span>
            </td>
            <td class="admin-members__cell px-5 py-5 text-[#2f343b]">
              <span class="admin-members__comments font-semibold text-[#15171a]">{{ member.commentCount }}</span>
              <span class="admin-members__comments-label ml-1 text-xs text-[#8c96a3]">개</span>
            </td>
            <td class="admin-members__cell px-5 py-5 text-sm text-[#657080]">
              {{ member.lastSeenIp || '-' }}
            </td>
            <td class="admin-members__cell py-5 pl-5">
              <span class="admin-members__created block text-sm text-[#15171a]">{{ formatDate(member.createdAt) }}</span>
              <span class="admin-members__last-seen mt-1 block text-sm text-[#9aa4b2]">{{ formatRelativeTime(member.lastSeenAt) }}</span>
            </td>
          </tr>
          <tr v-if="filteredMembers.length === 0">
            <td colspan="5" class="admin-members__empty px-4 py-12 text-center text-sm text-muted">
              검색 결과가 없습니다.
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </section>
</template>
