<template>
    <div>
        <el-upload v-if="listType === 'drap'" drag :class="bgClass ?? 'upload-demo'"
            action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15" :multiple="multiple" :limit="limit"
            :accept="accept" :showFileList="showFileList">
            <slot v-if="!hasFile" name="trigger">
                <span>⬆️</span>
                <div class="el-upload__text">
                    拖拽文件到这里 <em>点击上传</em>
                </div>
            </slot>
        </el-upload>

        <div v-else class="file-upload-wrapper" :style="wrapperStyle">
            <el-upload ref="uploadRef" class="avatar-uploader" :action="action" :headers="headers" :data="data"
                :multiple="multiple" :limit="limit" :show-file-list="showFileList" :list-type="listType"
                :accept="accept" :before-upload="beforeUpload" :on-success="handleSuccess" :on-error="handleError"
                :on-preview="handlePreview" :on-remove="handleRemove" :before-remove="beforeRemove"
                :file-list="fileList">
                <slot v-if="!hasFile" name="trigger">
                    <div class="upload-btn">
                        <span>+</span>
                    </div>
                </slot>
            </el-upload>
        </div>

        <!-- 图片预览弹窗 -->
        <el-dialog v-model="dialogVisible" title="图片预览" width="30%" align-center>
            <img w-full :src="dialogImageUrl" alt="预览图片" style="object-fit: contain;" />
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, ComputedRef } from 'vue';
import type { UploadProps, UploadUserFile, UploadFile } from 'element-plus';
import { ElMessage, ElMessageBox } from 'element-plus';

const props = defineProps({
    bgClass: {
        type: String,
        default: 'upload-demo'
    },
    action: {
        type: String,
        default: '',
    },
    headers: {
        type: Object,
        default: () => ({}),
    },
    data: {
        type: Object,
        default: () => ({}),
    },
    multiple: {
        type: Boolean,
        default: false,
    },
    limit: {
        type: Number,
        default: Infinity,
    },
    listType: {
        type: String,
        default: 'picture-card',
    },
    accept: {
        type: String,
        default: '.jpg,.jpeg,.png,.gif,.webp',
    },
    maxSize: {
        type: Number,
        default: 5,
    },
    showFileList: {
        type: Boolean,
        default: true,
    },
    /** 上传卡片宽度 px */
    width: {
        type: Number,
        default: 100,
    },
    /** 上传卡片高度 px，不传则默认等于 width */
    height: {
        type: Number,
        default: undefined,
    },
});

const emit = defineEmits<{
    'update:modelValue': [files: UploadUserFile[]];
    success: [response: any, file: UploadFile];
    error: [error: any, file: UploadFile];
}>();

const modelValue = defineModel<any>({ default: [] });

const uploadRef = ref<InstanceType<typeof import('element-plus').ElUpload>>();
const dialogImageUrl = ref('');
const dialogVisible = ref(false);

// 内部文件列表
const fileList = ref<UploadUserFile[]>([...modelValue.value]);

const hasFile: ComputedRef<boolean> = computed(() => fileList.value.length > 0);

// 计算最终宽高，height不存在则和width保持一致
const wrapperStyle = computed(() => {
    const h = props.height ?? props.width;
    return {
        '--upload-width': `${props.width}px`,
        '--upload-height': `${h}px`,
    };
});

// 同步外部 v-model → 内部列表
watch(
    modelValue,
    (val) => {
        fileList.value = Array.isArray(val) ? [...val] : [];
    },
    { deep: true }
);

// 内部列表变更同步到父组件
watch(
    fileList,
    (val) => {
        emit('update:modelValue', [...val]);
    },
    { deep: true }
);

const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
    const ext = rawFile.name.substring(rawFile.name.lastIndexOf('.') + 1);
    const allowExts = props.accept.split(',').map((e) => e.trim().replace('.', '').toLowerCase());
    if (allowExts.length && !allowExts.includes(ext.toLowerCase())) {
        ElMessage.warning(`仅支持 ${props.accept} 格式文件`);
        return false;
    }

    const maxBytes = props.maxSize * 1024 * 1024;
    if (rawFile.size > maxBytes) {
        ElMessage.warning(`文件不能超过 ${props.maxSize}MB`);
        return false;
    }
    return true;
};

const handleSuccess: UploadProps['onSuccess'] = (response, file) => {
    if (response.code === 200) {
        file.url = response.data;
        if (!props.multiple) {
            fileList.value = [file];
        }
    }
    emit('success', response, file);
};

const handleError: UploadProps['onError'] = (error, file) => {
    ElMessage.error(`上传失败：${file.name}`);
    emit('error', error, file);
};

const handleRemove: UploadProps['onRemove'] = () => {
    // 可扩展外部事件
};

const beforeRemove: UploadProps['beforeRemove'] = async () => {
    try {
        await ElMessageBox.confirm('确定移除该文件？', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
        });
        return true;
    } catch {
        return false;
    }
};

const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
    dialogImageUrl.value = uploadFile.url ?? '';
    dialogVisible.value = true;
};

defineExpose({
    uploadRef,
    submit: () => uploadRef.value?.submit(),
    clearFiles: () => uploadRef.value?.clearFiles(),
});
</script>

<style scoped lang="scss">
:deep(.upload-demo) {
    .el-upload-dragger {
        background-color: #eee;
    }
}
.file-upload-wrapper {
    display: inline-block;
    --upload-width: 100px;
    --upload-height: 100px;

    .avatar-uploader {
        :deep(.el-upload--picture-card) {
            width: var(--upload-width);
            height: var(--upload-height);
            border-radius: 6px;
            border: 1px dashed #dcdfe6;
            transition: all 0.3s;

            &:hover {
                border-color: rgba(var(--main-color), 0.4);
                background-color: rgba(var(--main-color), 0.05);
            }
        }

        :deep(.el-upload-list--picture-card .el-upload-list__item) {
            width: var(--upload-width);
            height: var(--upload-height);
            border-radius: 6px;
        }
    }

    .upload-btn {
        width: var(--upload-width);
        height: var(--upload-height);
        display: flex;
        align-items: center;
        justify-content: center;
        color: #888;
        font-size: 28px;
    }
}
</style>