<template>
	<div class="image-upload">
		<a-upload
			v-model:file-list="fileList"
			list-type="picture-card"
			:custom-request="customUpload"
			:before-upload="beforeUpload"
			:accept="accept"
			:max-count="maxCount"
			:disabled="disabled"
			:show-upload-list="{
				showPreviewIcon: true,
				showRemoveIcon: !disabled,
			}"
			@preview="handlePreview"
			@change="handleChange"
			@drop="handleDrop"
			@dragenter="handleDragEnter"
			@dragleave="handleDragLeave"
			class="custom-upload"
			:class="{ 'drag-over': isDragOver }"
		>
			<div v-if="fileList.length < maxCount && !disabled" class="upload-area">
				<loading-outlined v-if="uploading" class="upload-icon" />
				<plus-outlined v-else class="upload-icon" />
				<div class="ant-upload-text">
					{{ uploading ? '上传中...' : uploadText }}
				</div>
				<div v-if="tip" class="ant-upload-tip">{{ tip }}</div>
			</div>
		</a-upload>
		<a-modal :open="previewVisible" :title="previewTitle" :footer="null" :width="800" @cancel="handleCancel">
			<img alt="图片预览" style="width: 100%; max-height: 600px; object-fit: contain" :src="previewImage" />
		</a-modal>
	</div>
</template>

<script setup>
import { ref, watch, computed } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'
import uploadConfig from '@/config/upload'

const props = defineProps({
	// 图片列表
	modelValue: {
		type: [Array, String],
		default: () => [],
	},
	// 最大上传数量，默认1为单图上传
	maxCount: {
		type: Number,
		default: 1,
	},
	// 接受的文件类型
	accept: {
		type: String,
		default: 'image/*',
	},
	// 是否禁用
	disabled: {
		type: Boolean,
		default: false,
	},
	// 是否返回URL字符串（单图）或URL数组（多图）
	returnUrl: {
		type: Boolean,
		default: true,
	},
	// 上传按钮文字
	uploadText: {
		type: String,
		default: '上传图片',
	},
	// 提示文字
	tip: {
		type: String,
		default: '',
	},
	// 最小宽度（像素）
	minWidth: {
		type: Number,
		default: 0,
	},
	// 最大宽度（像素）
	maxWidth: {
		type: Number,
		default: 0,
	},
	// 最小高度（像素）
	minHeight: {
		type: Number,
		default: 0,
	},
	// 最大高度（像素）
	maxHeight: {
		type: Number,
		default: 0,
	},
	// 是否删除前确认
	confirmBeforeRemove: {
		type: Boolean,
		default: false,
	},
	// 自定义上传按钮内容
	customUploadBtn: {
		type: Function,
		default: null,
	},
})

const emit = defineEmits(['update:modelValue', 'change', 'preview', 'remove', 'uploadSuccess', 'uploadError'])

// 文件列表
const fileList = ref([])

// 预览相关
const previewVisible = ref(false)
const previewImage = ref('')
const previewTitle = computed(() => {
	return previewImage.value ? '图片预览' : ''
})

// 上传状态
const uploading = ref(false)

// 拖拽状态
const isDragOver = ref(false)

// 初始化文件列表
const initFileList = () => {
	if (props.modelValue) {
		if (typeof props.modelValue === 'string') {
			// 单图上传，字符串格式
			fileList.value = props.modelValue
				? [
						{
							uid: '-1',
							name: 'image.png',
							status: 'done',
							url: props.modelValue,
						},
					]
				: []
		} else if (Array.isArray(props.modelValue)) {
			// 多图上传，数组格式
			fileList.value = props.modelValue.map((url, index) => ({
				uid: `-${index}`,
				name: `image${index}.png`,
				status: 'done',
				url: url,
			}))
		}
	} else {
		fileList.value = []
	}
}

// 监听外部值变化
watch(
	() => props.modelValue,
	() => {
		initFileList()
	},
	{ immediate: true },
)

// 自定义上传
const customUpload = (options) => {
	const { file, onProgress, onSuccess, onError } = options
	const formData = new FormData()
	formData.append(uploadConfig.filename || 'file', file)

	uploading.value = true

	uploadConfig
		.apiObj(formData, {
			onUploadProgress: (progressEvent) => {
				const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100)
				onProgress({ percent }, file)
			},
		})
		.then((res) => {
			const data = uploadConfig.parseData(res)
			if (data.code === uploadConfig.successCode) {
				onSuccess(data, file)
				message.success('上传成功')
				emit('uploadSuccess', data, file)
			} else {
				onError(new Error(data.msg || '上传失败'))
				message.error(data.msg || '上传失败')
				emit('uploadError', data.msg || '上传失败', file)
			}
		})
		.catch((error) => {
			onError(error)
			message.error('上传失败：' + error.message)
			emit('uploadError', error.message, file)
		})
		.finally(() => {
			uploading.value = false
		})
}

