<!-- Раздел вебкам-моделей: заголовок с переключателем разделов, строка фильтров,
сетка тумб и пагинация.

Фильтры — фасетные, как на боевом движке: набор контролов и их варианты приходят
от провайдера и пересчитываются под уже применённые условия, а состояние живёт в
query-параметрах `filter_*`. Формат провайдера приводится к общему формату фильтров
(`utils/webcams/normalize-facets.ts`), поэтому строку фильтров, модалку и чипсы рисуют
готовые фильтр-компоненты — свои для вебкамов не нужны.

Сортировки у провайдера нет: «новые» — это его тег `new` (см. `lib/scheme/sort-webcams.ts`).
Переключатель «только онлайн» пока ничего не меняет в запросе: API под него не готово,
а других моделей провайдер и не отдаёт.

Клики и показы тумб вью отдаёт событиями — статистику подключает сайт. -->
<template>
  <div class="view-webcams _page _container">
    <header class="view-webcams__header">
      <div class="view-webcams__intro">
        <h1 class="view-webcams__title _title">{{ h1 }}</h1>

        <UiSwitcher
          :items="sections"
          :model-value="'webcams'"
          class="view-webcams__sections"
        />
      </div>

      <div class="view-webcams__options">
        <div class="view-webcams__sort">
          <SectionNavLinks
            :items="sortWebcams"
            theme="secondary"
          />

          <span
            v-if="filters.length"
            class="view-webcams__divider"
          />

          <FilterBtn
            v-if="filters.length"
            v-model="showFilters"
            :count="filterCount"
            theme="primary"
          />
        </div>

        <div class="view-webcams__quick">
          <span class="view-webcams__online">
            <UiToggle v-model="onlineOnly">{{ t('webcams.online_only') }}</UiToggle>
          </span>

          <FilterSelects
            v-if="filters.length"
            :filters="filters"
          />
        </div>
      </div>

      <FilterModel
        v-if="filters.length && (showFilters || filterCount > 0)"
        v-model="showFilters"
        :data="filterData"
        :status="status"
      />

    </header>

    <div class="view-webcams__subheader">
      <h2 class="view-webcams__section-title">
        <WebcamIcon/>
        {{ t(sortTitle) }}
      </h2>

      <NuxtLink
        v-if="tag"
        :to="tagResetLink"
        class="ui-chips view-webcams__tag"
      >
        {{ tagTitle }}
        <UiIcon
          class="ui-chips__close"
          name="close"
          size="16"
        />
      </NuxtLink>
    </div>

    <GridWebcams
      v-if="items.length"
      :class="{'_loading': status === 'pending'}"
      :items="items"
      class="view-webcams__grid"
      id="anchor"
      @model-click="payload => emit('model-click', payload)"
      @model-view="payload => emit('model-view', payload)"
    />

    <div
      v-else-if="status === 'pending'"
      class="view-webcams__loading _loading"
    />
    <UiEmptyState v-else/>

    <LazyUiPagination
      v-if="allPages > 1"
      :key="`pagination-${allPages}`"
      :total="allPages"
      arrow-icon
      class="_pagination"
      hydrate-on-visible
    />
  </div>
</template>

<script setup lang="ts">
import type { IModelCard, IModelFilter, ISwitcherItem, IStripcashModel, IWebcamModelsResponse, PaginatedResponse } from '../../types';
import { countWebcamFilters } from '../../utils/webcams/count-filters';
import { normalizeWebcamFacets } from '../../utils/webcams/normalize-facets';
import { toWebcamFilterPayload } from '../../utils/webcams/to-filter-payload';
import { WEBCAM_FILTER_ANY } from '../../utils/webcams/facets';
import { sortWebcams } from '../../lib';

const props = defineProps<{
  data: IWebcamModelsResponse | null
  status: string
  h1: string
  /** Тег из пути раздела (`/webcams/{tag}`), если он есть. */
  tag?: string
  /** Сколько всего видео-моделей в нише — подпись у пункта «Видео». */
  modelsTotal?: number
}>();

/**
 * Клики и показы тумб уходят наружу: индексы знает сетка, а куда их отправлять —
 * дело сайта (у статистики свой контекст страницы и устройства).
 */
const emit = defineEmits<{
  'model-click': [payload: { model: IStripcashModel, index: number }]
  'model-view': [payload: { model: IStripcashModel, index: number, element: HTMLElement }]
}>();

const { t, te, n, locale } = useI18n();
const route = useRoute();
const router = useRouter();
const { generateLink } = useGenerateLink();

const showFilters = ref(false);

/**
 * Провайдер отдаёт только моделей в эфире, так что переключатель всегда включён:
 * он про будущий фильтр, а не про текущую выдачу.
 */
const onlineOnly = ref(true);

const items = computed(() => props.data?.items || []);
const allPages = computed(() => props.data?.allPages || 0);
const filterCount = computed(() => countWebcamFilters(route.query));

/**
 * Фасеты провайдера → фильтры слоя. Подписи фильтров переводим по имени (провайдер
 * отдаёт их только по-английски), варианты оставляем как есть — их у него сотни.
 */
const filters = computed<IModelFilter[]>(() => normalizeWebcamFacets(props.data?.filters, props.data?.total).map(filter => ({
  ...filter,
  title: translate(`webcams.filters.${filter.name}`, filter.title),
  options: filter.options.map(option => (option.name === WEBCAM_FILTER_ANY ? { ...option, title: t('all') } : option)),
  group: { ...filter.group, title: translate(`webcams.filter_groups.${filter.group.name}`, filter.group.name) },
})));

