<template>
    <view class="ut-album-card" :style="albumStyle">
        <template v-for="(item, index) in displayItems" :key="index">
            <view class="card-item" :style="{ width: itemSize, height: itemSize }" @click="onItemClick(item, index)">
                <!-- 图片缩略图 -->
                <template v-if="item.type === 'image'">
                    <image class="thumb" :src="getThumb(item)" mode="aspectFill" />
                </template>

                <!-- 视频占位图 or 封面 -->
                <template v-else-if="item.type === 'video'">
                    <view class="video-box">
                        <image v-if="item.coverUrl" class="thumb" :src="item.coverUrl" mode="aspectFill" />
                        <video
                            v-else
                            class="thumb native-video"
                            :src="item.url"
                            object-fit="cover"
                            :controls="false"
                            :show-center-play-btn="false"
                            :show-play-btn="false"
                            :enable-play-gesture="false"
                        />
                        <view class="play-mask">
                            <up-icon name="play-circle" color="#fff" size="48rpx"></up-icon>
                        </view>
                    </view>
                </template>

                <!-- 文件卡片 -->
                <template v-else>
                    <view class="file-box d-flex flex-cln">
                        <view class="flex1">
                            <view class="f-s-28 c-primary up-line-2">{{ item.name || '文件' }}</view>
                            <view class="f-s-24 c-999">{{ formatSize(item.size) }}</view>
                        </view>
                        <view class="d-flex j-ed">
                            <image :src="getFileIconByUrl(item.url)" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
                        </view>
                    </view>
                </template>

                <!-- 超出数量显示 +N -->
                <view v-if="index === maxCount - 1 && items.length > maxCount" class="album-more">
                    <text class="more-text">+{{ items.length - maxCount }}</text>
                </view>
            </view>
        </template>
    </view>
</template>

<script setup>
import { computed } from 'vue';
import { fileExt, isUrl } from '@/utils/ruoyi';
import { getFileIconByUrl } from '@/utils/common';

// 兼容旧用法：urls，同时支持 modelValue 作为输入
const props = defineProps({
    // 通用输入：字符串 | 字符串数组 | 对象/对象数组
    modelValue: { type: [String, Array, Object], default: null },
    urls: { type: [Array, String, Object], default: () => [] },
    keyName: { type: String, default: 'url' },
    nameKey: { type: String, default: 'fileName' },
    sizeKey: { type: String, default: 'fileSize' },
    coverKey: { type: String, default: 'coverUrl' },
    maxCount: { type: Number, default: 9 },
    // 卡片网格样式
    space: { type: String, default: '10rpx' },
    multipleSize: { type: String, default: '210rpx' },
    // 预览
    previewFullImage: { type: Boolean, default: true },
    // 缩略图缩放因子（像素）
    factor: { type: Number, default: 4 },
    // 缓存破坏参数
    t: { type: String, default: '0' }
});

const itemSize = computed(() => props.multipleSize);

const albumStyle = computed(() => ({ display: 'flex', flexWrap: 'wrap', gap: props.space, width: '100%' }));

const inputValue = computed(() => (props.modelValue !== null && props.modelValue !== undefined ? props.modelValue : props.urls));

