<template>
    <template v-if="style == 'card'">
        <view class="ut-upload-card">
            <template v-for="(item, index) in fileList" :key="index">
                <view :style="{ width, height }" class="card-item">
                    <template v-if="accept === 'image'">
                        <image :src="item.status === 'uploading' && item.tempUrl ? item.tempUrl : item.url" :mode="mode" style="width: 100%; height: 100%" @click="onPreview(index)"></image>
                    </template>
                    <template v-else-if="accept === 'video'">
                        <video :src="item.status === 'uploading' && item.tempUrl ? item.tempUrl : item.url" controls style="width: 100%; height: 100%" @click="onPreview(index)"></video>
                    </template>
                    <template v-if="accept === 'file'">
                        <view class="d-flex flex-cln file-box" @click="onPreview(index)">
                            <view class="flex1">
                                <view class="f-s-28 c-primary up-line-2">{{ item.fileName || '文件' }}</view>
                                <view class="f-s-24 c-999">{{ item.fileSize ? (item.fileSize / 1024).toFixed(2) + ' KB' : '' }}</view>
                            </view>
                            <view class="d-flex j-ed">
                                <image :src="getFileIconByUrl(item.tempUrl || item.url)" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
                            </view>
                        </view>
                    </template>
                    <view v-if="item.status === 'uploading'" class="uploading-mask">
                        <view class="uploading-text">上传中...</view>
                    </view>
                    <view class="del-btn" @click.stop="onDelete(index)">
                        <up-icon name="close" color="#fff" size="32rpx"></up-icon>
                    </view>
                </view>
            </template>
            <view v-if="(fileList.length < maxCount)" :style="{ width, height }" @click="clickBtnUpload" class="card-item btn-select d-flex flex-cln j-c a-c">
                <view class="mb-10">
                    <up-icon :color="iconColor" :name="iconName" :iconSize="iconSize"></up-icon>
                </view>
                <view class="f-s-24 c-primary">{{ uploadText }}</view>
            </view>
        </view>
    </template>
</template>

<script setup lang="ts">
import upload from '@/utils/upload';
import { fileExt, isUrl } from '@/utils/ruoyi';
import { getFileIconByUrl } from '@/utils/common';

interface FileItem {
    url: string;
    fileName?: string;
    fileSize?: number;
    // 封面图
    coverUrl?: string;
    tempUrl?: string;
    status?: 'uploading' | 'done' | 'error';
    progress?: number;
}

interface UploadEvent {
    file: Array<{ url: string }>;
}

interface DeleteEvent {
    index: number;
}

interface Props {
    modelValue: string | string[] | Record<string, any> | null;
    maxCount: number;
    width: string;
    height: string;
    multiple: boolean;
    uploadText: string;
    uploadIcon: string;
    accept: string; // image/video/file
    uploadUrl?: string;
    uploadTimeout?: number;
    extension?: string | string[]; // 限制选择的文件扩展名（仅在 accept==='file' 生效）
    valueType: 'string' | 'array' | 'object';
    style: 'card' | 'list';
    iconName: string;
    iconColor: string;
    iconSize: string | number;
    mode: 'aspectFill' | 'aspectFit' | 'widthFix' | 'heightFix' | 'top' | 'bottom' | 'center' | 'left' | 'right' | 'top left' | 'top right' | 'bottom left' | 'bottom right';
}

const props = withDefaults(defineProps<Props>(), {
    modelValue: () => [],
    maxCount: 1,
    width: '210rpx',
    height: '210rpx',
    multiple: true,
    uploadText: '点击上传',
    uploadIcon: 'plus',
    accept: 'image',
    uploadUrl: '/resource/oss/upload',
    uploadTimeout: 600000,
    extension: () => ['pdf'],
    valueType: 'string',
    style: 'card',
    iconName: 'plus', // plus/camera
    iconColor: '#37a954',
    iconSize: '30rpx',
    mode: 'aspectFill',
});

const emit = defineEmits<{
    change: [value: any];
    'update:modelValue': [value: any];
}>();

