<script setup lang="ts" generic="T extends {[key:string]:any}, P extends Path<T>">
import type { Field, BglFormSchemaT, Path, BaseBagelField } from '@bagelink/vue'
import type { MaybeRefOrGetter } from 'vue'
import { onMounted, watch, ref, computed, toValue } from 'vue'
import { useSchemaField } from '../../composables/useSchemaField'
import { getNestedValue } from '../../utils'

const props = withDefaults(defineProps<{
	modelValue?: T
	schema?: MaybeRefOrGetter<BglFormSchemaT<T>>
	tag?: 'form' | 'template'
	class?: string
	onSubmit?: (data: T) => Promise<unknown> | unknown
}>(), {
	modelValue: undefined,
	schema: undefined,
	tag: 'form',
	class: ''
})

const emit = defineEmits<{
	(e: 'update:modelValue', value: T): void
}>()

// Clone helper
const clone = <T>(obj: T): T => !obj ? obj : JSON.parse(JSON.stringify(obj))

// Form state
const form = ref<HTMLFormElement>()
const formData = ref<T>(clone(props.modelValue ?? {}) as T)
const initialFormData = ref<T>(clone(props.modelValue ?? {}) as T)
const formState = ref<'success' | 'error' | 'idle' | 'submitting'>('idle')
const resolvedSchema = computed(() => toValue(props.schema))

const isDirty = computed(() => {
	try {
		return JSON.stringify(formData.value) !== JSON.stringify(initialFormData.value)
	} catch {
		return false
	}
})

// Initialize on mount
onMounted(() => {
	if (props.modelValue) initialFormData.value = clone(props.modelValue)
})

// Watch for model changes
watch(() => props.modelValue, (val) => {
	if (val !== undefined) formData.value = clone(val)
}, { immediate: true, deep: true })

// Update form data
function updateFormData({ fieldId, value }: { fieldId?: string, value: any }) {
	const keys = fieldId?.split('.') || []
	const newData = clone(formData.value) as { [key: string]: any }
	let current = newData

	for (let i = 0; i < keys.length - 1; i++) {
		const key = keys[i]
		if (!current[key] || typeof current[key] !== 'object') current[key] = {}
		current = current[key]
	}

	current[keys[keys.length - 1]] = value
	formData.value = newData as T
	emit('update:modelValue', formData.value)
}

// Form validation
const validateForm = () => form.value?.reportValidity() ?? false
const checkValidity = () => form.value?.checkValidity() ?? false
const formError = ref<Error>()
// Form submission
async function handleSubmit() {
	try {
		const valid = validateForm()
		if (!valid) return
		if (formState.value === 'submitting') return
		formState.value = 'submitting'
		await props.onSubmit?.(formData.value)
		initialFormData.value = clone(formData.value)
		formState.value = 'success'
		window.parent.postMessage({ type: 'BAGEL_FORM_SUCCESS', data: JSON.stringify(formData.value) }, '*')
	} catch (error) {
		formState.value = 'error'
		formError.value = error as Error
		console.error('Submit error:', formError.value)
	}
}

// Field renderingks
const { renderField } = useSchemaField<T, P>({
	mode: 'form',
	getFormData: () => ({
		...formData.value,
		get: (path: string) => getNestedValue(formData.value, path, '')
	}),
	onUpdateModelValue: (field, value) => {
		if (!field.id) return

		updateFormData({ fieldId: field.id, value })
		field.onUpdate?.(value, formData.value)
	}
})

const renderSchemaField = (field: Field<T>) => renderField(field as BaseBagelField<T, P>)

// Add new method to handle slot input changes
function handleSlotInputChange(event: Event) {
	const input = event.target as HTMLInputElement
	if (input.name) {
		const value = input.type === 'checkbox' ? input.checked : input.value
		updateFormData({ fieldId: input.name, value })
	}
}

defineExpose({ form, isDirty, validateForm, checkValidity })
</script>

<template>
	<template v-if="formState !== 'success' || !$slots.success">
		<form
			v-if="props.tag === 'form'" ref="form"
			:class="props.class"
			@submit.prevent="handleSubmit"
			@input="handleSlotInputChange"
		>
			<!-- Render fields -->
			<template v-if="resolvedSchema">
				<Component
					:is="renderSchemaField(field)"
					v-for="field in resolvedSchema"
					:key="field.id"
				/>
			</template>

			<!-- Default slot -->
			<slot v-else />
			<!-- Submit slot -->
			<slot
				name="submit"
				:submit="handleSubmit"
				:isDirty="isDirty"
				:validateForm="validateForm"
				:formState="formState"
			/>
		</form>

		<!-- Template mode -->
		<template v-else>
			<template v-if="resolvedSchema">
				<component
					:is="renderSchemaField(field)"
					v-for="field in resolvedSchema"
					:key="field.id"
					:class="props.class"
				/>
			</template>
		</template>
	</template>

	<!-- Success/error slots -->
	<slot v-if="formState === 'success'" name="success" />
	<slot v-if="formState === 'error'" name="error" :error="formError" />
</template>
