<template>
	<view class="l-upload" ref="uploadRef" :style="styles">
		<view class="l-upload__item" v-for="(file, index) in customFiles" :style="[itemStyle]" :key="file.url">
			<!-- #ifndef APP || WEB -->
			<view class="l-upload__item-inner" :style="[innerStyle]">
			<!-- #endif -->
				<slot name="file" :file="file" :index="index">
					<image class="l-upload__image" v-if="file.type == 'image'" :src="file.url" :data-file="file"
						:mode="imageFit" @click="onProofTap(index)" />
					<video class="l-upload__image" v-if="file.type == 'video'" :src="file.url" :data-file="file"
						 :autoplay="false" objectFit="contain" @click="onFileClick(index)" />
					<view class="l-upload__progress-mask" v-if="file.status != null && file.status != 'done'" :data-file="file" @click="onFileClick(index)">
						<template v-if="file.status == 'loading'">
							<l-loading class="l-upload__progress-loading" size="24px" color="white" />
							<text class="l-upload__progress-text" v-if="file.percent != null">{{file.percent}}%</text>
							<text class="l-upload__progress-text" v-else>{{loadingText}}</text>
						</template>
						<l-icon v-else class="l-upload__progress-icon"
							:name="file.status == 'reload' ? 'refresh' : 'close-circle'" size="48rpx" aria-hidden />
						<text v-if="file.status == 'reload' || file.status == 'failed'" class="l-upload__progress-text">
							{{file.status == 'reload' ? reloadText : failedText}}
						</text>
					</view>
					<view class="l-upload__delete-btn" aria-role="button" aria-label="删除" :data-index="index"
						@click="onDelete(index)">
						<l-icon name="close" size="16px" color="#fff" />
					</view>
				</slot>
			<!-- #ifndef APP || WEB -->
			</view>
			<!-- #endif -->
		</view>
		<!-- #ifdef APP || WEB -->
		<view class="l-upload__item l-upload__item--add" 
			:class="{'l-upload__item--disabled':disabled}"
			v-show="!multiple ? customFiles.length == 0: true"
			:style="[itemStyle,  addBgColor!=null ? {background: addBgColor}: '']" 
			aria-label="上传"
			@click="onAddTap">
				<slot>
					<l-icon class="l-upload__add-icon" :size="uploadIconSize" :name="uploadIcon" />
				</slot>
		</view>
		<!-- #endif -->
		<!-- #ifndef APP || WEB -->
		<view class="l-upload__item l-upload__item--add"
			:class="{'l-upload__item--disabled':disabled}"
			v-show="!multiple ? customFiles.length == 0: true"
			:style="[itemStyle]" 
			aria-label="上传"
			@click="onAddTap">
			<view class="l-upload__item-inner" :style="[innerStyle,  addBgColor!=null ? {background: addBgColor}: '']">
				<slot>
					<l-icon class="l-upload__add-icon" :size="uploadIconSize" :name="uploadIcon" />
				</slot>
			</view>
		</view>
		<!-- #endif -->
	</view>