const fileList = ref<FileItem[]>([]);
const buildFileName = (path: string, kind: 'image' | 'video' | 'file') => {
    const ext = fileExt(path) || 'dat';
    const prefix = kind === 'image' ? 'img' : kind === 'video' ? 'video' : 'file';
    return `${prefix}_${Date.now()}.${ext}`;
};
const clickBtnUpload = async () => {
    // 判断是图片/视频/文件不同的上传
    // 使用chooseMedia选择图片或视频 chooseMessageFile选择文件
    try {
        const remain = props.maxCount - fileList.value.length;
        if (remain <= 0) {
            uni.showToast({ title: '已达最大上传数量', icon: 'none' });
            return;
        }

        if (props.accept === 'file') {
            const res: any = await uni.chooseMessageFile({
                count: props.multiple ? remain : 1,
                type: 'file',
                extension: Array.isArray(props.extension) ? props.extension : [props.extension]
            });
            const files = (res?.tempFiles || []) as Array<{ name?: string; size: number; path: string }>;
            for (const f of files) {
                const name = f.name || buildFileName(f.path, 'file');
                const placeholder: FileItem = { url: '', fileName: name, fileSize: f.size, tempUrl: f.path, status: 'uploading' };
                const idx = fileList.value.push(placeholder) - 1;
                try {
                    const upRes = await upload({ url: props.uploadUrl!, filePath: f.path, name: 'file', timeout: props.uploadTimeout });
                    const serverUrl = (upRes as any)?.data?.url || (upRes as any)?.data?.fileUrl || (upRes as any)?.data?.path || (upRes as any)?.data?.uri;
                    if (upRes.code === 200) {
                        const current = fileList.value[idx] || placeholder;
                        fileList.value.splice(idx, 1, { ...current, url: serverUrl || current.tempUrl || '', status: 'done' });
                        uni.showToast({ title: '上传成功', icon: 'success' });
                        emitCurrentValue();
                    } else {
                        const current = fileList.value[idx] || placeholder;
                        fileList.value.splice(idx, 1, { ...current, status: 'error' });
                        uni.showToast({ title: '上传失败', icon: 'none' });
                    }
                } catch (err) {
                    console.error('upload file error:', err);
                    const current = fileList.value[idx] || placeholder;
                    fileList.value.splice(idx, 1, { ...current, status: 'error' });
                }
            }
        } else {
            const res: any = await uni.chooseMedia({
                count: props.multiple ? remain : 1,
                mediaType: props.accept === 'image' ? ['image'] : ['video'],
                sourceType: ['album', 'camera'],
            });
            const files = (res?.tempFiles || []) as Array<{ tempFilePath: string; size: number; thumbTempFilePath?: string }>;
            for (const f of files) {
                const name = buildFileName(f.tempFilePath, props.accept === 'image' ? 'image' : 'video');
                const placeholder: FileItem = { url: '', fileName: name, fileSize: f.size, tempUrl: f.tempFilePath, status: 'uploading' };
                const idx = fileList.value.push(placeholder) - 1;
                try {
                    const upRes = await upload({ url: props.uploadUrl!, filePath: f.tempFilePath, name: 'file', timeout: props.uploadTimeout });
                    const serverUrl = (upRes as any)?.data?.url || (upRes as any)?.data?.fileUrl || (upRes as any)?.data?.path || (upRes as any)?.data?.uri;
                    if (upRes.code === 200) {
                        const current = fileList.value[idx] || placeholder;
                        fileList.value.splice(idx, 1, { ...current, url: serverUrl || current.tempUrl || '', status: 'done' });
                        uni.showToast({ title: '上传成功', icon: 'success' });
                        emitCurrentValue();
                    } else {
                        const current = fileList.value[idx] || placeholder;
                        fileList.value.splice(idx, 1, { ...current, status: 'error' });
                        uni.showToast({ title: '上传失败', icon: 'none' });
                    }
                } catch (err) {
                    console.error('upload media error:', err);
                    const current = fileList.value[idx] || placeholder;
                    fileList.value.splice(idx, 1, { ...current, status: 'error' });
                }
            }
        }

        // 最后再统一触发一次
        emitCurrentValue();
    } catch (e) {
        console.log('upload error:', e);
    }
};


