<template>
	<!-- 封面模式 -->
	<div v-if="mode === 'cover'" class="file-upload cover">
		<!-- 已上传的封面 -->
		<div v-if="currentCoverImg" class="image-wrapper">
			<img :src="currentCoverImg" class="image" :class="coverImageClass" @load="handleCoverImageLoad"
				alt="封面图片" />
			<div class="actions">
				<ElButton type="danger" size="small" @click="handleRemoveCover">
					×
				</ElButton>
			</div>
		</div>

		<!-- 上传区域 -->
		<div v-else class="area cover" :class="{ dragover: isDragover, disabled: uploading }" @click="triggerFileInput"
			@drop.prevent="handleDrop" @dragover.prevent="handleDragOver" @dragleave.prevent="handleDragLeave">
			<div v-if="!uploading" class="content">
				<div class="icon">
					<div class="plus"></div>
				</div>
				<div class="text">点击上传封面</div>
				<div class="tip">
					{{ `支持 ${getAcceptText()} 格式，最大 ${(maxSize / 1024 / 1024).toFixed(0)}MB` }}
				</div>
			</div>
			<div v-if="uploading" class="loading">
				<div class="spinner">⟳</div>
				<div>上传中...</div>
			</div>
		</div>

		<input ref="fileInputRef" type="file" :accept="accept" @change="handleFileSelect" hidden />
	</div>

	<!-- 弹窗模式 -->
	<div v-else-if="mode === 'dialog'" class="file-upload dialog">
		<ElButton @click="showDialog = true" type="primary">
			{{ buttonText || '上传文件' }}
		</ElButton>

		<ElDialog 
			v-model="showDialog" 
			:title="title || '文件上传'"
			width="500px"
			:before-close="handleDialogClose"
		>
			<div class="dialog-content">
				<div class="area" :class="{ dragover: isDragover, disabled: uploading }" @click="triggerFileInput"
					@drop.prevent="handleDrop" @dragover.prevent="handleDragOver"
					@dragleave.prevent="handleDragLeave">
					<div class="icon">
						<div class="plus"></div>
					</div>
					<div class="text">
						将文件拖到此处，或<span class="link">点击上传</span>
					</div>
					<div class="tip">{{ getUploadTip() }}</div>
				</div>

				<input ref="fileInputRef" type="file" :accept="accept" :multiple="maxCount > 1"
					@change="handleFileSelect" hidden />

				<!-- 文件列表 -->
				<div v-if="selectedFiles.length > 0" class="files">
					<div v-for="(file, index) in selectedFiles" :key="index" class="file">
						<div class="preview">
							<img v-if="isImageFile(file)" :src="getFilePreviewUrl(file)" class="thumb" />
							<span v-else class="type-icon">{{ getFileTypeIcon(file) }}</span>
						</div>

						<div class="info">
							<div class="name">{{ file.name }}</div>
							<div class="size">{{ formatFileSize(file.size) }}</div>
						</div>

						<div class="actions">
							<ElButton type="danger" size="small" @click="removeFile(index)" :disabled="uploading">
								删除
							</ElButton>
						</div>
					</div>
				</div>
			</div>

			<template #footer>
				<div class="dialog-footer">
					<ElButton @click="closeDialog" :disabled="uploading">取消</ElButton>
					<ElButton type="primary" @click="uploadFiles" :loading="uploading"
						:disabled="selectedFiles.length === 0">
						{{ uploading ? '上传中...' : '确定上传' }}
					</ElButton>
				</div>
			</template>
		</ElDialog>
	</div>

	<!-- 内容模式 -->
	<div v-else class="file-upload content">
		<div class="container">
			<!-- 上传区域 -->
			<div v-if="!uploading && currentFileList.length < maxCount" class="area" :class="{ dragover: isDragover }" @click="triggerFileInput"
				@drop.prevent="handleDrop" @dragover.prevent="handleDragOver" @dragleave.prevent="handleDragLeave">
				<div class="icon">
					<div class="plus"></div>
				</div>
				<div class="text">
					将文件拖到此处，或<span class="link">点击上传</span>
				</div>
				<div class="tip">{{ getUploadTip() }}</div>
			</div>

			<!-- 上传中状态 -->
			<div v-if="uploading" class="area disabled">
				<div class="loading">
					<div class="spinner">⟳</div>
					<div>上传中...</div>
				</div>
			</div>

			<input ref="fileInputRef" type="file" :accept="accept" :multiple="maxCount > 1" @change="handleFileSelect"
				hidden />

			<!-- 已上传文件列表 -->
			<div v-if="currentFileList.length > 0" class="files">
				<div v-for="(file, index) in currentFileList" :key="file.fileId || index" class="file">
					<div class="preview">
						<img v-if="isUploadedImageFile(file)" :src="getUploadedFileUrl(file)" class="thumb" />
						<span v-else class="type-icon">{{ getUploadedFileTypeIcon(file) }}</span>
					</div>

					<div class="info">
						<div class="name">{{ file.name || file.fileName || '未知文件' }}</div>
						<div class="size">{{ file.size ? formatFileSize(file.size) : '' }}</div>
					</div>

					<div class="actions">
						<ElButton type="danger" size="small" @click="removeUploadedFile(index)">
							删除
						</ElButton>
					</div>
				</div>
			</div>
		</div>
	</div>