// 上传前校验
const beforeUpload = async (file) => {
	// 文件大小校验
	const maxSizeMB = uploadConfig.maxSize || 10
	const maxSizeBytes = maxSizeMB * 1024 * 1024

	if (file.size > maxSizeBytes) {
		message.error(`图片大小不能超过 ${maxSizeMB}MB`)
		return false
	}

	// 图片尺寸校验
	if (props.minWidth || props.maxWidth || props.minHeight || props.maxHeight) {
		try {
			const dimensions = await getImageDimensions(file)
			const { width, height } = dimensions

			if (props.minWidth && width < props.minWidth) {
				message.error(`图片宽度不能小于 ${props.minWidth}px`)
				return false
			}
			if (props.maxWidth && width > props.maxWidth) {
				message.error(`图片宽度不能大于 ${props.maxWidth}px`)
				return false
			}
			if (props.minHeight && height < props.minHeight) {
				message.error(`图片高度不能小于 ${props.minHeight}px`)
				return false
			}
			if (props.maxHeight && height > props.maxHeight) {
				message.error(`图片高度不能大于 ${props.maxHeight}px`)
				return false
			}
		} catch (error) {
			message.error('图片尺寸校验失败')
			return false
		}
	}

	return true
}

// 获取图片尺寸
const getImageDimensions = (file) => {
	return new Promise((resolve, reject) => {
		const img = new Image()
		const reader = new FileReader()

		reader.onload = (e) => {
			img.src = e.target.result
			img.onload = () => {
				resolve({ width: img.width, height: img.height })
			}
			img.onerror = reject
		}
		reader.onerror = reject
		reader.readAsDataURL(file)
	})
}

// 处理预览
const handlePreview = async (file) => {
	if (!file.url && !file.preview) {
		file.preview = await getBase64(file.originFileObj)
	}
	previewImage.value = file.url || file.preview
	previewVisible.value = true
	emit('preview', file)
}

// 获取Base64
const getBase64 = (file) => {
	return new Promise((resolve, reject) => {
		const reader = new FileReader()
		reader.readAsDataURL(file)
		reader.onload = () => resolve(reader.result)
		reader.onerror = (error) => reject(error)
	})
}

// 处理文件列表变化
const handleChange = ({ fileList: newFileList }) => {
	// 更新文件列表，确保上传成功的文件有正确的 url
	const updatedFileList = newFileList.map((file) => {
		// 如果文件上传成功且有响应数据但没有 url，则设置 url
		if (file.status === 'done' && file.response?.src && !file.url) {
			return {
				...file,
				url: file.response.src,
			}
		}
		return file
	})

	fileList.value = updatedFileList

	// 过滤掉失败的文件
	const validFileList = updatedFileList.filter((file) => file.status !== 'error')

	// 提取成功的文件URL
	const successFiles = validFileList.filter((file) => file.status === 'done' && (file.url || file.response?.src)).map((file) => file.url || file.response?.src)

	// 触发更新事件
	if (props.returnUrl) {
		// 返回URL字符串或数组
		const value = props.maxCount === 1 ? successFiles[0] || '' : successFiles
		emit('update:modelValue', value)
		emit('change', value, validFileList)
	} else {
		// 返回完整文件列表
		emit('update:modelValue', validFileList)
		emit('change', validFileList)
	}
}

// 拖拽相关
const handleDragEnter = (e) => {
	e.preventDefault()
	isDragOver.value = true
}

const handleDragLeave = (e) => {
	e.preventDefault()
	isDragOver.value = false
}

const handleDrop = (e) => {
	e.preventDefault()
	isDragOver.value = false
}

// 取消预览
const handleCancel = () => {
	previewVisible.value = false
}
</script>

<style scoped>
.image-upload {
	width: 100%;
}

.upload-area {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	height: 100%;
}

.upload-icon {
	font-size: 24px;
	color: rgba(0, 0, 0, 0.45);
}

.ant-upload-text {
	margin-top: 8px;
	font-size: 12px;
	color: rgba(0, 0, 0, 0.85);
}

.ant-upload-tip {
	margin-top: 4px;
	font-size: 10px;
	color: rgba(0, 0, 0, 0.45);
	line-height: 1.4;
}

.drag-over {
	border: 2px dashed #1890ff;
	background-color: rgba(24, 144, 255, 0.05);
}

.drag-over .upload-icon {
	color: #1890ff;
}
</style>
