<script setup>
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
import { useNamespace } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import { useLocale } from "@yh-ui/hooks";
import { filterBarProps, filterBarEmits } from "./filter-bar";
defineOptions({ name: "YhFilterBar" });
const props = defineProps(filterBarProps);
const emit = defineEmits(filterBarEmits);
const ns = useNamespace("filter-bar");
const { themeStyle } = useComponentTheme(
  "filter-bar",
  computed(() => props.themeOverrides)
);
const { t } = useLocale();
const innerSort = ref({ ...props.sort });
watch(
  () => props.sort,
  (v) => {
    innerSort.value = { ...v };
  },
  { deep: true }
);
function handleSortClick(key) {
  let order;
  if (innerSort.value.key !== key) {
    order = "desc";
  } else if (innerSort.value.order === "desc") {
    order = "asc";
  } else if (innerSort.value.order === "asc") {
    order = null;
  } else {
    order = "desc";
  }
  const newSort = { key: order === null ? null : key, order };
  innerSort.value = newSort;
  emit("update:sort", newSort);
  emit("sortChange", newSort);
}
const isPanelOpen = ref(false);
const panelFilter = ref(null);
const tempPanelValue = ref({});
const innerFilterValue = ref({ ...props.filterValue });
watch(
  () => props.filterValue,
  (v) => {
    innerFilterValue.value = { ...v };
  },
  { deep: true }
);
function openFilterPanel(item) {
  panelFilter.value = item;
  tempPanelValue.value = { ...innerFilterValue.value };
  isPanelOpen.value = true;
}
function closePanel() {
  isPanelOpen.value = false;
  panelFilter.value = null;
}
function confirmPanel() {
  innerFilterValue.value = { ...tempPanelValue.value };
  emit("update:filterValue", { ...innerFilterValue.value });
  emit("filterChange", { ...innerFilterValue.value });
  emit("confirm", { ...innerFilterValue.value });
  closePanel();
}
function resetPanel() {
  if (panelFilter.value) {
    const key = panelFilter.value.key;
    tempPanelValue.value = { ...tempPanelValue.value, [key]: null };
    emit("resetPanel", panelFilter.value, tempPanelValue.value);
    confirmPanel();
  }
}
function resetAll() {
  innerFilterValue.value = {};
  innerSort.value = { key: null, order: null };
  emit("update:filterValue", {});
  emit("update:sort", { key: null, order: null });
  emit("reset");
}
function toggleOption(item, option) {
  const key = item.key;
  const currentVal = tempPanelValue.value[key];
  if (item.type === "checkbox") {
    const arr = Array.isArray(currentVal) ? [...currentVal] : [];
    const idx = arr.indexOf(option.value);
    if (idx >= 0) arr.splice(idx, 1);
    else arr.push(option.value);
    tempPanelValue.value = { ...tempPanelValue.value, [key]: arr.length ? arr : null };
  } else {
    const newVal = currentVal === option.value ? null : option.value;
    tempPanelValue.value = { ...tempPanelValue.value, [key]: newVal };
    if (!props.filterInPanel) {
      innerFilterValue.value = { ...tempPanelValue.value };
      emit("update:filterValue", { ...innerFilterValue.value });
      emit("filterChange", { ...innerFilterValue.value });
    }
  }
}
function isOptionSelected(item, option) {
  const val = props.filterInPanel ? tempPanelValue.value[item.key] : innerFilterValue.value[item.key];
  if (item.type === "checkbox") {
    return Array.isArray(val) && val.includes(option.value);
  }
  return val === option.value;
}
const activeFilterCount = computed(() => {
  return Object.values(innerFilterValue.value).filter((v) => {
    if (v === null || v === void 0) return false;
    if (Array.isArray(v)) return v.length > 0;
    return true;
  }).length;
});
const barRef = ref(null);
const isStuck = ref(false);
let observer = null;
onMounted(() => {
  if (!props.sticky || typeof IntersectionObserver === "undefined") return;
  observer = new IntersectionObserver(
    ([entry]) => {
      isStuck.value = !entry.isIntersecting;
    },
    { rootMargin: `-${props.stickyOffset}px 0px 0px 0px`, threshold: 1 }
  );
  if (barRef.value) observer.observe(barRef.value);
});
onUnmounted(() => {
  observer?.disconnect();
});
function handleViewToggle() {
  const next = props.viewType === "list" ? "grid" : "list";
  emit("update:viewType", next);
  emit("viewChange", next);
}
</script>