// 解析后缀，识别文件类型
const getTypeByUrl = (url) => {
    if (!url) return 'file';
    // 清理可能包裹的引号并取无查询参数的部分
    const cleaned = String(url).trim().replace(/^['"]+|['"]+$/g, '');
    const u = (cleaned.split('?')[0] || '').toLowerCase();
    // 提取扩展名并去除非字母数字的尾随字符（如引号）
    let ext = (u.split('.').pop() || '').trim().replace(/[^a-z0-9]+$/g, '');
    const imgExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
    const videoExt = ['mp4', 'mov', 'avi', 'mkv', 'webm', 'm4v'];
    if (imgExt.includes(ext)) return 'image';
    if (videoExt.includes(ext)) return 'video';
    return 'file';
};

// 将输入统一为 items
const items = computed(() => {
    const v = inputValue.value;
    const out = [];
    if (!v) return out;
    const pushItem = (raw) => {
        if (!raw) return;
        let url = '';
        let name = '';
        let size = undefined;
        let coverUrl = '';
        if (typeof raw === 'string') {
            url = String(raw).trim().replace(/^['"]+|['"]+$/g, '');
        } else if (typeof raw === 'object') {
            url = String(raw[props.keyName] || raw.url || '').trim().replace(/^['"]+|['"]+$/g, '');
            name = raw[props.nameKey] || raw.name || '';
            size = raw[props.sizeKey] || raw.size;
            coverUrl = String(raw[props.coverKey] || raw.coverUrl || '').trim().replace(/^['"]+|['"]+$/g, '');
        }
        if (!url) return;
        const originalUrl = (url.split('?')[0] || url);
        const type = getTypeByUrl(originalUrl);
        
        out.push({ type, url, originalUrl, name, size, coverUrl });
    };
        if (Array.isArray(v)) {
            v.forEach(pushItem);
        } else if (typeof v === 'string') {
            v.split(',').forEach((s) => pushItem(s));
        } else {
            pushItem(v);
        }
    return out;
});

const displayItems = computed(() => items.value.slice(0, props.maxCount));

// 生成图片缩略图地址（保持原图预览）
const getThumb = (item) => {
    if (!item?.url) return '';
    const size = (uni?.$u?.getPx ? uni.$u.getPx(props.multipleSize) : 160) * props.factor;
    // 保留原有参数的简单拼接策略
    return `${item.url}?w=${size}&h=${size}&t=${props.t}`;
};

const formatSize = (bytes) => {
    if (!bytes && bytes !== 0) return '';
    const kb = bytes / 1024;
    if (kb < 1024) return kb.toFixed(2) + ' KB';
    const mb = kb / 1024;
    return mb.toFixed(2) + ' MB';
};

// 预览逻辑：图片/视频/文件
const onItemClick = (item, displayIndex) => {
    if (!item) return;
    if (item.type === 'image') return previewImages(item);
    if (item.type === 'video') return previewVideo(item, displayIndex);
    return previewFile(item);
};

const previewImages = (clickedItem) => {
    if (!props.previewFullImage) return;
    const allImages = items.value.filter((i) => i.type === 'image');
    const urls = allImages.map((i) => i.originalUrl);
    const current = urls.indexOf(clickedItem.originalUrl);
    if (!urls.length) return;
    uni.previewImage({ urls, current: current >= 0 ? current : 0 });
};

const previewVideo = (item, index) => {
    // #ifdef MP-WEIXIN
    const allVideos = items.value.filter((i) => i.type === 'video');
    const sources = allVideos.map((i) => ({ url: i.url, type: 'video', poster: i?.coverUrl || '' }));
    const current = allVideos.findIndex((i) => i.url === item.url);
    // @ts-ignore
    uni.previewMedia({ sources, current: current >= 0 ? current : 0 });
    // #endif
    // #ifndef MP-WEIXIN
    uni.showToast({ title: '当前平台暂不支持视频预览', icon: 'none' });
    // #endif
};

const previewFile = (item) => {
    // 优先打开本地文件
    if (item.url && !isUrl(item.url)) {
        return uni.openDocument({ filePath: item.url, showMenu: true });
    }
    if (!item.url || !isUrl(item.url)) return;
    uni.showLoading({ title: '打开中...' });
    uni.downloadFile({
        url: item.url,
        success: (res) => {
            uni.openDocument({
                filePath: res.tempFilePath,
                showMenu: true,
                success: () => uni.hideLoading(),
                fail: () => {
                    uni.hideLoading();
                    uni.showToast({ title: '打开文件失败', icon: 'none' });
                }
            });
        },
        fail: () => {
            uni.hideLoading();
            uni.showToast({ title: '文件下载失败', icon: 'none' });
        }
    });
};
</script>

<style lang="scss" scoped>
.ut-album-card {
    display: flex;
    gap: 10rpx;
    flex-wrap: wrap;
    width: 100%;
    .card-item {
        background-color: #FAFAFA;
        border-radius: 8rpx;
        overflow: hidden;
        border: 1rpx solid #ccc;
        position: relative;
        .thumb {
            width: 100%;
            height: 100%;
            object-fit: cover;
            background-color: #ccc;
            display: block;
        }
        .video-box {
            width: 100%;
            height: 100%;
            position: relative;
            .native-video {
                background: #000;
            }
            .play-mask {
                position: absolute;
                top: 0;left: 0;right: 0;bottom: 0;
                display: flex;
                align-items: center;
                justify-content: center;
                background: rgba(0,0,0,0.15);
            }
        }
        .file-box {
            width: 100%;
            height: 100%;
            box-sizing: border-box;
            padding: 20rpx;
        }
        .album-more {
            position: absolute;
            top: 0;left: 0;right: 0;bottom: 0;
            background: rgba(0, 0, 0, 0.2);
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 8rpx;
            .more-text {
                color: #fff;
                font-size: 32rpx;
                font-weight: 500;
            }
        }
    }
}
</style>