</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { FILE_DOWNLOAD_URL } from '@/config'
import { fileAPI } from '@/api/file/file'
import type { TbSysFileDTO } from '@/types/file/file'
import { ElButton, ElDialog } from 'element-plus'
import {
	formatFileSize,
	isImageFile,
	getFileTypeIcon,
	isValidFileType,
	getFilePreviewUrl
} from '@/utils/file'

interface Props {
	mode: 'cover' | 'dialog' | 'content'
	coverImg?: string
	fileList?: TbSysFileDTO[]
	accept?: string
	maxSize?: number
	maxCount?: number
	title?: string
	buttonText?: string
	// 是否自动上传，默认 true，设为 false 时需要手动调用 uploadFiles
	autoUpload?: boolean
	// 自定义上传函数，如果提供则使用外部实现，否则使用默认上传逻辑
	customUpload?: (files: File[]) => Promise<TbSysFileDTO[] | void>
}

const props = withDefaults(defineProps<Props>(), {
	coverImg: '',
	fileList: () => [],
	accept: '',
	maxSize: 10 * 1024 * 1024,
	maxCount: 10,
	title: '文件上传',
	buttonText: '上传文件',
	autoUpload: true,
	customUpload: undefined
})

const emit = defineEmits<{
	'update:coverImg': [value: string]
	'update:fileList': [value: TbSysFileDTO[]]
	'upload-success': [files: TbSysFileDTO[]]
	'upload-error': [error: string]
	// 文件准备就绪事件，当使用自定义上传时触发，传递文件列表供外部处理
	'files-ready': [files: File[]]
}>()

// 响应式数据
const showDialog = ref(false)
const uploading = ref(false)
const selectedFiles = ref<File[]>([])
const isDragover = ref(false)
const coverImageClass = ref('')

// 内部文件列表（包含本地预览URL）
interface InternalFile extends TbSysFileDTO {
	localPreviewUrl?: string  // 本地预览URL，避免重新下载
}
const internalFileList = ref<InternalFile[]>([])

// 文件输入引用
const fileInputRef = ref<HTMLInputElement>()

// 计算属性
const currentCoverImg = computed({
	get: () => props.coverImg,
	set: (value: string) => emit('update:coverImg', value)
})

// 当前文件列表（优先使用内部列表，否则使用props）
const currentFileList = computed(() => {
	return internalFileList.value.length > 0 ? internalFileList.value : props.fileList
})

// 判断已上传文件是否为图片
const isUploadedImageFile = (file: InternalFile): boolean => {
	if (file.localPreviewUrl) return true  // 有本地预览说明是图片
	const mimeType = file.mimeType || file.extension || ''
	return mimeType.includes('image') || /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(file.name || '')
}

// 获取已上传文件的显示URL（优先使用本地预览）
const getUploadedFileUrl = (file: InternalFile): string => {
	if (file.localPreviewUrl) return file.localPreviewUrl
	if (file.url) return file.url
	if (file.fileId) return `${FILE_DOWNLOAD_URL}${file.fileId}`
	return ''
}

// 获取已上传文件的类型图标
const getUploadedFileTypeIcon = (file: InternalFile): string => {
	const ext = file.extension || file.name?.split('.').pop() || ''
	const iconMap: Record<string, string> = {
		pdf: '📄',
		doc: '📝',
		docx: '📝',
		xls: '📊',
		xlsx: '📊',
		ppt: '📽️',
		pptx: '📽️',
		zip: '📦',
		rar: '📦',
		txt: '📃'
	}
	return iconMap[ext.toLowerCase()] || '📎'
}

