<template>
    <view class="lazy-image-wrapper" :style="wrapStyle">
        <!-- 仅在可视区才真正挂载 image，避免无意义的下载+解码 -->
        <image v-if="visible" class="img" :src="realSrc" :mode="mode" :lazy-load="true" webp="true"
            show-menu-by-longpress="false" :style="{ opacity: imageLoaded ? 1 : 0 }" @load="onImageLoad"
            @error="onImageError" />
        <view v-if="!imageLoaded && !showError" class="placeholder">
            <view class="loading-bg"></view>
        </view>
        <view v-if="showError" class="error-placeholder">
            <image class="fallback" src="/static/images/home/defaultIcon.png" :mode="mode" />
        </view>
    </view>
</template>

<script>
/** ===== 全局简单并发池（同域连接数受限时很有用） ===== */
const POOL_MAX = 6;
const queue = [];
let loadingCount = 0;

function schedule(task) {
    if (loadingCount < POOL_MAX) {
        loadingCount++;
        task(() => {
            loadingCount--;
            const next = queue.shift();
            if (next) schedule(next);
        });
    } else {
        queue.push(task);
    }
}

export default {
    name: 'LazyImage',
    props: {
        src: { type: String, default: '' },
        width: { type: String, default: '100%' },
        height: { type: String, default: '100%' },
        mode: { type: String, default: 'aspectFill' },
        /** 后端/CDN若支持按需裁剪，可传如 'w=360' 自动拼接到 url 上 */
        query: { type: String, default: '' },
    },
    data() {
        return {
            imageLoaded: false,
            showError: false,
            visible: false,
            retry: 0,
            io: null,
            release: null,
        };
    },
    computed: {
        wrapStyle() {
            return `width:${this.width};height:${this.height};`;
        },
        optimizedSrc() {
            if (!this.src) return '';
            if (!this.query) return this.src;
            const joiner = this.src.includes('?') ? '&' : '?';
            return `${this.src}${joiner}${this.query}`;
        },
        // 只有在可视区且进入并发池后才真正给 <image> 一个 src
        realSrc() {
            return this.visible ? this.optimizedSrc : '';
        },
    },
    mounted() {
        // 视口观察：进入视区才 visible=true；离开且已加载则释放（长列表更稳）
        // #ifdef MP-WEIXIN
        this.io = this.createIntersectionObserver();
        this.io.relativeToViewport({ top: 200, bottom: 200 }).observe('.lazy-image-wrapper', (res) => {
            const inView = res?.intersectionRatio > 0;
            if (inView && !this.visible) {
                this.visible = true;
                // 进入视区后，放入并发池启动加载
                schedule((done) => {
                    this.release = done;
                    // realSrc 已绑定，微信会开始拉取
                });
            } else if (!inView && this.visible && this.imageLoaded) {
                // 离开视区：释放内存，下次滚回来再加载（已缓存，几乎瞬开）
                this.visible = false;
                this.imageLoaded = false;
                this.showError = false;
                this.retry = 0;
            }
        });
        // #endif
    },
    beforeDestroy() {
        if (this.io) this.io.disconnect();
    },
    methods: {
        onImageLoad() {
            this.imageLoaded = true;
            this.showError = false;
            if (this.release) this.release();
        },
        onImageError() {
            if (this.release) this.release();
            if (this.retry < 2) {
                // 指数退避重试（常见于弱网/CDN偶发 499/超时）
                this.retry++;
                const delay = 200 * Math.pow(2, this.retry); // 200ms, 400ms
                setTimeout(() => {
                    // 复位触发二次下载（依然通过并发池）
                    this.imageLoaded = false;
                    this.showError = false;
                    schedule((done) => {
                        this.release = done;
                        // realSrc 不变，微信会再次尝试（有些机型需先置空再赋值，可按需启用下面两行）
                        // this.visible = false;
                        // this.$nextTick(() => (this.visible = true));
                    });
                }, delay);
            } else {
                this.showError = true;
            }
        },
    },
};
</script>

<style lang="less" scoped>
.lazy-image-wrapper {
    position: relative;
    overflow: hidden;

    .img,
    .fallback {
        display: block;
        width: 100%;
        height: 100%;
        transition: opacity .25s ease;
        will-change: opacity;
    }

    .placeholder,
    .error-placeholder {
        position: absolute;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #f5f5f5;
        z-index: 1;
    }

    .error-placeholder {
        z-index: 2;
    }

    .loading-bg {
        width: 100%;
        height: 100%;
        background: linear-gradient(90deg, rgba(0, 0, 0, .06), rgba(0, 0, 0, .12), rgba(0, 0, 0, .06));
        background-size: 200% 100%;
        animation: shimmer 1.2s infinite linear;
        border-radius: 4rpx;
    }

    @keyframes shimmer {
        0% {
            background-position: -200% 0;
        }

        100% {
            background-position: 200% 0;
        }
    }
}
</style>