<template>
  <div
    ref="barRef"
    :class="[ns.b(), ns.is('sticky', sticky), ns.is('stuck', isStuck)]"
    :style="[themeStyle, sticky ? {
  top: `${stickyOffset}px`
} : {}]"
  >
    <!-- 筛选/排序 tab 行 -->
    <div :class="ns.e('bar')">
      <!-- 全部 tab -->
      <button
        v-if="showAll"
        :class="[ns.e('tab'), ns.is('active', !innerSort.key && !activeFilterCount)]"
        @click="resetAll"
      >
        {{ t('filterbar.all') }}
      </button>

      <!-- 排序 tabs -->
      <button
        v-for="sortItem in sorts"
        :key="sortItem.key"
        :class="[ns.e('tab'), ns.e('sort-tab'), ns.is('active', innerSort.key === sortItem.key)]"
        @click="handleSortClick(sortItem.key)"
      >
        <span>{{ sortItem.label }}</span>
        <span :class="[ns.e('sort-arrows')]">
          <i
            :class="[ns.e('arrow-up'), ns.is('active', innerSort.key === sortItem.key && innerSort.order === 'asc')]"
          >
            <svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor">
              <path d="M512 320L192 704h640z" />
            </svg>
          </i>
          <i
            :class="[ns.e('arrow-down'), ns.is('active', innerSort.key === sortItem.key && innerSort.order === 'desc')]"
          >
            <svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor">
              <path d="M512 704L832 320H192z" />
            </svg>
          </i>
        </span>
      </button>

      <!-- 筛选 tabs -->
      <template v-if="filterInPanel">
        <button
          v-for="filterItem in filters"
          :key="filterItem.key"
          :class="[ns.e('tab'), ns.e('filter-tab'), ns.is('active', !!innerFilterValue[filterItem.key] && (Array.isArray(innerFilterValue[filterItem.key]) ? innerFilterValue[filterItem.key].length > 0 : true))]"
          @click="openFilterPanel(filterItem)"
        >
          <span>{{ filterItem.label }}</span>
          <i :class="ns.e('arrow-icon')">
            <svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor">
              <path d="M512 704L832 320H192z" />
            </svg>
          </i>
        </button>
        <!-- 综合筛选按钮 -->
        <button
          v-if="showGlobalFilter"
          :class="[ns.e('tab'), ns.e('filter-btn'), ns.is('active', activeFilterCount > 0)]"
          @click="emit('openFilter')"
        >
          <span>{{ t("filterbar.filter") }}</span>
          <span v-if="activeFilterCount" :class="ns.e('badge')">{{ activeFilterCount }}</span>
          <slot name="filter-icon">
            <i :class="ns.e('filter-icon')">
              <svg viewBox="0 0 1024 1024" width="1em" height="1em" fill="currentColor">
                <path
                  d="M880.005 224H143.995c-23.778 0-38.653 25.492-22.95 45.418L384 602.822v263.854c0 15.365 17.554 24.088 29.818 14.89l185.394-139.046c6.242-4.682 9.924-12.072 9.924-19.866V602.823l293.818-333.406c15.704-19.926.83-45.417-22.949-45.417z"
                ></path>
              </svg>
            </i>
          </slot>
        </button>
      </template>

      <!-- 内联筛选 -->
      <template v-else>
        <div v-for="filterItem in filters" :key="filterItem.key" :class="ns.e('inline-filter')">
          <span :class="ns.e('inline-label')">{{ filterItem.label }}</span>
          <button
            v-for="opt in filterItem.options"
            :key="String(opt.value)"
            :class="[ns.e('inline-opt'), ns.is('active', isOptionSelected(filterItem, opt))]"
            @click="toggleOption(filterItem, opt)"
          >
            {{ opt.label }}
          </button>
        </div>
      </template>

      <!-- 视图切换按钮 -->
      <button
        v-if="showViewToggle"
        :class="[ns.e('tab'), ns.e('view-btn')]"
        @click="handleViewToggle"
      >
        <slot name="view-icon" :view-type="viewType">
          <svg
            v-if="viewType === 'list'"
            viewBox="0 0 1024 1024"
            width="1.2em"
            height="1.2em"
            fill="currentColor"
          >
            <path d="M128 320h768v85.333H128V320zM128 618.667h768V704H128v-85.333z" />
          </svg>
          <svg v-else viewBox="0 0 1024 1024" width="1.2em" height="1.2em" fill="currentColor">
            <path
              d="M213.333 213.333h256v256h-256v-256zM554.667 213.333h256v256h-256v-256zM213.333 554.667h256v256h-256v-256zM554.667 554.667h256v256h-256v-256z"
            />
          </svg>
        </slot>
      </button>

      <!-- 自定义扩展区 -->
      <slot name="extra" />
    </div>

    <!-- 筛选面板（下拉浮层） -->
    <Teleport to="body" :disabled="!teleported">
      <Transition :name="ns.b('panel')">
        <div
          v-if="isPanelOpen && filterInPanel && panelFilter"
          :class="ns.e('panel')"
          role="dialog"
          :aria-label="t('filterbar.filter')"
          :style="themeStyle"
        >
          <!-- 面板遮罩 -->
          <div :class="ns.e('overlay')" @click="closePanel" />
          <div :class="ns.e('panel-body')">
            <!-- 标题 -->
            <div :class="ns.e('panel-header')">
              <span :class="ns.e('panel-title')">{{ panelFilter.label }}</span>
              <button
                :class="ns.e('panel-close')"
                :aria-label="t('filterbar.cancel')"
                @click="closePanel"
              >
                <svg viewBox="0 0 1024 1024" width="14" height="14" fill="currentColor">
                  <path
                    d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9c-4.4 5.2-.7 13.1 6.1 13.1h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
                  />
                </svg>
              </button>
            </div>
            <!-- 内容 -->
            <div :class="ns.e('panel-content')">
              <slot
                name="panel-content"
                :filter="panelFilter"
                :value="tempPanelValue"
                :toggle="toggleOption"
              >
                <div v-if="panelFilter.options?.length" :class="ns.e('panel-options')">
                  <button
                    v-for="opt in panelFilter.options"
                    :key="String(opt.value)"
                    :class="[ns.e('panel-opt'), ns.is('active', isOptionSelected(panelFilter, opt))]"
                    @click="toggleOption(panelFilter, opt)"
                  >
                    {{ opt.label }}
                  </button>
                </div>
                <p v-else :class="ns.e('no-options')">
                  {{ t("filterbar.noOptions") }}
                </p>
              </slot>
            </div>
            <!-- 操作区 -->
            <div :class="ns.e('panel-footer')">
              <button :class="[ns.e('btn'), ns.e('btn-reset')]" @click="resetPanel">
                {{ t("filterbar.reset") }}
              </button>
              <button :class="[ns.e('btn'), ns.e('btn-confirm')]" @click="confirmPanel">
                {{ t("filterbar.confirm") }}
              </button>
            </div>
          </div>
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-filter-bar {
  --yh-filter-bar-bg: var(--yh-bg-color);
  --yh-filter-bar-border: var(--yh-border-color-lighter);
  --yh-filter-bar-height: 44px;
  --yh-filter-bar-tab-color: var(--yh-text-color-regular);
  --yh-filter-bar-tab-active-color: var(--yh-color-primary);
  --yh-filter-bar-tab-active-bg: var(--yh-color-primary-light-9);
  --yh-filter-bar-tab-radius: var(--yh-radius-base);
  --yh-filter-bar-badge-bg: var(--yh-color-danger);
  --yh-filter-bar-panel-bg: var(--yh-bg-color);
  --yh-filter-bar-panel-shadow: 0 4px 20px var(--yh-shadow-color);
  --yh-filter-bar-panel-opt-bg: var(--yh-fill-color-light);
  --yh-filter-bar-panel-opt-active-bg: var(--yh-color-primary-light-9);
  --yh-filter-bar-panel-opt-active-color: var(--yh-color-primary);
  --yh-filter-bar-panel-opt-active-border: var(--yh-color-primary);
  --yh-filter-bar-z-index: 100;
  position: relative;
  background: var(--yh-filter-bar-bg);
  border-bottom: 1px solid var(--yh-filter-bar-border);
  z-index: var(--yh-filter-bar-z-index);
  transition: box-shadow var(--yh-transition-base);
}
.yh-filter-bar.is-sticky {
  position: sticky;
  top: 0;
}

