<template>
  <div ref="waterfallWrapper" class="waterfall-list" :style="wrapperStyle">
    <div
      v-for="(item, index) in list"
      :key="getKey(item, index)"
      :style="getItemStyle(index)"
      class="waterfall-item"
    >
      <div class="waterfall-card h-full">
        <slot name="item" :item="item" :index="index" :url="getRenderURL(item)" />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useDebounceFn } from "@vueuse/core";
import type { PropType } from "vue";
import { computed, provide, useTemplateRef, watch } from "vue";
import type { ViewCard } from "../types/waterfall";
import { useCalculateCols, useLayout } from "../use";
import { waterfallImageLoadedKey } from "../utils/keys";
import { getValue } from "../utils/util";

const props = defineProps({
  list: {
    type: Array as PropType<any[]>,
    default: () => [],
  },
  rowKey: {
    type: String,
    default: "id",
  },
  imgSelector: {
    type: String,
    default: "src",
  },
  width: {
    type: Number,
    default: 200,
  },
  widthSelector: {
    type: String,
    default: "width",
  },
  heightSelector: {
    type: String,
    default: "height",
  },
  columns: {
    type: Number,
    default: 3,
  },
  gutter: {
    type: Number,
    default: 10,
  },
  hasAroundGutter: {
    type: Boolean,
    default: true,
  },
  animationPrefix: {
    type: String,
    default: "animate__animated",
  },
  animationEffect: {
    type: String,
    default: "fadeIn",
  },
  animationDuration: {
    type: Number,
    default: 1000,
  },
  animationDelay: {
    type: Number,
    default: 300,
  },
  backgroundColor: {
    type: String,
    default: "#fff",
  },
  lazyload: {
    type: Boolean,
    default: true,
  },
  loadProps: {
    type: Object,
    default: () => {},
  },
  crossOrigin: {
    type: Boolean,
    default: true,
  },
  delay: {
    type: Number,
    default: 300,
  },
});

const waterfallWrapper = useTemplateRef<HTMLElement>("waterfallWrapper");

// 瀹瑰櫒鍧椾俊鎭?
const { wrapperWidth, colWidth, cols, offsetX } = useCalculateCols(
  props,
  waterfallWrapper
);

const getNumericValue = (item: ViewCard, selector: string): number | null => {
  const value = getValue(item, selector)[0];
  const resolved = Number(value);

  if (!Number.isFinite(resolved) || resolved <= 0) return null;

  return resolved;
};

const getItemHeightByIndex = (index: number): number | null => {
  const item = props.list[index];
  if (!item || colWidth.value <= 0) return null;

  const width = getNumericValue(item, props.widthSelector);
  const height = getNumericValue(item, props.heightSelector);

  if (!width || !height) return null;

  return (colWidth.value * height) / width;
};

const itemHeights = computed(() =>
  props.list.map((_, index) => getItemHeightByIndex(index))
);

// 瀹瑰櫒楂樺害锛屽潡瀹氫綅
const { wrapperHeight, layoutHandle } = useLayout(
  props,
  colWidth,
  cols,
  offsetX,
  waterfallWrapper,
  (index, item) => getItemHeightByIndex(index) ?? item.offsetHeight
);

const wrapperStyle = computed(() => ({
  height: `${wrapperHeight.value}px`,
  backgroundColor: props.backgroundColor,
}));

const getItemStyle = (index: number) => {
  const height = itemHeights.value[index];
  if (!height) return undefined;

  return {
    height: `${height}px`,
  };
};

// 1s鍐呮渶澶氭墽琛屼竴娆℃帓鐗堬紝鍑忓皯鎬ц兘寮€閿€
const renderer = useDebounceFn(() => {
  layoutHandle();
}, props.delay);

watch(
  [wrapperWidth, cols, itemHeights, () => props.list],
  () => {
    renderer();
  },
  { immediate: true }
);

// 鍥剧墖鍔犺浇瀹屾垚
provide(waterfallImageLoadedKey, renderer);

// 鏍规嵁閫夋嫨鍣ㄨ幏鍙栧浘鐗囧湴鍧€
const getRenderURL = (item: ViewCard): string => {
  return String(getValue(item, props.imgSelector)[0] ?? "");
};

// 鑾峰彇鍞竴鍊?
const getKey = (item: ViewCard, index: number): string | number => {
  return item[props.rowKey] ?? index;
};

const clearAndReload = () => {
  layoutHandle();
};

defineExpose({
  waterfallWrapper,
  wrapperHeight,
  getRenderURL,
  getKey,
  list: props.list,
  backgroundColor: props.backgroundColor,
  renderer,
  clearAndReload,
});
</script>

<style scoped>
.waterfall-list {
  width: 100%;
  position: relative;
  overflow: hidden;
  --waterfall-radius: 22px;
  --waterfall-border: rgba(255, 255, 255, 0.72);
  --waterfall-surface: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.9),
    rgba(244, 250, 255, 0.72)
  );
  --waterfall-shadow: 0 16px 32px rgba(118, 144, 169, 0.12);
}

.waterfall-item {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate3d(0, 3000px, 0);
  visibility: hidden;
  will-change: transform, opacity;
}

.waterfall-card {
  height: 100%;
  overflow: hidden;
  border: 1px solid var(--waterfall-border);
  border-radius: var(--waterfall-radius);
  background: var(--waterfall-surface);
  box-shadow: var(--waterfall-shadow);
  backdrop-filter: blur(14px);
  transform-origin: center top;
}

.waterfall-card :deep(img) {
  display: block;
}

@keyframes fadeIn {
  0% {
    opacity: 0.01;
    transform: translate3d(0, 18px, 0) scale(0.985);
  }

  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

.fadeIn {
  animation-name: fadeIn;
}
</style>
