<template>
    <div class="upload">
        <el-upload
            v-model:file-list="fileList"
            ref="uploadRefs"
            :action="action"
            :multiple="multiple"
            :limit="limit"
            :show-file-list="false"
            :headers="headers"
            :data="data"
            :http-request="useDirect ? httpRequest : undefined"
            :on-progress="handleProgress"
            :on-success="handleSuccess"
            :on-exceed="handleExceed"
            :on-error="handleError"
            :accept="getAccept"
        >
            <slot />
        </el-upload>
        <el-dialog
            v-if="showProgress && fileList.length"
            v-model="visible"
            title="上传进度"
            :close-on-click-modal="false"
            width="500px"
            :modal="false"
            @close="handleClose"
        >
            <div class="file-list p-4">
                <template v-for="(item, index) in fileList" :key="index">
                    <div class="mb-5">
                        <div>{{ item.name }}</div>
                        <div class="flex-1">
                            <el-progress :percentage="parseInt(item.percentage)" />
                        </div>
                    </div>
                </template>
            </div>
        </el-dialog>
    </div>
</template>

<script lang="ts">
import type { ElUpload, UploadRequestOptions } from 'element-plus'
import { computed, defineComponent, ref, shallowRef } from 'vue'

import config from '@/config'
import { RequestCodeEnum } from '@/enums/requestEnums'
import useAppStore from '@/stores/modules/app'
import useUserStore from '@/stores/modules/user'
import feedback from '@/utils/feedback'
import {
    DirectUploadFallbackError,
    uploadVideoDirectToCos
} from '@/utils/oss-direct-upload'

export default defineComponent({
    components: {},
    props: {
        // 上传文件类型
        type: {
            type: String,
            default: 'image'
        },
        // 是否支持多选
        multiple: {
            type: Boolean,
            default: true
        },
        // 多选时最多选择几条
        limit: {
            type: Number,
            default: 10
        },
        // 上传时的额外参数
        data: {
            type: Object,
            default: () => ({})
        },
        // 是否显示上传进度
        showProgress: {
            type: Boolean,
            default: false
        },
        // 视频直传到 OSS（绕开服务器中转，仅 type=video 生效）
        direct: {
            type: Boolean,
            default: false
        }
    },
    emits: ['change', 'error', 'success', 'allSuccess'],
    setup(props, { emit }) {
        const userStore = useUserStore()
        const appStore = useAppStore()
        const uploadRefs = shallowRef<InstanceType<typeof ElUpload>>()
        const action = ref(`${config.baseUrl}${config.urlPrefix}/upload/${props.type}`)
        const headers = computed(() => ({
            token: userStore.token,
            version: appStore.config.version
        }))
        const visible = ref(false)
        const fileList = ref<any[]>([])

        // 仅 video + direct 时才接管 http-request
        const useDirect = computed(() => props.direct && props.type === 'video')

        const handleProgress = () => {
            visible.value = true
        }
        let uploadLen = 0
        const handleSuccess = (response: any, file: any) => {
            uploadLen++
            if (uploadLen == fileList.value.length) {
                uploadLen = 0
                fileList.value = []
                emit('allSuccess')
            }
            emit('change', file)
            if (response.code == RequestCodeEnum.SUCCESS) {
                emit('success', response)
            }
            if (response.code == RequestCodeEnum.FAIL && response.msg) {
                feedback.msgError(response.msg)
            }
        }
        const handleError = (event: any, file: any) => {
            uploadLen++
            if (uploadLen == fileList.value.length) {
                uploadLen = 0
                fileList.value = []
                emit('allSuccess')
            }
            feedback.msgError(`${file.name}文件上传失败`)
            uploadRefs.value?.abort(file)
            visible.value = false
            emit('change', file)
            emit('error', file)
        }
        const handleExceed = () => {
            feedback.msgError(`超出上传上限${props.limit}，请重新上传`)
        }
        const handleClose = () => {
            fileList.value = []
            visible.value = false
        }

        const getAccept = computed(() => {
            switch (props.type) {
                case 'image':
                    return '.jpg,.png,.gif,.jpeg,.ico'
                case 'video':
                    return '.wmv,.avi,.mpg,.mpeg,.3gp,.mov,.mp4,.flv,.rmvb,.mkv'
                default:
                    return '*'
            }
        })

        // 走 COS 直传：成功时模拟老接口的响应 envelope，失败/降级时回到默认 XHR
        const httpRequest = async (options: UploadRequestOptions) => {
            visible.value = true
            try {
                const data = await uploadVideoDirectToCos({
                    file: options.file,
                    type: 'video',
                    cid: Number((options.data as any)?.cid ?? 0),
                    onProgress(info) {
                        // 触发 ElUpload 内部进度（保持与默认上传一致的体验）
                        ;(options as any).onProgress?.({
                            percent: info.percent,
                            loaded: info.loaded,
                            total: info.total
                        })
                    }
                })
                ;(options as any).onSuccess?.({ code: RequestCodeEnum.SUCCESS, msg: 'ok', data })
            } catch (err: any) {
                if (err instanceof DirectUploadFallbackError) {
                    feedback.msgWarning('当前存储不支持直传，已切换为普通上传')
                    await defaultXhrUpload(options)
                    return
                }
                ;(options as any).onError?.(err instanceof Error ? err : new Error(String(err?.message ?? err)))
            }
        }

        // 当 STS 不可用时，回退到 ElUpload 原生 XHR 行为（POST /upload/{type}）
        const defaultXhrUpload = (options: UploadRequestOptions) =>
            new Promise<void>((resolve) => {
                const xhr = new XMLHttpRequest()
                xhr.open('POST', action.value)
                Object.entries(headers.value).forEach(([k, v]) => xhr.setRequestHeader(k, String(v ?? '')))
                xhr.upload.onprogress = (e) => {
                    if (!e.lengthComputable) return
                    const percent = (e.loaded / e.total) * 100
                    ;(options as any).onProgress?.({ percent, loaded: e.loaded, total: e.total })
                }
                xhr.onload = () => {
                    try {
                        const json = JSON.parse(xhr.responseText)
                        ;(options as any).onSuccess?.(json)
                    } catch (e) {
                        ;(options as any).onError?.(new Error('上传响应解析失败'))
                    }
                    resolve()
                }
                xhr.onerror = () => {
                    ;(options as any).onError?.(new Error('网络错误'))
                    resolve()
                }
                const formData = new FormData()
                formData.append('file', options.file)
                Object.entries(options.data || {}).forEach(([k, v]) => formData.append(k, String(v)))
                xhr.send(formData)
            })

        return {
            uploadRefs,
            action,
            headers,
            visible,
            fileList,
            useDirect,
            httpRequest,
            getAccept,
            handleProgress,
            handleSuccess,
            handleError,
            handleExceed,
            handleClose
        }
    }
})
</script>

<style lang="scss"></style>