.yh-filter-bar.is-stuck {
  box-shadow: 0 2px 12px var(--yh-shadow-color);
}

.yh-filter-bar__bar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 12px;
  height: var(--yh-filter-bar-height);
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.yh-filter-bar__bar::-webkit-scrollbar {
  display: none;
}

.yh-filter-bar__tab {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 5px 12px;
  border: none;
  background: transparent;
  color: var(--yh-filter-bar-tab-color);
  font-size: var(--yh-font-size-sm);
  font-weight: var(--yh-font-weight-medium);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: color var(--yh-transition-base), background var(--yh-transition-base);
}
.yh-filter-bar__tab:hover {
  color: var(--yh-filter-bar-tab-active-color);
}
.yh-filter-bar__tab:active {
  transform: scale(0.96);
}
.yh-filter-bar__tab.is-active {
  color: var(--yh-filter-bar-tab-active-color);
  background: transparent;
  font-weight: 600;
}

.yh-filter-bar__view-btn {
  margin-left: auto;
  padding: 0 4px; /* Slightly tighter padding for the icon */
  font-size: 16px; /* Icon size */
  color: var(--yh-text-color-secondary);
}
.yh-filter-bar__view-btn:hover {
  color: var(--yh-filter-bar-tab-active-color);
}

.yh-filter-bar__sort-arrows {
  display: inline-flex;
  flex-direction: column;
  gap: 1px;
  font-size: 10px;
  line-height: 1;
  margin-left: 4px;
  color: var(--yh-text-color-placeholder);
}

