<!-- Карточка видео: постер, логотип канала, превью при наведении, теги моделей -->
<template>
  <div
    :class="[
      {'--mini': mini},
      {'--compact': compact},
      {'--avatar': card.channelAvatar},
      {'--no-channel': isChannelPage},
      {'--progress': hasProgress},
      {'--favorite': isFavoriteCard},
    ]"
    class="card-video"
  >
    <NuxtLink
      :to="link"
      class="card-video__poster-wrapper"
      @mouseenter.passive="startPreviewByHover(previewId)"
      @mouseleave.passive="stopPreviewByHover(previewId)"
      @touchstart.passive="startPreviewByTouch(previewId)"
    >
      <UiImg
        :src="currentPoster"
        :urls="card.thumbUrls"
        :loading="loading"
        :priority="priority"
        :sizes="`(max-width: ${CSS_BREAKPOINTS.lg}px) 180px, 480px`"
        :alt="card.title"
        class="card-video__poster"
        width="480"
        height="270"
        @error="onPosterError"
      />
      <UiVideoPreview
        v-if="isPreviewShow && card.previewUrl"
        :url="card.previewUrl"
      />
      <UiLabel
        v-if="card.isNew"
        :text="t('new')"
        class="card-video__new"
        theme="primary-gradient"
      />
      <UiLabel
        v-if="card.duration"
        :text="duration"
        class="card-video__duration"
        theme="black"
      />
      <span
        v-if="hasProgress"
        :style="{ '--card-video-progress': `${progress}%` }"
        :aria-label="t('watched_percent', { percent: progress })"
        :aria-valuenow="progress"
        class="card-video__progress"
        role="progressbar"
        aria-valuemin="0"
        aria-valuemax="100"
      />
    </NuxtLink>

    <CardVideoActions
      v-if="showActions"
      :card="card"
      :show-favorites="canFavorite"
      :show-watch-later="canWatchLater"
    />

    <div class="card-video__wrapper">
      <component
        v-if="!mini"
        :is="component"
        :to="channelLink"
        class="card-video__logo-wrapper"
      >
        <UiImg
          v-if="card.channelAvatar"
          :loading="loading"
          :src="currentAvatar"
          :alt="card.channelName"
          class="card-video__channel-logo"
          width="32"
          height="32"
          sizes="32px"
          @error="onAvatarError"
        />
        <span
          v-else
          class="card-video__user"
        >U</span>
      </component>
      <NuxtLink
        :to="link"
        :title="card.title"
        class="card-video__title _truncate"
      >{{ card.title }}
      </NuxtLink>
      <component
        v-if="!mini && !compact && !history && card.channelName"
        :is="component"
        :to="channelLink"
        class="card-video__channel-wrapper"
      >
        <span
          :data-nosnippet="!card.channelName || null"
          class="card-video__channel-title"
        >
          <template v-if="card.channelName">
            {{ card.channelName }}
          </template>
        </span>
      </component>
      <p
        v-if="history"
        class="card-video__meta"
      >
        <NuxtLink
          v-if="card.channelName"
          :to="channelLink"
          class="card-video__channel-title"
        >{{ card.channelName }}</NuxtLink>
        <span
          :class="{'--remaining': remaining}"
          class="card-video__watched"
        >
          <UiIcon
            name="clock"
            size="12"
          />
          {{ remaining ? remainingText : watchedAgo }}
        </span>
      </p>
      <div
        v-if="!mini && !compact && !history && cardTags && cardTags.length > 0"
        class="card-video__tags"
      >
        <div class="card-video__tags-wrapper">
          <UiChips
            v-for="(item, index) in cardTags?.slice(0, 4)"
            micro
            :key="`${card.guid}-tag-${index}`"
            :item="item"
            class="card-video__chips"
            is-link
          />
        </div>
      </div>
      <CardDropdownTrigger
        :card="card"
        :show-watch-later="canWatchLater"
        :show-favorites="canFavorite"
        :show-report="!history"
        :show-remove="history"
        :show-share="history"
        class="card-video__dropdown"
        @share="emit('share', card)"
        @remove="emit('remove', card)"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import type { IVideoCard } from '../../../types';