</template>
<script lang="uts" setup>
	import { unitConvert } from '@/uni_modules/lime-shared/unitConvert'
	import { chooseFiles } from './utils'
	import { UploadProps, UploadFile, ChooseFileOptions } from './type';
	
	defineSlots<{
		 file(props : { file : UploadFile, index: number }) : any,
	}>()
	const emits = defineEmits(['fail', 'remove', 'success', 'click', 'add', 'update:modelValue'])
	const props = withDefaults(defineProps<UploadProps>(), {
		imageFit: 'aspectFill',
		disablePreview: false,
		autoUpload: false,
		multiple: true,
		max: 0,
		disabled: false,
		mediaType: 'image',
		sizeType: ['original', 'compressed'],
		sourceType: ['album', 'camera'],
		uploadIcon: 'camera',
		loadingText: '上传中...',
		reloadText: '重新上传',
		failedText: '上传失败',
		mode: 'grid'
	})

	const transformFiles = (it : any) : UploadFile => {
		// #ifdef APP-ANDROID
		if(it instanceof UploadFile) {
			return it
		}
		// #endif
		
		// #ifndef APP-ANDROID || APP-IOS
		const file:UTSJSONObject = {...it}
		// #endif
		// #ifdef APP-ANDROID || APP-IOS
		const file = UTSJSONObject.assign({}, it as UTSJSONObject) //it as UTSJSONObject
		// #endif
		return {
			url: file.getString('url') ?? '',
			path: file.getString('path'),
			name: file.getString('name'),
			thumb: file.getString('thumb'),
			size: file.getNumber('size'),
			type: file.getString('type'),
			percent: file.getNumber('percent'),
			status: file.getString('status') ?? 'done',
		} as UploadFile
	}
	const customFiles = ref<UploadFile[]>((props.modelValue ?? props.defaultFiles)?.map(transformFiles) ?? []);

	const listWidth = ref(0);
	const uploadRef = ref<UniElement | null>(null);
	const styles = computed(() : Map<string, any>=> {
		const style = new Map<string, any>();
		const gutter = unitConvert(props.gutter ?? 8) / 2 * -1
		style.set('margin-left', `${gutter}px`)
		style.set('margin-right', `${gutter}px`)
		style.set('margin-top', `${gutter}px`)
		return style
	})
	const itemStyle = computed(() : Map<string, any> => {
		const style = new Map<string, any>();
		
		// const gridWidth = unitConvert(props.gridWidth ?? 80)
		const gutter = unitConvert(props.gutter ?? 8) / 2
		let column = props.column ?? 4;
		
		if(props.gridWidth != null || props.column != null) {
			// #ifdef APP || WEB
			const width = listWidth.value / column - gutter * 2.0275;// ios 计算精度导致不均分
			style.set('width', props.gridWidth ?? `${width}px`)
			// #endif
			// #ifndef APP || WEB
			style.set('width', props.gridWidth ?? `${100 / column}%`)
			// #endif
		}
		
		// #ifdef APP || WEB
		if(props.gridHeight != null){
			style.set('height', props.gridHeight!)
		}
		style.set('margin', `${gutter}px`)
		if(props.gridBgColor != null){
			style.set('background', props.gridBgColor!)
		}
		// #endif
		return style
	})
	// #ifndef APP || WEB
	const innerStyle = computed((): Map<string, any> => {
		const style = new Map<string, any>();
		
		const gutter = unitConvert(props.gutter ?? 8) / 2
		style.set('margin', `${gutter}px`)
		if(props.gridBgColor != null){
			style.set('background', props.gridBgColor!)
		}
		if(props.gridHeight != null){
			style.set('height', props.gridHeight!)
		}
		return style
	})
	// #endif
	
	const onFileClick = (index : number) => {
		const file = customFiles.value[index]
		emits('click', { file })
	}
	const onProofTap = (index : number) => {
		onFileClick(index);
		if (props.disablePreview) return
		uni.previewImage({
			urls: customFiles.value.filter((file) : boolean => file.percent != -1).map((file) : string => file.url),
			current: index
		});
	}
	const onDelete = (index : number) => {
		const delFile = customFiles.value[index]
		customFiles.value = customFiles.value.filter((file, i) : boolean => index != i)
		emits('remove', { index, file: delFile })
	}
	
	let last:number// = 0
	const upload = (files: UploadFile[]) => {
		if(!props.autoUpload || props.action == null || props.action!.length < 5) return
		if(props.action == 'uniCloud') {
			let uploadImgs:Promise<UniCloudUploadFileResult>[] = [];
			files.forEach((file, index) =>{
				// props.beforeRead(file).then((res)=>{})
				
				const promise = new Promise<UniCloudUploadFileResult>((resolve, reject) =>{
					uniCloud.uploadFile({
						filePath: file.url,
						cloudPath: file.name!.substring(file.name!.lastIndexOf('.')),
						onUploadProgress: (res)=>{
							file.status = 'loading'
							file.percent = Math.floor(res.loaded / res.total * 100)
						},
					}).then(res=>{
						file.path = res.fileID;
						file.status = 'done'
						resolve(res)
					}).catch(err=>{
						file.status = 'failed'
						reject(err)
					})
				})
				uploadImgs.push(promise as Promise<UniCloudUploadFileResult>)
			})
			
			Promise.all(uploadImgs).then(res =>{
				emits('success', res)
			}).catch(err => {
				emits('fail', err)
			})
		} else {
			let uploadImgs:Promise<UploadFileSuccess>[] = [];
			files.forEach((file, index) =>{
				const promise = new Promise<UploadFileSuccess>((resolve, reject) =>{
					const task = uni.uploadFile({
						url: props.action!,
						filePath: file.url,
						name: file.name,
						formData: props.formData,
						header: props.headers,
						success: (res) => {
							file.status = 'done'
							if(res.statusCode == 200) {
								if(typeof res.data == 'string') {
									try{
										const data = JSON.parse<UTSJSONObject>(res.data);
										const url = data?.getString('url');
										if(url != null) {
											file.path = url
										}
									}catch(e){
										//TODO handle the exception
									}
								}
							}
							resolve(res)
						},
						fail(err) {
							file.status = 'failed'
							reject(err)
						}
					});
					task.onProgressUpdate((res) => {
						file.status = 'loading'
						file.percent = res.progress
					});
				})
				uploadImgs.push(promise as Promise<UploadFileSuccess>)
			})
			Promise.all(uploadImgs).then(res =>{
				emits('success', res)
			}).catch(err => {
				emits('fail', err)
			})
		}
	}
	
	const customLimit = computed(() : number => props.max == 0 ? 20 : props.max - customFiles.value.length)
	const onAddTap = () => {
		if (props.disabled) return;
		chooseFiles({
			mediaType: props.mediaType,
			count: customLimit.value,
			sourceType: props.sourceType,
			sizeType: props.sizeType,
			sizeLimit: props.sizeLimit
		} as ChooseFileOptions).then((files) =>{
			last = customFiles.value.length
			customFiles.value = customFiles.value.concat(files) 
			// @ts-ignore
			const _files = customFiles.value.filter((it, i):boolean => i > last - 1)
			upload(_files)
			emits('add', _files)
		})
	}

	
	
	
	const stop = watch(customFiles, (v : UploadFile[]) => {
		emits('update:modelValue', v)
	})
	
	const stopValue = watch(():(UTSJSONObject[] | null) => props.modelValue, (v: UTSJSONObject[]|null)=>{
		if(v != null && v.length != customFiles.value.length){
			customFiles.value = v.map(transformFiles) 
		}
	})
	
	defineExpose({
		remove: onDelete
	})
	
	// #ifdef APP || WEB
	const resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
		listWidth.value = entries[0].target.getBoundingClientRect().width
	})
	
	onMounted(() => {
		nextTick(() => {
			listWidth.value = uploadRef.value?.getBoundingClientRect().width ?? 0;
			resizeObserver.observe(uploadRef.value!)
		})
	})
	// #endif
	
	onUnmounted(() => {
		stop()
		// #ifdef APP
		resizeObserver.disconnect()
		// #endif
	})
</script>
<style lang="scss">
	@import './index';
</style>