<script setup lang="ts">
    import md5 from 'crypto-js/md5'
    import { CACHE_PREFIX } from './index'

    const props = defineProps({
        width: {
            type: [String, Number],
            default: '320',
        },
        height: {
            type: [String, Number],
            default: '240',
        },
        radius: {
            type: [String, Number],
            default: 0,
        },
        mode: {
            type: String,
            default: 'scaleToFill',
        },
        background: {
            type: String,
            default: '#F7F7F7',
        },
        src: {
            type: String,
            required: true,
        },
        debug: {
            type: Boolean,
            default: false,
        },
    })

    const visibleSrc = ref()
    const hashCacheKey = ref()
    onMounted(async () => {
        await tryCache()
    })

    // 监听 src 变化
    watch(
        () => props.src,
        async () => await tryCache(),
    )

    function log(...args: any[]) {
        if (props.debug) {
            console.log('[CacheImage]', ...args)
        }
    }

    // 尝试缓存图片，如果失败则使用原始图片
    async function tryCache() {
        let url = props.src
        if (!url) {
            return
        }

        // 如果是网络图片，则缓存到本地
        if (url.startsWith('http')) {
            // #ifdef APP-PLUS
            const hash = md5(url).toString().toUpperCase()
            hashCacheKey.value = `${CACHE_PREFIX}${hash}`
            const cache = uni.getStorageSync(hashCacheKey.value)
            if (cache) {
                log('cache hit', url)
                try {
                    const result = JSON.parse(cache)
                    url = result.url
                } catch (_) {
                    url = cache
                }
            } else {
                log('cache miss', url)
                try {
                    const res = await uni.downloadFile({ url })
                    if (res.statusCode === 200) {
                        const { savedFilePath, size } = await new Promise<{ savedFilePath: string; size: number }>(
                            (resolve, reject) => {
                                uni.saveFile({
                                    tempFilePath: res.tempFilePath,
                                    success: (res) => {
                                        const filePath = res.savedFilePath
                                        uni.getFileInfo({
                                            filePath,
                                            success: (res) =>
                                                resolve({
                                                    savedFilePath: filePath,
                                                    size: res.size,
                                                }),
                                            fail: (err) => reject(err),
                                        })
                                    },
                                    fail: (err) => reject(err),
                                })
                            },
                        )
                        url = `${savedFilePath}`

                        // 缓存图片本地地址
                        uni.setStorageSync(
                            hashCacheKey.value,
                            JSON.stringify({
                                url,
                                size,
                                timestamp: Date.now(),
                            }),
                        )
                    } else {
                        log('cache error', url, res)
                        url = props.src
                    }
                } catch (e) {
                    console.error(e)
                }
            }
            log(props.src, '=>', url, hash, hashCacheKey.value)
            // #endif
        }

        visibleSrc.value = url
    }

    const hasError = ref(false)

    function onError() {
        if (hasError.value) {
            return
        }

        hasError.value = true
        visibleSrc.value = props.src
        // 清除缓存
        hashCacheKey.value && uni.removeStorageSync(hashCacheKey.value)
        console.warn('CacheImage cache error')
    }
</script>

<template>
    <fui-lazyload
        v-bind="$attrs"
        :mode="props.mode"
        :width="props.width"
        :height="props.height"
        :radius="props.radius"
        :background="props.background"
        :src="visibleSrc"
        @error="onError"
    >
        <slot></slot>
    </fui-lazyload>
</template>