.yh-filter-bar__arrow-up {
  display: inline-flex;
  transition: color var(--yh-transition-base);
}
.yh-filter-bar__arrow-up.is-active {
  color: var(--yh-filter-bar-tab-active-color);
}

.yh-filter-bar__arrow-down {
  display: inline-flex;
  transition: color var(--yh-transition-base);
}
.yh-filter-bar__arrow-down.is-active {
  color: var(--yh-filter-bar-tab-active-color);
}

.yh-filter-bar__arrow-icon {
  font-style: normal;
  display: inline-flex;
  font-size: 10px;
  margin-left: 2px;
  color: var(--yh-text-color-placeholder);
  transition: transform var(--yh-transition-base);
}

.yh-filter-bar__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--yh-filter-bar-badge-bg);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
}

.yh-filter-bar__filter-icon {
  font-style: normal;
  display: inline-flex;
  align-items: center;
  font-size: 14px;
}

.yh-filter-bar__inline-filter {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.yh-filter-bar__inline-label {
  font-size: var(--yh-font-size-xs);
  color: var(--yh-text-color-secondary);
  white-space: nowrap;
}

.yh-filter-bar__inline-opt {
  padding: 4px 14px;
  border: none;
  border-radius: 999px;
  background: var(--yh-fill-color-light);
  font-size: var(--yh-font-size-xs);
  font-weight: 500;
  color: var(--yh-text-color-regular);
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.3s ease, background 0.3s ease, transform 0.2s cubic-bezier(0.3, 1.5, 0.4, 1.5);
}
.yh-filter-bar__inline-opt:active {
  transform: scale(0.95);
}
.yh-filter-bar__inline-opt.is-active {
  color: var(--yh-filter-bar-tab-active-color);
  background: var(--yh-filter-bar-panel-opt-active-bg);
  font-weight: 600;
}

.yh-filter-bar__panel {
  position: fixed;
  inset: 0;
  z-index: calc(var(--yh-filter-bar-z-index, 100) + 1);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  --yh-filter-bar-panel-bg: var(--yh-bg-color);
  --yh-filter-bar-panel-shadow: 0 4px 20px var(--yh-shadow-color);
  --yh-filter-bar-panel-opt-bg: var(--yh-fill-color-light);
  --yh-filter-bar-panel-opt-active-bg: var(--yh-color-primary-light-9);
  --yh-filter-bar-panel-opt-active-color: var(--yh-color-primary);
  --yh-filter-bar-panel-opt-active-border: var(--yh-color-primary);
}

.yh-filter-bar__overlay {
  position: absolute;
  inset: 0;
  background: var(--yh-overlay-color-light, rgba(0, 0, 0, 0.45));
}

.yh-filter-bar__panel-body {
  position: relative;
  background: var(--yh-filter-bar-panel-bg);
  border-radius: var(--yh-radius-large) var(--yh-radius-large) 0 0;
  box-shadow: var(--yh-filter-bar-panel-shadow);
  max-height: 60vh;
  display: flex;
  flex-direction: column;
}

.yh-filter-bar__panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--yh-border-color-lighter);
  flex-shrink: 0;
}