// 删除已上传的文件
const removeUploadedFile = (index: number) => {
	const newList = [...internalFileList.value]
	const removed = newList.splice(index, 1)[0]
	// 释放本地预览URL
	if (removed?.localPreviewUrl) {
		URL.revokeObjectURL(removed.localPreviewUrl)
	}
	internalFileList.value = newList
	emit('update:fileList', newList)
}

// 触发文件输入
const triggerFileInput = () => {
	fileInputRef.value?.click()
}

// 拖拽相关
const handleDragOver = (event: DragEvent) => {
	event.preventDefault()
	isDragover.value = true
}

const handleDragLeave = () => {
	isDragover.value = false
}

const handleDrop = (event: DragEvent) => {
	event.preventDefault()
	isDragover.value = false
	if (uploading.value) return

	const files = event.dataTransfer?.files
	if (files && files.length > 0) {
		addFiles(Array.from(files))

		// cover模式和content模式下拖拽文件后立即上传
		if (props.autoUpload && (props.mode === 'cover' || props.mode === 'content')) {
			uploadFiles()
		}
	}
}

// 处理封面图片加载
const handleCoverImageLoad = (event: Event) => {
	const img = event.target as HTMLImageElement
	const width = img.naturalWidth
	const height = img.naturalHeight

	if (width > height) {
		coverImageClass.value = 'horizontal'
	} else {
		coverImageClass.value = 'vertical'
	}
}

// 删除封面
const handleRemoveCover = () => {
	emit('update:coverImg', '')
	coverImageClass.value = ''
	selectedFiles.value = []
}

// 获取接受文件类型文本
const getAcceptText = (): string => {
	if (!props.accept || props.accept === '*/*') return '所有'
	if (props.accept.includes('image')) return '图片'
	return props.accept
}

// 获取上传提示文本
const getUploadTip = (): string => {
	const acceptText = getAcceptText()
	const sizeText = (props.maxSize / 1024 / 1024).toFixed(0)
	return `支持 ${acceptText} 格式，单个文件不超过 ${sizeText}MB`
}

// 添加文件
const addFiles = (files: File[]) => {
	files.forEach(file => {
		// 验证文件大小
		if (file.size > props.maxSize) {
			emit('upload-error', `文件 ${file.name} 大小超过 ${(props.maxSize / 1024 / 1024).toFixed(0)}MB`)
			return
		}

		// 验证文件类型
		if (props.accept && !isValidFileType(file, props.accept)) {
			emit('upload-error', `文件 ${file.name} 类型不符合要求`)
			return
		}

		// 检查是否重复（包括已上传的文件）
		if (selectedFiles.value.some(f => f.name === file.name && f.size === file.size)) {
			emit('upload-error', `文件 ${file.name} 已添加`)
			return
		}
		if (internalFileList.value.some(f => f.name === file.name)) {
			emit('upload-error', `文件 ${file.name} 已上传`)
			return
		}

		// 如果不允许多选或封面模式，清空之前的文件
		if (props.maxCount === 1 || props.mode === 'cover') {
			selectedFiles.value = [file]
		} else {
			// 检查数量限制（已上传 + 待上传）
			const totalCount = internalFileList.value.length + selectedFiles.value.length
			if (totalCount >= props.maxCount) {
				emit('upload-error', `最多只能上传 ${props.maxCount} 个文件`)
				return
			}
			selectedFiles.value.push(file)
		}
	})
}

// 处理文件选择
const handleFileSelect = (event: Event) => {
	const target = event.target as HTMLInputElement
	const files = Array.from(target.files || [])

	if (files.length === 0) return

	addFiles(files)

	// 清空 input，允许重复选择同一文件
	target.value = ''

	// cover模式和content模式下，如果 autoUpload 为 true 则立即上传
	if (props.autoUpload && (props.mode === 'cover' || props.mode === 'content')) {
		uploadFiles()
	}
}

// 移除文件
const removeFile = (index: number) => {
	selectedFiles.value.splice(index, 1)
}

// 关闭弹窗
const closeDialog = () => {
	showDialog.value = false
	selectedFiles.value = []
	isDragover.value = false
}

// 处理弹窗关闭前的回调
const handleDialogClose = (done: () => void) => {
	if (uploading.value) {
		return
	}
	closeDialog()
	done()
}