/** Модалка фильтра слоя ждёт ответ листинга — ей нужны только фасеты. */
const filterData = computed(() => ({ filters: filters.value }) as unknown as PaginatedResponse<IModelCard>);

const sortTitle = computed(() => {
  const current = String(route.query.sort || '');

  return sortWebcams.find(item => item.value === current)?.title || sortWebcams[0].title;
});

/** Теги провайдера приходят слагами (`cheap-privates`) — показываем словами. */
const tagTitle = computed(() => String(props.tag || '').replace(/[-_]+/g, ' '));

/**
 * Сброс тега — обычная ссылка на корень раздела: остальные фильтры сохраняются
 * (тег такой же фильтр, только живёт в пути). Путь считаем от текущего, чтобы не
 * собирать заново нишу и локаль.
 */
const tagResetLink = computed(() => {
  const query = { ...route.query };
  delete query.page;

  return { path: route.path.replace(/\/[^/]+$/, ''), query };
});

/**
 * Переключатель разделов: видео-модели ↔ вебкам-модели, у каждого свой счётчик
 * (у вебкамов — сколько сейчас в эфире).
 */
const sections = computed<ISwitcherItem[]>(() => [
  {
    name: 'videos',
    title: t('webcams.sections.videos'),
    icon: 'user-group',
    subtitle: props.modelsTotal ? n(props.modelsTotal) : '',
    to: generateLink('/models'),
  },
  {
    name: 'webcams',
    title: t('webcams.sections.webcams'),
    icon: 'webcam',
    subtitle: props.data?.total ? t('webcams.online_short', { count: n(props.data.total) }) : '',
  },
]);

/**
 * Вход в раздел: если интерфейс не английский, сразу показываем моделей, которые
 * говорят на языке пользователя. Только при первом заходе за сессию — иначе
 * снятый пользователем фильтр возвращался бы сам, — и только если такой язык у
 * провайдера в выдаче есть (иначе получили бы пустую страницу).
 */
const isLanguageFilterApplied = useState('webcams-language-filter', () => false);

onMounted(() => {
  if (isLanguageFilterApplied.value) return;
  isLanguageFilterApplied.value = true;

  const code = locale.value.slice(0, 2).toLowerCase();
  if (code === 'en' || filterCount.value > 0) return;

  const languageFilter = filters.value.find(item => item.name === 'language');
  if (!languageFilter?.options.some(option => option.name === code)) return;

  router.replace({ query: { ...route.query, filter_language: code } });
});

function translate(key: string, fallback: string) {
  return te(key) ? t(key) : fallback;
}

/**
 * Смена фильтра сбрасывает пагинацию: на второй странице новой выборки может не
 * быть вовсе, и пользователь упирался бы в пустую выдачу (правило боевого движка).
 */
watch(() => JSON.stringify(toWebcamFilterPayload(route.query)), () => {
  if (!route.query.page) return;

  const query = { ...route.query };
  delete query.page;
  router.replace({ query });
});
</script>

<style lang="scss">
.view-webcams__header {
  display: grid;
  row-gap: 32px;
  margin-bottom: 32px;
}

.view-webcams__intro {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 20px;
  row-gap: 16px;
}

.view-webcams__title {
  margin-bottom: 0;
}

.view-webcams__options {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.view-webcams__sort,
.view-webcams__quick {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

// на узких экранах контролы строки компактнее (в макете `px-3` вместо `px-4`)
.view-webcams__sort .ui-btn {
  @include to-br(xm) {
    padding-inline: 12px;
  }
}

// в макете быстрые фильтры видны и на мобиле, а слой прячет их до `sm`
.view-webcams__quick > .filter-selects > ._from-sm {
  @include to-br(sm) {
    display: block !important;
  }
}

.view-webcams__divider {
  width: 1px;
  height: 24px;
  margin-inline: 4px;
  background-color: $page-webcams-divider;
}

.view-webcams__online {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  color: $page-webcams-control-text;
  background-color: $page-webcams-control-bg;
  border: 1px solid $page-webcams-control-border;
  border-radius: $radius-lg;
}

.view-webcams__online .ui-toggle__text {
  @include font-sm;

  padding-left: 46px;
}

// строка фильтров в макете тише кнопок сортировки: подложка и рамка на тон слабее,
// подпись без жирности, высота — как у остальных контролов строки
.view-webcams .filter-selects .filter-selects__select-button {
  padding: 8px 12px;
  font-weight: 400;
}

.view-webcams .filter-selects .filter-selects__select-button:not(.--active-select) {
  background-color: $page-webcams-control-bg;
  border-color: $page-webcams-control-border;
}


// заголовок выдачи — только на десктопе: в мобильном макете его нет
.view-webcams__subheader {
  display: none;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;

  @include from-br(xm) {
    display: flex;
  }
}

.view-webcams__section-title {
  @include font-lg;

  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 0;
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
}

.view-webcams__header .filter-model {
  margin-top: 0;
}

.grid-webcams.view-webcams__grid {
  // ресет обнуляет отступы у `ul[class]`, одиночный класс его не перебивает
  margin-bottom: 40px;
}
</style>