.yh-filter-bar__panel-title {
  font-size: var(--yh-font-size-md);
  font-weight: 600;
  color: var(--yh-text-color-primary);
}

.yh-filter-bar__panel-close {
  border: none;
  background: none;
  font-size: var(--yh-font-size-sm);
  color: var(--yh-text-color-secondary);
  cursor: pointer;
  padding: 0;
}

.yh-filter-bar__panel-content {
  flex: 1;
  overflow-y: auto;
  padding: 12px 16px;
}

.yh-filter-bar__panel-options {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.yh-filter-bar__panel-opt {
  padding: 6px 16px;
  border: none;
  border-radius: 999px;
  background: var(--yh-filter-bar-panel-opt-bg);
  font-size: var(--yh-font-size-sm);
  color: var(--yh-text-color-regular);
  cursor: pointer;
  transition: color 0.3s ease, transform 0.3s ease, background 0.3s ease;
}
.yh-filter-bar__panel-opt:active {
  transform: scale(0.95);
}
.yh-filter-bar__panel-opt.is-active {
  color: var(--yh-filter-bar-panel-opt-active-color);
  background: var(--yh-filter-bar-panel-opt-active-bg);
  font-weight: 600;
}

.yh-filter-bar__no-options {
  color: var(--yh-text-color-placeholder);
  text-align: center;
  font-size: var(--yh-font-size-sm);
  padding: 20px 0;
}

.yh-filter-bar__panel-footer {
  display: flex;
  gap: 12px;
  padding: 12px 16px;
  border-top: 1px solid var(--yh-border-color-lighter);
  flex-shrink: 0;
}

.yh-filter-bar__btn {
  flex: 1;
  height: 40px;
  border-radius: var(--yh-radius-base);
  font-size: var(--yh-font-size-base);
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: background var(--yh-transition-base), color var(--yh-transition-base);
}

.yh-filter-bar__btn-reset {
  background: var(--yh-fill-color-light);
  color: var(--yh-text-color-regular);
}

.yh-filter-bar__btn-confirm {
  background: var(--yh-color-primary);
  color: #fff;
}
.yh-filter-bar__btn-confirm:hover {
  background: var(--yh-color-primary-dark-2);
}

.yh-filter-bar-panel-enter-active,
.yh-filter-bar-panel-leave-active {
  transition: opacity 0.25s ease;
}
.yh-filter-bar-panel-enter-active .yh-filter-bar__panel-body,
.yh-filter-bar-panel-leave-active .yh-filter-bar__panel-body {
  transition: transform 0.25s ease;
}

.yh-filter-bar-panel-enter-from,
.yh-filter-bar-panel-leave-to {
  opacity: 0;
}
.yh-filter-bar-panel-enter-from .yh-filter-bar__panel-body,
.yh-filter-bar-panel-leave-to .yh-filter-bar__panel-body {
  transform: translateY(100%);
}
</style>