// 上传文件
const uploadFiles = async () => {
	if (selectedFiles.value.length === 0) return

	// 如果提供了自定义上传函数，则使用外部实现
	if (props.customUpload) {
		uploading.value = true
		try {
			// 触发 files-ready 事件，传递文件列表
			emit('files-ready', [...selectedFiles.value])
			
			// 调用自定义上传函数
			const result = await props.customUpload([...selectedFiles.value])
			
			// 如果自定义函数返回了文件列表，触发上传成功事件
			if (result && result.length > 0) {
				emit('upload-success', result)
				emit('update:fileList', [...props.fileList, ...result])
				
				if (props.mode === 'cover' && result[0]?.url) {
					emit('update:coverImg', result[0].url)
				}
			}
			
			// 清空文件列表
			selectedFiles.value = []
			if (props.mode === 'dialog') {
				closeDialog()
			}
		} catch (error) {
			console.error('自定义上传失败:', error)
			emit('upload-error', error instanceof Error ? error.message : '上传失败，请重试')
		} finally {
			uploading.value = false
		}
		return
	}

	// 默认上传逻辑
	uploading.value = true
	const uploadedFilesList: InternalFile[] = []
	
	// 为图片文件创建本地预览URL的映射
	const localPreviewMap = new Map<string, string>()
	selectedFiles.value.forEach(file => {
		if (file.type.startsWith('image/')) {
			localPreviewMap.set(file.name, URL.createObjectURL(file))
		}
	})

	try {

		if (selectedFiles.value.length === 1) {
			const file = selectedFiles.value[0]
			const result = await fileAPI.uploadFile({
				file: file,
				module: props.mode,
				optsn: ''
			})

			if (result.success && result.data) {
				// 添加本地预览URL
				const uploadedFile: InternalFile = {
					...result.data,
					localPreviewUrl: localPreviewMap.get(file.name)
				}
				uploadedFilesList.push(uploadedFile)

				if (props.mode === 'cover' && result.data.url) {
					emit('update:coverImg', result.data.url)
				}
			} else {
				// 释放预览URL
				localPreviewMap.forEach(url => URL.revokeObjectURL(url))
				emit('upload-error', result.message || '上传失败，请重试')
				return
			}
		} else {
			const result = await fileAPI.batchUploadFiles({
				files: selectedFiles.value,
				module: props.mode,
				optsn: ''
			})

			if (result.success) {
				const files = result.dataList || []
				// 为每个上传成功的文件添加本地预览URL
				files.forEach(f => {
					const uploadedFile: InternalFile = {
						...f,
						localPreviewUrl: localPreviewMap.get(f.name || '')
					}
					uploadedFilesList.push(uploadedFile)
				})
			} else {
				// 释放预览URL
				localPreviewMap.forEach(url => URL.revokeObjectURL(url))
				emit('upload-error', result.message || '上传失败，请重试')
				return
			}
		}

		// 上传成功 - 更新内部文件列表
		if (uploadedFilesList.length > 0) {
			// content模式：追加到内部列表
			if (props.mode === 'content') {
				internalFileList.value = [...internalFileList.value, ...uploadedFilesList]
			}
			emit('upload-success', uploadedFilesList)
			emit('update:fileList', [...props.fileList, ...uploadedFilesList])
		}

		// 清空待上传文件列表
		selectedFiles.value = []
		if (props.mode === 'dialog') {
			closeDialog()
		}

	} catch (error) {
		console.error('上传失败:', error)
		emit('upload-error', '上传失败，请重试')
	} finally {
		uploading.value = false
	}
}

// 暴露方法和状态供外部使用
defineExpose({
	// 当前选中的文件列表
	selectedFiles,
	// 已上传的文件列表
	internalFileList,
	// 上传状态
	uploading,
	// 弹窗显示状态
	showDialog,
	// 手动触发文件选择
	triggerFileInput,
	// 手动添加文件
	addFiles,
	// 移除指定文件
	removeFile,
	// 清空所有文件（包括已上传的）
	clearFiles: () => { 
		// 释放本地预览URL
		internalFileList.value.forEach(f => {
			if (f.localPreviewUrl) {
				URL.revokeObjectURL(f.localPreviewUrl)
			}
		})
		selectedFiles.value = []
		internalFileList.value = []
	},
	// 手动触发上传
	uploadFiles,
	// 关闭弹窗
	closeDialog,
	// 打开弹窗
	openDialog: () => { showDialog.value = true }
})
</script>

<style lang="scss" scoped>
@import url('./FileUpload.scss');
</style>