const onPreview = (index: number) => {
    const item = fileList.value[index];
    if (!item) return;
    // 文件类型：上传中也允许预览本地临时文件
    if (props.accept === 'file') {
        const localPath = item.status === 'uploading' && item.tempUrl
            ? item.tempUrl
            : (!isUrl(item.url) ? item.url : '');
        if (localPath) {
            uni.openDocument({
                filePath: localPath,
                showMenu: true,
                fail: () => {
                    uni.showToast({ title: '打开文件失败', icon: 'none' });
                }
            });
            return;
        }
        if (item.url && isUrl(item.url)) {
            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' });
                }
            });
        }
        return;
    }
    // 图片/视频：上传中不允许预览
    if (item?.status === 'uploading') return;
    if (props.accept === 'image') {
        const urls = fileList.value.map((i) => i.url);
        uni.previewImage({
            urls,
            current: urls[index]
        });
        return;
    }
    if (props.accept === 'video') {
        // #ifdef MP-WEIXIN
        const sources = fileList.value.map((i) => ({ url: i.url, type: 'video', poster: i.coverUrl || '' }));
        // @ts-ignore
        wx.previewMedia({
            sources,
            current: index
        });
        // #endif
        // #ifndef MP-WEIXIN
        uni.showToast({ title: '当前平台暂不支持视频预览', icon: 'none' });
        // #endif
    }
};

const onDelete = (index: number) => {
    fileList.value.splice(index, 1);
    const urls = fileList.value.map((i) => i.url);
    let out: any;
    if (props.valueType === 'string') {
        out = urls[0] || '';
    } else if (props.valueType === 'array') {
        out = fileList.value;
    } else {
        out = props.multiple ? fileList.value : fileList.value[0] || null;
    }
    emit('update:modelValue', out);
    emit('change', out);
};
function emitCurrentValue() {
    const validList = fileList.value;
    const urls = validList.map((i) => i.url).filter(Boolean);
    let out: any;
    if (props.valueType === 'string') {
        out = urls[0] || '';
    } else if (props.valueType === 'array') {
        out = validList;
    } else {
        out = props.multiple ? validList : validList[0] || null;
    }
    emit('update:modelValue', out);
    emit('change', out);
}
watch(
    () => props.modelValue,
    (val) => {
        if (!val) {
            fileList.value = [];
            return;
        }
        if (props.valueType === 'string') {
            if (typeof val === 'string') {
                fileList.value = val ? [{ url: val }] : [];
            }
        } else if (props.valueType === 'array') {
            if (Array.isArray(val)) {
                if (val.length && typeof val[0] === 'string') {
                    fileList.value = (val as string[]).map((u) => ({ url: u }));
                } else {
                    fileList.value = val as FileItem[];
                }
            }
        } else {
            if (Array.isArray(val)) {
                fileList.value = val as FileItem[];
            } else if (typeof val === 'object' && (val as any).url) {
                fileList.value = [val as FileItem];
            }
        }
    },
    { immediate: true }
);
</script>

<style lang="scss" scoped>
.ut-upload-card {
    display: flex;
    gap: 10rpx;
    flex-wrap: wrap;
    .card-item {
        width: 210rpx;
        height: 210rpx;
        background-color: #FAFAFA;
        border-radius: 8rpx;
        overflow: hidden;
        border: 1rpx solid #ccc;
        position: relative;
    }
    .file-box {
        width: 100%;
        height: 100%;
        box-sizing: border-box;
        padding: 20rpx;
    }
    .btn-select {
        border-style: dashed;
    }
}
.del-btn {
    position: absolute;
    top: 0rpx;
    right: 0rpx;
    width: 48rpx;
    height: 48rpx;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.35);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
}
.uploading-mask {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.4);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
}
.uploading-text {
    font-size: 24rpx;
}
</style>