import { convertString, getDuration } from '../../../runtime';
import { CSS_BREAKPOINTS } from '../../../config/css-breakpoints';

const featureFlags = useRuntimeConfig().public.featureFlags;

const props = defineProps<{
  card: IVideoCard
  mini?: boolean
  /** Аватар канала и тайтл, но без названия канала и тегов — лента подписок */
  compact?: boolean
  loading?: 'eager' | 'lazy'
  topChips?: string[]
  playlistId?: string
  priority?: boolean
  playlist?: boolean
  /** Тип «история»: мета-строка со временем просмотра вместо канала и тегов, меню с удалением и шарингом */
  history?: boolean
  /** Развилка мета-строки истории: вместо «когда смотрели» — сколько осталось до конца («Продолжить просмотр») */
  remaining?: boolean
}>();

const emit = defineEmits<{
  (e: 'remove', card: IVideoCard): void
  (e: 'share', card: IVideoCard): void
}>();

const { t, locale } = useI18n();

const NuxtLinkComponent = resolveComponent('NuxtLink');

const component = computed(() => props.card.channelName ? NuxtLinkComponent : 'div');

const { generateLink } = useGenerateLink();

const pageName = useGetPureRouteName();
const isChannelPage = computed(() => pageName === 'channels-channel');
const isModelPage = computed(() => pageName.startsWith('models-slug'));
const isSearchPage = computed(() => pageName.startsWith('search-slug'));

const previewId = useId();
const { activePreviewId, startPreviewByHover, stopPreviewByHover, startPreviewByTouch } = useVideoPreview();

const isPreviewShow = computed(() => activePreviewId.value === previewId);

const posterError = ref(false);
const avatarError = ref(false);

const posterPlaceholder = '/img/placeholder.webp';

const currentPoster = computed(() => {
  if (posterError.value || !props.card.thumbUrls) return posterPlaceholder;
  return undefined;
});

const currentAvatar = computed(() => {
  return props.card.channelAvatar;
});

function onPosterError() {
  posterError.value = true;
}

function onAvatarError() {
  avatarError.value = true;
}

const duration = computed(() => {
  return getDuration(props.card.duration);
});

const { isFavorite } = useVideoCollections();

const isFavoriteCard = computed(() => isFavorite(props.card.guid));

const canFavorite = computed(() => featureFlags.FeatureFavoritesEnabled);
const canWatchLater = computed(() => !props.history && featureFlags.FeatureWatchLaterEnabled);
const showActions = computed(() => !props.mini && (canFavorite.value || canWatchLater.value));

const hasProgress = computed(() => props.card.progress !== undefined);

const progress = computed(() => Math.min(100, Math.max(0, Math.round(props.card.progress ?? 0))));

const watchedAgo = computed(() => props.card.watchedAt === undefined
  ? ''
  : useFormatTimeAgo(props.card.watchedAt, locale.value, true));

const remainingText = computed(() => {
  const left = Math.ceil((props.card.duration * (100 - progress.value)) / 100 / 60);
  return t('minutes_left', { count: Math.max(left, 1) });
});

const rootPath = computed(() => props.playlistId ? `playlists/${props.playlistId}` : 'videos');

const link = computed(() => generateLink(`/${rootPath.value}/${props.card[ featureFlags.FetchByUrlEnabled ? 'url' : 'id' ]}`));

const channelLink = computed(() => props.card.channelName
  ? generateLink(`/channels/${convertString().toSlug(props.card.channelName)}`)
  : undefined);

const slug = useSlug();

