import type { Ref } from 'vue'
import { inject, provide, ref } from 'vue'
import { getNestedValue } from '../../utils'

export const FORM_STATE_KEY = Symbol('bagelFormState')

export interface BagelFormState<T> {
	data: Ref<T>
	getFieldData: (path?: string) => any
	updateField: (path: string, value: any) => void
	isDirty: Ref<boolean>
}

// Helper function to safely clone objects without circular references
function safeClone(obj: any): any {
	if (obj === null || typeof obj !== 'object') { return obj }

	const seen = new WeakSet()
	return JSON.parse(JSON.stringify(obj, (key, value) => {
		if (typeof value === 'object' && value !== null) {
			if (seen.has(value)) {
				return undefined // Remove circular reference
			}
			seen.add(value)
		}
		return value
	}))
}

export function provideBagelFormState<T>(initialData: T) {
	const data = ref(initialData) as Ref<T>
	const isDirty = ref(false)

	const getFieldData = (path?: string) => getNestedValue(data.value, path, '')

	const updateField = (path: string, value: any) => {
		const keys = path.split(/[.[]/)

		// Initialize the root if it's not an object
		if (typeof data.value !== 'object' || data.value === null) {
			data.value = {} as T
		}

		let current = data.value as any

		// Build the path, ensuring each level is an object
		for (let i = 0; i < keys.length - 1; i++) {
			const key = keys[i]
			if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
				current[key] = {}
			}
			current = current[key]
		}

		// Safely clone the value to remove circular references
		const safeValue = safeClone(value)
		current[keys[keys.length - 1]] = safeValue
		isDirty.value = true
	}

	const state: BagelFormState<T> = {
		data,
		getFieldData,
		updateField,
		isDirty
	}

	provide(FORM_STATE_KEY, state)
	return state
}

export function useBagelFormState<T>(injectionKey: typeof FORM_STATE_KEY = FORM_STATE_KEY): BagelFormState<T> {
	const state = inject<BagelFormState<T>>(injectionKey)
	if (!state) { throw new Error('BagelFormState must be provided') }
	return state
}
