<script setup>
// 常用属性：
//   action - 动作或URL
//   data - 绑定的数据
//   method - 请求方法
//   timeout - 超时时间（秒数）
//   refresh <boolean> - 成功后自动刷新页面
//
// 常用事件：
//   @before - 提交前触发
//   @success - 成功时触发
//   @fail - 失败时触发
//   @error - 错误时触发
//   @done - 完成时触发（几乎一定会触发）
//   @progress - 上传进度

// TODO 实现 data-tea-confirm（confirm="确认语"）

import {getCurrentInstance, inject, provide, ref} from "vue";
import useUtils from "@/utils/utils.js";
import CSRFToken from "@/components/common/CSRFToken.vue";

const props = defineProps(["action", "data", "method", "timeout", "refresh", "onSuccess", "onFail", "onProgress", "disableCSRF"])
const emits = defineEmits(["before", "progress", "success", "fail", "error", "done"])
const disableCSRF = ref(props.disableCSRF)
if (typeof disableCSRF.value === "string") {
	disableCSRF.value = true
}

let autoData = false
let realData = ref(props.data)
if (props.data === undefined) {
	autoData = true
	realData.value = {
		csrfToken: ""
	}
}

provide("postFormData", realData.value)
provide("$postForm", getCurrentInstance())

const Tea = inject("$Tea")
const utils = useUtils()

const formRef = ref()

const onSubmit = e => {
	submit()
}

const submit = () => {
	if (typeof props.method === "string" && props.method.length > 0 && props.method.toLowerCase() != "post") {
		console.error("'PostForm' says: invalid method: '" + props.method + "'")
		return
	}

	// emit 'before' event
	emits("before")

	let action = props.action
	if (typeof action !== "string") {
		action = "$"
	}

	const actionObject = Tea.action(action)
	if (!autoData) {
		let data = props.data
		if (data == null || typeof data !== "object") {
			data = {}
		}
		actionObject.params(data)
	} else {
		const formData = new FormData(formRef.value)
		for (const k in realData.value) {
			const v = realData.value[k]

			// process files
			if (v instanceof Array) {
				let processed = false
				for (const v2 of v) {
					if (v2 instanceof File) {
						formData.append(k, v2)
						processed = true
					}
				}
				if (processed) {
					continue
				}
			}

			formData.set(k, v)
		}
		actionObject.form(formData)
	}

	// apply timeout
	if (typeof props.timeout === "number") {
		actionObject.timeout(props.timeout)
	} else if (typeof props.timeout === "string") {
		const numberTimeout = parseFloat(props.timeout)
		if (!isNaN(numberTimeout)) {
			actionObject.timeout(numberTimeout)
		}
	}

	// success function
	if (typeof props.onSuccess === "function") {
		actionObject.success(resp => {
			emits("success", resp)
		})
	}

	// fail function
	if (typeof props.onFail === "function") {
		actionObject.fail(resp => {
			emits("fail", resp)
		})
	}

	// progress function
	if (typeof props.onProgress === "function") {
		actionObject.progress((loaded, total, event) => {
			emits("progress", loaded, total, event)
		})
	}

	actionObject
		.error((resp) => {
			console.error(resp.message)
			emits("error", resp)
		})
		.done(() => {
			emits("done")
		})
		.alert((message, callback, success) => {
			if (typeof message === "string" && message.length > 0) {
				if (success) {
					utils.success(message, () => {
						if (typeof callback === "function") {
							callback()
						} else {
							if (props.refresh || props.refresh === "") {
								Tea.reload()
							}
						}
					})
				} else {
					utils.warn(message, () => {
						if (typeof callback === "function") {
							callback()
						} else {
							if (props.refresh || props.refresh === "") {
								Tea.reload()
							}
						}
					})
				}
			}
		})
		.post()
}

defineExpose({
	get rawForm() {
		return formRef.value
	},
	submit() {
		setTimeout(submit)
	}
})
</script>

<template>
	<form @submit.prevent="onSubmit" method="post" ref="formRef">
		<slot></slot>

		<CSRFToken v-if="realData != null && !disableCSRF" v-model="realData.csrfToken"></CSRFToken>
	</form>
</template>

<style scoped>

</style>