const cardTags = computed(() => {
  const query = slug.value.toLowerCase();

  const models = props.card.models
      ? [ ...props.card.models ].filter(item => !isModelPage.value || item.toLowerCase() !== query)
      : [];

  const searchRank = (item: string) => {
    if (!isSearchPage.value || !query) return 0;
    const name = item.toLowerCase();
    if (name === query) return 0;
    return name.includes(query) ? 1 : 2;
  };

  const sortModels = models.sort((a, b) =>
      searchRank(a) - searchRank(b) || a.localeCompare(b)
  );

  return sortModels?.map(item => convertString().toChips(item, 'models'));
});
</script>

<style lang="scss">
$inset-label: 8px;
$layout-breakpoint: 250px;
$progress-height: 4px;

// Колонка под меню карточки фиксирована, а не auto: иначе ширина зависит от того, отрисовано меню
// или нет, и в момент подмены кнопки заголовок дёргается
$dropdown-column: 18px;

.card-video {
  @include video-card;

  position: relative;
  container-type: inline-size;

  @include to-br(xs) {
    touch-action: manipulation;
  }
}

.card-video__poster-wrapper {
  @include video-card-poster;
}

.card-video__poster {
  width: 100%;
  height: auto;
  object-fit: cover;
  aspect-ratio: 16/9;
}

.card-video__duration,
.card-video__new {
  position: absolute;
}

.card-video__new {
  top: $inset-label;
  left: $inset-label;
}

/* Кнопка избранного занимает левый верхний угол постера — плашка «новое» встаёт правее неё */
.--favorite .card-video__new {
  left: $inset-label * 2 + 36px;
}

.card-video__dropdown {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  color: $video-card-dropdown-icon;

  @include from-cq($layout-breakpoint) {
    grid-column: 3;
    grid-row: 1/3;
  }

  @include hover(color) {
    color: $video-card-dropdown-icon--hover;
  }
}

.card-dropdown__trigger > .ui-icon {
  @media (hover: hover) {
    transition: var(--transition) transform;
  }
}

.card-dropdown__trigger:is(:hover, :focus-visible) > .ui-icon {
  @media (hover: hover) {
    transform: scale($video-card-dropdown-size--hover);
  }
}

.card-video__duration {
  bottom: $inset-label;
  left: $inset-label;
}

.--progress .card-video__duration {
  bottom: $inset-label + $progress-height;
}

/* Превью кроет постер целиком ($z-index-plus) — полоса прогресса должна остаться поверх него */
.card-video__progress {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: $z-index-loader;
  height: $progress-height;
  background-color: $video-card-progress-bg;

  &::after {
    content: '';
    display: block;
    width: var(--card-video-progress, 0%);
    height: 100%;
    background-color: $video-card-progress-fill;
  }
}

.card-video__wrapper {
  display: grid;
  grid-template-columns: minmax(0, 1fr) $dropdown-column;
  align-items: start;
  align-content: center;
  color: $video-card-secondary;

  @include to-cq($layout-breakpoint) {
    row-gap: 2px;
  }

  @include from-cq($layout-breakpoint) {
    column-gap: 12px;
    grid-template-columns: 36px minmax(0, 1fr) $dropdown-column;
  }
}

.--avatar .card-video__wrapper {
  column-gap: 12px;
  grid-template-columns: 36px minmax(0, 1fr) $dropdown-column;
}

.--avatar .card-video__title {
  grid-column: 2;
}

.--avatar .card-video__channel-wrapper,
.--avatar .card-video__meta {
  grid-column: 2;
}

.--avatar .card-video__dropdown,
.--avatar .card-video__dots-wrapper {
  grid-column: 3;
  grid-row: 1/3;
}

.--avatar .card-video__tags {
  grid-column: 2;
}

@include to-br(sm) {
  .--no-channel .card-video__logo-wrapper,
  .--no-channel .card-video__channel-wrapper {
    display: none;
  }

  .--no-channel .card-video__wrapper {
    grid-template-columns: minmax(0, 1fr) $dropdown-column;
  }

  .--no-channel .card-video__title {
    grid-column: 1;
  }

  .--no-channel .card-video__tags {
    grid-column: 1;
  }

  .--no-channel .card-video__dropdown {
    grid-column: 2;
    grid-row: 1;
  }
}

.--mini .card-video__wrapper {
  grid-template-columns: minmax(0, 1fr) $dropdown-column;
}

/* Без строки канала и тегов вторая строка грида не нужна — иначе аватар и «троеточие» растягиваются. */
.--compact .card-video__logo-wrapper,
.--compact .card-video__dropdown {
  grid-row: 1;
}

.--mini .card-video__dropdown {
  grid-row: 1;
}

.card-video__title {
  @include font-sm;

  --line-clamp: 2;

  grid-row: 1;
  color: $video-card-text;

  @include to-cq($layout-breakpoint) {
    grid-column: 1;
  }

  @media (hover: hover) {
    transition: var(--transition) color;
  }
}

.card-video:is(:hover, :focus-visible) .card-video__title {
  @media (hover: hover) {
    color: $video-card-text--hover;
  }
}

.card-video__channel-wrapper {
  @include font-xs;

  font-weight: 400;
  display: grid;
  grid-row: 2;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: 4px;
  margin-top: 6px;

  @include to-cq($layout-breakpoint) {
    grid-column: 1;
  }
}

/* Мета-строка истории: «канал · когда смотрели / сколько осталось» на месте строки канала */
.card-video__meta {
  @include font-xs;

  display: flex;
  align-items: center;
  grid-row: 2;
  min-width: 0;
  margin: 6px 0 0;
  font-weight: 400;
  color: $video-card-secondary;

  @include to-cq($layout-breakpoint) {
    grid-column: 1;
  }
}

/* На узкой карточке (две колонки на телефоне) «канал · время» в строку не встают, а нешрикуемое
   время задаёт колонке min-content и разносит грид — оставляем только время просмотра */
@include to-cq($layout-breakpoint) {
  .card-video__meta .card-video__channel-title {
    display: none;
  }
}

.card-video__meta .card-video__channel-title + .card-video__watched::before {
  content: '·';
  margin-inline: 6px;
}

.card-video__watched {
  display: flex;
  align-items: center;
  column-gap: 4px;
  flex-shrink: 0;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
}

.card-video__watched.--remaining {
  color: $video-card-remaining;
}

.card-video__channel-logo {
  border-radius: 50%;
}

.card-video__logo-wrapper {
  grid-row: 1/3;
  grid-column: 1;
}

.card-video__logo-wrapper[href],
.card-video__channel-wrapper[href],
.card-video__title {
  @include hover(opacity) {
    opacity: 0.9;
  }
}

.card-video__channel-title {
  @include font-xs;

  font-weight: 400;
  color: $video-card-secondary;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  max-width: 100%;
}

.card-video.--mini {
  row-gap: 8px;
}

.card-video.--mini .card-video__title {
  color: $video-card-mini-title;
  font-weight: $video-card-mini-weight;
}

.card-video.--mini .card-video__poster-wrapper,
.card-video.--mini .card-video__poster {
  aspect-ratio: 16/9;
}

.card-video__tags {
  align-items: center;
  gap: 8px;
  grid-column: 1;

  @include from-cq($layout-breakpoint) {
    grid-column: 2;
    margin-top: 10px;
  }
}

.card-video__tags-wrapper {
  max-height: calc( var(--chips-height-micro) * 2 + 8px);
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.card-video__user {
  @include font-xs;

  width: 36px;
  height: 36px;
  display: grid;
  place-content: center;
  font-weight: 700;
  text-transform: uppercase;
  color: $video-card-user-text;
  background-color: $video-card-user-bg;
  border-radius: $radius-full;
}

@include to-cq($layout-breakpoint) {
  .card-video:not(.--avatar) .card-video__channel-logo,
  .card-video:not(.--avatar) .card-video__logo-wrapper {
    display: none;
  }
}
</style>
