<script setup lang="ts" generic="T">
import type { BglFormSchemaT, Option } from '@bagelink/vue'
import type { Field } from '../types/BagelForm'
import {
	Btn,
	Card,
	CheckInput,
	Icon,
	Modal,
	Pill,
	SelectInput,
	Spreadsheet,
	useExcel,
	DragOver,
} from '@bagelink/vue'
import { computed, reactive, ref, watch, watchEffect } from 'vue'

import { useSchemaField } from '../composables/useSchemaField'
import { formatString } from '../utils/strings'
import TextInput from './form/inputs/TextInput.vue'

// Add interface for schema items
interface SchemaItem {
	id: string
	label: string
	$el?: string
	required?: boolean
	isArrayField?: boolean
	parentField?: string
	disabled?: boolean
	disabledReason?: string
	options?: Option[] // For enum options
	dataType?: string // Add dataType field
	attrs?: {
		required?: boolean
		schema?: SchemaItem[]
		attrs?: {
			required?: boolean
		}
		options?: Option[] // For enum options
		dataType?: string // Add dataType in attrs
	}
	children?: SchemaItem[]
}

// Add interface for mapped row data
interface MappedRow {
	[key: string]: any
}

// Interface for transformations
interface Transformation {
	fieldId: string
	sourceValue: any
	targetValue: any
}

const props = defineProps<{
	schema?: BglFormSchemaT<T>
	title?: string
}>()

const emit = defineEmits<{
	(e: 'processedData', data: T[]): void
}>()

// Get Excel utilities from composable
const {
	readSheetData,
	getSheetNames,
	isExcelSerialDate,
	excelSerialDateToJSDate,
	formatDate
} = useExcel()

// Data type constants
const DATA_TYPES = {
	STRING: 'string',
	NUMBER: 'number',
	DATE: 'date',
	DATETIME: 'datetime',
	BOOLEAN: 'boolean'
}

// Data type options for selection
const dataTypeOptions = [
	{ value: DATA_TYPES.STRING, label: 'Text (String)' },
	{ value: DATA_TYPES.NUMBER, label: 'Number' },
	{ value: DATA_TYPES.DATE, label: 'Date' },
	{ value: DATA_TYPES.DATETIME, label: 'Date & Time' },
	{ value: DATA_TYPES.BOOLEAN, label: 'Boolean' }
]

// Component state
const file = ref<File | null>(null)
const fileData = ref<any[]>([])
const sheetNames = ref<string[]>([])
const selectedSheet = ref<string>('')
const hasHeaders = ref(true)
const fieldMapping = reactive<Record<string, string>>({})
const isLoading = ref(false)
const showPreviewModal = ref(false)
const previewData = ref<any[]>([])
const mappingComplete = ref(false)
const fileHeaders = ref<string[]>([])

// New state for constant values and transformations
const defaultValues = reactive<Record<string, any>>({})

// State for transformations
const transformations = reactive<Record<string, Transformation[]>>({})
const showTransformDialog = ref(false)
const selectedTransformField = ref<SchemaItem | null>(null)

// State for data types
const fieldDataTypes = reactive<Record<string, string>>({})

// State for related data
const relatedFiles = reactive<Record<string, File | null>>({})
const relatedFileData = reactive<Record<string, any[]>>({})
const relatedFileMappings = reactive<Record<string, Record<string, string>>>({})
const showRelatedDialog = ref(false)
const selectedRelationField = ref<SchemaItem | null>(null)
const relatedKeyField = reactive<Record<string, string>>({})
const parentKeyField = reactive<Record<string, string>>({})

// Improve the transformations management
const selectedSourceValue = ref('')
const selectedTargetValue = ref('')

// State for related field data types
const relatedFieldDataTypes = reactive<Record<string, string>>({})

// State for related default values
const relatedDefaultValues = reactive<Record<string, Record<string, any>>>({})

// State for related transformations
const relatedTransformations = reactive<Record<string, Record<string, Transformation[]>>>({})
const showRelatedTransformDialog = ref(false)
const selectedRelatedTransformField = ref<{ parentId: string, field: SchemaItem } | null>(null)
const selectedRelatedSourceValue = ref('')
const selectedRelatedTargetValue = ref('')

// Set up schema field rendering
const formData = ref<any>({})
const { renderField } = useSchemaField<any, any>({
	mode: 'form',
	getFormData: () => formData.value,
	onUpdateModelValue: (field: Field<any>, value: any) => {
		if (!field.id) return

		// Check if this is a related field (id contains a dot)
		if (field.id.includes('.')) {
			const [parentId, childId] = field.id.split('.')
			if (!relatedDefaultValues[parentId]) {
				relatedDefaultValues[parentId] = {}
			}
			relatedDefaultValues[parentId][childId] = value
		} else {
			// Regular field
			defaultValues[field.id] = value
		}
	}
})

// Add function to get unique source values for a field
function getUniqueSourceValues(fieldId: string): any[] {
	if (!fieldMapping[fieldId] || !fileData.value || fileData.value.length === 0) {
		return []
	}

	// Get all values from the mapped column
	const allValues = fileData.value
		.map(row => row[fieldMapping[fieldId]])
		.filter(value => value !== undefined && value !== null && value !== '')
	const uniqueValues = [...new Set(allValues)]

	return uniqueValues.filter((value) => {
		if (!transformations[fieldId] || transformations[fieldId].length === 0) {
			return true
		}
		return !transformations[fieldId].some(t => t.sourceValue == value || t.sourceValue === value.toString()
		)
	})
}

const availableSourceValues = computed(() => {
	if (!selectedTransformField.value || !selectedTransformField.value.id) {
		return []
	}
	return getUniqueSourceValues(selectedTransformField.value.id)
})

// Create options array from unique values
const sourceValueOptions = computed(() => {
	return availableSourceValues.value.map(value => ({
		value: String(value),
		label: String(value)
	}))
})

// Fix the findMatchingTargetValue function to handle Option types correctly
function findMatchingTargetValue(sourceValue: string, options: Option[]): string | null {
	if (!sourceValue || !options || !Array.isArray(options) || options.length === 0) return null

	// Convert sourceValue to string and lowercase for comparison
	const lowerSourceValue = String(sourceValue).toLowerCase().trim()

	// First try exact match
	const exactMatch = options.find((option) => {
		const optionObj = typeof option === 'object' && option !== null ? option : { value: String(option), label: String(option) }
		// Make sure option and label exist and is a string
		if (!optionObj) return false
		const optionLabel = `${optionObj.label}`.toLowerCase().trim()
		return optionLabel === lowerSourceValue
	})

	if (exactMatch) {
		const optionObj = typeof exactMatch === 'object' && exactMatch !== null
			? exactMatch
			: { value: String(exactMatch), label: String(exactMatch) }
		return String(optionObj.value)
	}

	// Try more flexible matching if exact match fails
	const fuzzyMatch = options.find((option) => {
		const optionObj = typeof option === 'object' && option !== null ? option : { value: String(option), label: String(option) }
		if (!optionObj || typeof optionObj.label !== 'string') return false

		const optionLabel = `${optionObj.label}`.toLowerCase().trim()

		// Try contains match
		return lowerSourceValue.includes(optionLabel) || optionLabel.includes(lowerSourceValue)
	})

	if (fuzzyMatch) {
		const optionObj = typeof fuzzyMatch === 'object' && fuzzyMatch !== null
			? fuzzyMatch
			: { value: String(fuzzyMatch), label: String(fuzzyMatch) }
		return String(optionObj.value)
	}

	return null
}

// Watch for changes in selected source value and auto-match if enabled
watch(selectedSourceValue, (newValue) => {
	if (selectedTransformField.value?.options) {
		const matchedValue = findMatchingTargetValue(newValue, selectedTransformField.value.options)
		if (matchedValue) {
			selectedTargetValue.value = matchedValue
		}
	}
})

// Add a transformation
function addTransformation(fieldId: string) {
	if (!transformations[fieldId]) {
		transformations[fieldId] = []
	}

	if (selectedSourceValue.value && selectedTargetValue.value) {
		// Check if this source value already has a transformation
		const existingIndex = transformations[fieldId].findIndex(t => t.sourceValue === selectedSourceValue.value
		)

		if (existingIndex >= 0) {
			// Update existing transformation
			transformations[fieldId][existingIndex].targetValue = selectedTargetValue.value
		} else {
			// Add new transformation
			transformations[fieldId].push({
				fieldId,
				sourceValue: selectedSourceValue.value,
				targetValue: selectedTargetValue.value
			})
		}
		selectedSourceValue.value = ''
		selectedTargetValue.value = ''
	}
}

// Create a revised function to extract fields from the schema structure without duplicates
function getAllFields(schema: any[]): any[] {
	if (!schema || !Array.isArray(schema)) return []

	const allFields: any[] = []
	const seenIds = new Set() // Keep track of field IDs we've already added

	// Helper to add a field if it hasn't been seen before
	function addFieldIfNew(field: any) {
		if (field && field.id && field.label && !seenIds.has(field.id)) {
			seenIds.add(field.id)

			// Always create options array if it doesn't exist
			if (!field.options) {
				field.options = []
			}

			// Extract options if they exist in attrs
			if (field.attrs && field.attrs.options && field.attrs.options.length > 0) {
				field.options = field.attrs.options
			}

			allFields.push(field)
		}
	}

	// Process each schema item
	schema.forEach((item: any) => {
		// Direct fields (like more_info.session_rate)
		if (item && item.id && item.label) {
			addFieldIfNew(item)
		}

		// Containers with children (like flex divs)
		if (item && item.children && Array.isArray(item.children)) {
			// Process each child
			item.children.forEach((child: any) => {
				// Regular fields
				if (child && child.id && child.label) {
					// Check if it's an array field
					if (child.$el === 'array' && child.attrs && child.attrs.schema) {
						// Add the array field itself (phones, emails)
						addFieldIfNew(child)

						if (Array.isArray(child.attrs.schema)) {
							child.attrs.schema.forEach((schemaItem: SchemaItem) => {
								if (schemaItem && schemaItem.id && schemaItem.label) {
									const qualifiedField = {
										...schemaItem,
										id: `${child.id}.${schemaItem.id}`,
										parentField: child.id,
										isArrayField: true
									}

									if (schemaItem.options) {
										qualifiedField.options = schemaItem.options
									} else if (schemaItem.attrs && schemaItem.attrs.options) {
										qualifiedField.options = schemaItem.attrs.options
									}

									addFieldIfNew(qualifiedField)
								}
							})
						}
					} else {
						addFieldIfNew(child)
					}
				}
			})
		}
	})

	return allFields
}

// Get extracted fields from schema
const schemaFields = computed(() => {
	return getAllFields(props.schema || [])
})

// Update the isFieldRequired function to handle the special case for array fields
function isFieldRequired(field: any): boolean {
	if (field.isArrayField && field.parentField) {
		return false
	}

	// For regular fields, check the standard required attributes
	return (field.attrs && field.attrs.required === true)
		|| (field.required === true)
		|| (field.attrs && field.attrs.attrs && field.attrs.attrs.required === true)
}

// Add a function to get field display information including conditional requirements
function getFieldDescription(field: any): { description: string, isConditional: boolean } {
	// For array child fields, show they're conditionally required
	if (field.isArrayField && field.parentField) {
		const parentLabel = schemaFields.value.find(f => f.id === field.parentField)?.label || field.parentField
		return {
			description: `Required only if ${parentLabel} has items`,
			isConditional: true
		}
	}

	// No special handling needed
	return {
		description: '',
		isConditional: false
	}
}

async function parseFile(file: File) {
	isLoading.value = true

	try {
		sheetNames.value = await getSheetNames(file)
		selectedSheet.value = sheetNames.value[0]

		await loadSheetData()
	} catch (error) {
		console.error('Error parsing file:', error)
	} finally {
		isLoading.value = false
	}
}

// Replace loadSheetData function with simplified version
async function loadSheetData() {
	if (!file.value || !selectedSheet.value) return
	isLoading.value = true
	try {
		// Use the readSheetData utility from our composable
		const { headers, data } = await readSheetData(file.value, selectedSheet.value, hasHeaders.value)

		fileHeaders.value = headers
		fileData.value = data
		resetMapping()
		setTimeout(() => { guessDataTypes() }, 100)
	} catch (error) {
		console.error('Error loading sheet data:', error)
	} finally {
		isLoading.value = false
	}
}

// Add a function to check if a field is related to an array parent that's already mapped
function checkArrayFieldConflicts() {
	// Get all array parent fields that are currently mapped
	const mappedArrayParents = new Set()
	const mappedArrayChildren = new Map() // Map from parent ID to child IDs

	// Identify which array parents and children are mapped
	Object.keys(fieldMapping).forEach((fieldId: string) => {
		const field = schemaFields.value.find(f => f.id === fieldId)
		if (field) {
			if (field.$el === 'array') {
				// This is an array parent
				mappedArrayParents.add(field.id)
			} else if (field.isArrayField && field.parentField) {
				// This is an array child
				if (!mappedArrayChildren.has(field.parentField)) {
					mappedArrayChildren.set(field.parentField, new Set())
				}
				mappedArrayChildren.get(field.parentField).add(field.id)
			}
		}
	})

	// For each array parent that's mapped, disable its children
	for (const parentId of mappedArrayParents) {
		const childFields = schemaFields.value.filter(f => f.parentField === parentId)
		childFields.forEach((child: any) => {
			child.disabled = true
			child.disabledReason = `Parent field "${parentId}" is already mapped`
		})
	}

	// For each array child that's mapped, disable its parent
	for (const [parentId, childIds] of mappedArrayChildren.entries()) {
		if (childIds.size > 0) {
			const parentField = schemaFields.value.find(f => f.id === parentId)
			if (parentField) {
				parentField.disabled = true
				parentField.disabledReason = `Child field(s) already mapped`
			}
		}
	}
}

// Modify the resetMapping function to call checkArrayFieldConflicts
function resetMapping() {
	// Reset field mapping
	Object.keys(fieldMapping).forEach((key: string) => {
		delete fieldMapping[key]
	})

	// Reset disabled state on all fields
	schemaFields.value.forEach((field: any) => {
		field.disabled = false
		field.disabledReason = ''
	})

	// Try to auto-map fields based on similar names
	if (fileHeaders.value.length > 0) {
		schemaFields.value.forEach((field: any) => {
			// Format variations of field id and label for comparison
			const fieldIdCamel = formatString(field.id, 'camel')
			const fieldIdPascal = formatString(field.id, 'pascal')
			const fieldLabelCamel = formatString(field.label.replace(/\s+/g, '_'), 'camel')
			const fieldLabelPascal = formatString(field.label.replace(/\s+/g, '_'), 'pascal')

			// Look for exact match
			const exactMatch = fileHeaders.value.find(
				header => header.toLowerCase() === field.id.toLowerCase()
					|| header.toLowerCase() === field.label.toLowerCase()
					|| header.toLowerCase() === fieldIdCamel.toLowerCase()
					|| header.toLowerCase() === fieldIdPascal.toLowerCase()
					|| header.toLowerCase() === fieldLabelCamel.toLowerCase()
					|| header.toLowerCase() === fieldLabelPascal.toLowerCase()
					|| formatString(header.replace(/\s+/g, '_'), 'camel').toLowerCase() === fieldIdCamel.toLowerCase()
					|| formatString(header.replace(/\s+/g, '_'), 'camel').toLowerCase() === fieldLabelCamel.toLowerCase()
			)

			if (exactMatch && !field.disabled) {
				fieldMapping[field.id] = exactMatch
			} else {
				// Look for partial match
				const partialMatch = fileHeaders.value.find(
					header => header.toLowerCase().includes(field.id.toLowerCase())
						|| header.toLowerCase().includes(field.label.toLowerCase())
						|| header.toLowerCase().includes(fieldIdCamel.toLowerCase())
						|| header.toLowerCase().includes(fieldLabelCamel.toLowerCase())
						|| field.id.toLowerCase().includes(header.toLowerCase())
						|| field.label.toLowerCase().includes(header.toLowerCase())
				)

				if (partialMatch && !field.disabled) {
					fieldMapping[field.id] = partialMatch
				}
			}
		})
	}

	// Check for array field conflicts
	checkArrayFieldConflicts()
	checkMappingComplete()
}

// Simplify the checkMappingComplete function to match canProcessData
function checkMappingComplete() {
	// Use the same logic as canProcessData for consistency
	if (!file.value || (Object.keys(fieldMapping).length === 0 && Object.keys(defaultValues).length === 0)) {
		mappingComplete.value = false
		return
	}

	// Get strictly required fields (non-conditional)
	const requiredFields = schemaFields.value.filter((field) => {
		// Exclude array children which are conditionally required
		if (field.isArrayField && field.parentField) return false

		// Check various ways a field might be marked as required
		return (field.attrs && field.attrs.required === true)
			|| (field.required === true)
			|| (field.attrs && field.attrs.attrs && field.attrs.attrs.required === true)
	})

	// If no strictly required fields, we just need at least one mapping or default value
	if (requiredFields.length === 0) {
		mappingComplete.value = Object.keys(fieldMapping).some(key => !!fieldMapping[key])
			|| Object.keys(defaultValues).length > 0
		return
	}

	// Check that all required fields are mapped or have default values
	mappingComplete.value = requiredFields.every(field => !!fieldMapping[field.id] || hasDefaultValue(field.id))
}

function showPreview() {
	guessDataTypes()
	const mappedData: MappedRow[] = []

	for (let i = 0; i < fileData.value.length; i++) {
		const sourceRow = fileData.value[i]
		const mappedRow: MappedRow = {}

		schemaFields.value.forEach((field) => {
			// Skip array fields as they're handled separately
			if (field.isArrayField || field.$el === 'array') return

			// Get value from mapping or use default
			let value: any = null
			let useDefault = false

			if (fieldMapping[field.id] && sourceRow[fieldMapping[field.id]] !== undefined) {
				value = sourceRow[fieldMapping[field.id]]
				// Use default value as fallback if the mapped value is empty
				if (value === '' && defaultValues[field.id] !== undefined) {
					value = defaultValues[field.id]
					useDefault = true
				}
			} else if (defaultValues[field.id] !== undefined) {
				// Use default value if no direct mapping or mapping is empty
				value = defaultValues[field.id]
				useDefault = true
			}

			// Skip if no value
			if (value === null) {
				return
			}

			// Apply transformations if any exist and not using default value
			if (!useDefault && transformations[field.id] && transformations[field.id].length > 0) {
				// Find matching transformation
				const transform = transformations[field.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
				)
				if (transform) {
					value = transform.targetValue
				}
			}

			// Apply data type conversion
			const dataType = fieldDataTypes[field.id] || DATA_TYPES.STRING
			value = convertValueByType(value, dataType)

			if (field.id.includes('.')) {
				// Handle dot notation for nested objects
				const parts = field.id.split('.')
				const rootField = parts[0]

				// Create nested structure
				if (!mappedRow[rootField]) {
					mappedRow[rootField] = {}
				}

				// Handle multi-level nesting
				let current = mappedRow[rootField]
				for (let i = 1; i < parts.length - 1; i++) {
					if (!current[parts[i]]) {
						current[parts[i]] = {}
					}
					current = current[parts[i]]
				}

				// Set the value at the final level
				current[parts[parts.length - 1]] = value
			} else {
				mappedRow[field.id] = value
			}
		})

		// Process array child fields
		const arrayChildFields = schemaFields.value.filter(field => field.isArrayField && field.parentField)

		// Process each array field
		arrayChildFields.forEach((childField) => {
			const [parentId, childId] = childField.id.split('.')
			if (!parentId || !childId) return

			// Get value from mapping or use default
			let value: any = null
			let useDefault = false

			if (fieldMapping[childField.id] && sourceRow[fieldMapping[childField.id]] !== undefined) {
				value = sourceRow[fieldMapping[childField.id]]
				// Use default value as fallback if the mapped value is empty
				if (value === '' && defaultValues[childField.id] !== undefined) {
					value = defaultValues[childField.id]
					useDefault = true
				}
			} else if (defaultValues[childField.id] !== undefined) {
				// Use default value if no direct mapping
				value = defaultValues[childField.id]
				useDefault = true
			}

			// Skip if no value
			if (value === null) {
				return
			}

			// Create the array structure if it doesn't exist
			if (!mappedRow[parentId]) {
				mappedRow[parentId] = []
			}

			// Apply transformations if any exist and not using default
			if (!useDefault && transformations[childField.id] && transformations[childField.id].length > 0) {
				// Find matching transformation
				const transform = transformations[childField.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
				)
				if (transform) {
					value = transform.targetValue
				}
			}

			// Apply data type conversion for array fields
			const dataType = fieldDataTypes[childField.id] || DATA_TYPES.STRING
			value = convertValueByType(value, dataType)

			// Add the array item with the proper structure
			mappedRow[parentId].push({
				[childId]: value
			})
		})

		// Handle related file data
		Object.keys(relatedFiles).forEach((fieldId) => {
			if (!relatedFiles[fieldId] || !relatedKeyField[fieldId] || !parentKeyField[fieldId]) {
				return
			}

			// Get parent key value from this row
			const parentKeyValue = sourceRow[parentKeyField[fieldId]]
			if (!parentKeyValue) return

			// Create the array structure if it doesn't exist
			if (!mappedRow[fieldId]) {
				mappedRow[fieldId] = []
			}

			// Find matching rows in related data
			const matchingRows = relatedFileData[fieldId].filter(relatedRow => relatedRow[relatedKeyField[fieldId]] == parentKeyValue
				|| relatedRow[relatedKeyField[fieldId]] === parentKeyValue.toString()
			)

			// Add each matching row as an item in the array
			matchingRows.forEach((matchingRow) => {
				const mappedItem: Record<string, any> = {}

				// Apply mappings from related file
				if (selectedRelationField.value?.attrs?.schema) {
					selectedRelationField.value.attrs.schema.forEach((schemaItem: SchemaItem) => {
						if (!schemaItem.id) return

						// Get value from related mapping or use default
						let value: any = null
						let useDefault = false

						if (relatedFileMappings[fieldId][schemaItem.id]
							&& matchingRow[relatedFileMappings[fieldId][schemaItem.id]] !== undefined) {
							value = matchingRow[relatedFileMappings[fieldId][schemaItem.id]]
							// Use default as fallback for empty values
							if (value === '' && relatedDefaultValues[fieldId][schemaItem.id] !== undefined) {
								value = relatedDefaultValues[fieldId][schemaItem.id]
								useDefault = true
							}
						} else if (relatedDefaultValues[fieldId][schemaItem.id] !== undefined) {
							// Use default value if no mapping
							value = relatedDefaultValues[fieldId][schemaItem.id]
							useDefault = true
						}

						// Skip if no value
						if (value === null) return

						// Apply transformations if any exist and not using default
						if (!useDefault
							&& relatedTransformations[fieldId][schemaItem.id]
							&& relatedTransformations[fieldId][schemaItem.id].length > 0) {
							// Find matching transformation
							const transform = relatedTransformations[fieldId][schemaItem.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
							)
							if (transform) {
								value = transform.targetValue
							}
						}

						// Apply data type conversion
						const fullChildId = `${fieldId}.${schemaItem.id}`
						const dataType = relatedFieldDataTypes[fullChildId] || DATA_TYPES.STRING
						value = convertValueByType(value, dataType)

						// Add to mapped item
						mappedItem[schemaItem.id] = value
					})
				}

				// Only add item if it has at least one value
				if (Object.keys(mappedItem).length > 0) {
					mappedRow[fieldId].push(mappedItem)
				}
			})
		})

		// Check if this row has any data (not empty)
		const hasData = Object.values(mappedRow).some((value) => {
			if (value === null || value === undefined) return false
			if (value === '') return false
			if (Array.isArray(value) && value.length === 0) return false
			return true
		})

		// Only add rows that have data
		if (hasData) {
			mappedData.push(mappedRow)
		}
	}

	// Update the preview data with all non-empty rows
	previewData.value = mappedData

	// Show the modal
	showPreviewModal.value = true
}

// Create a function to convert our schema fields to Spreadsheet column config
function createSpreadsheetColumns() {
	return schemaFields.value
		.filter((field) => {
			return field.$el !== 'array'
		})
		.map((field) => {
			// Create a column config for each field
			return {
				key: field.id,
				title: field.label,
				// Special formatting for array child fields
				formatter: field.isArrayField ? formatArrayChildValue : undefined
			}
		})
}

// Helper function to format array child values for display
function formatArrayChildValue(value: any, row: any, fieldId: string): string {
	// For array child fields, we need to check if it's in the parent array
	const field = schemaFields.value.find(f => f.id === fieldId)
	if (!field || !field.isArrayField || !field.parentField) return value

	const [parentId, childId] = fieldId.split('.')
	const parentArray = row[parentId]

	if (Array.isArray(parentArray) && parentArray.length > 0) {
		// Extract values from the parent array
		return parentArray
			.map(item => item[childId])
			.filter(val => val !== undefined)
			.join(', ')
	}

	return ''
}

// Add computed for spreadsheet columns
const spreadsheetColumns = computed(() => createSpreadsheetColumns())

function processData() {
	emit('processedData', previewData.value)
	showPreviewModal.value = false
}

function updateFieldMapping(fieldId: string, value: string) {
	const previousValue = fieldMapping[fieldId]
	if (previousValue && previousValue !== value) {
		fieldMapping[fieldId] = ''
		schemaFields.value.forEach((field) => {
			field.disabled = false
			field.disabledReason = ''
		})
	}
	if (value) {
		fieldMapping[fieldId] = value
		const field = schemaFields.value.find(f => f.id === fieldId)
		if (field) {
			if (field.$el === 'array') {
				const childFields = schemaFields.value.filter(f => f.parentField === field.id)
				childFields.forEach((child) => {
					child.disabled = true
					child.disabledReason = `Parent field "${field.id}" is already mapped`
				})
			}

			if (field.isArrayField && field.parentField) {
				const parentField = schemaFields.value.find(f => f.id === field.parentField)
				if (parentField) {
					parentField.disabled = true
					parentField.disabledReason = `Child field already mapped`
				}
			}
		}
	}
	checkArrayFieldConflicts()
	checkMappingComplete()
}

function handleSelectChange(event: Event, fieldId: string) {
	const target = event.target as HTMLSelectElement
	if (target) {
		updateFieldMapping(fieldId, target.value)
	}
}

async function handleFilesUploaded(files: FileList) {
	file.value = files[0]
	if (!file.value) return
	isLoading.value = true

	try {
		await parseFile(file.value)
	} catch (error) {
		console.error('Error parsing file:', error)
	} finally {
		isLoading.value = false
	}
}
// Watch for changes in the selected sheet
watchEffect(() => {
	if (selectedSheet.value) {
		loadSheetData()
	}
})

// Function to check if field has default value
function hasDefaultValue(fieldId: string): boolean {
	return defaultValues[fieldId] !== undefined
		&& defaultValues[fieldId] !== null
		&& defaultValues[fieldId] !== ''
}

// Function to open transformation dialog
function openTransformDialog(field: SchemaItem) {
	try {
		if (!field.options) {
			field.options = []
		}

		if (field.attrs && field.attrs.options) {
			field.options = field.attrs.options
		}
		selectedTransformField.value = field
		if (!transformations[field.id]) {
			transformations[field.id] = []
		}
		showTransformDialog.value = true
	} catch (error) {
		console.error('Error opening transform dialog:', error)
		alert('An error occurred while opening the transform dialog. See console for details.')
	}
}

// Function to remove a transformation
function removeTransformation(fieldId: string, index: number) {
	if (transformations[fieldId] && transformations[fieldId].length > index) {
		transformations[fieldId].splice(index, 1)
	}
}

// Function to open related file dialog
function openRelatedDialog(field: SchemaItem) {
	selectedRelationField.value = field
	showRelatedDialog.value = true
}

// Function to process related file
async function processRelatedFile(fieldId: string, files: File[] | FileList) {
	if (!files) return
	const file = files[0]

	relatedFiles[fieldId] = file

	try {
		// First get sheet names
		const sheets = await getSheetNames(file)
		const selectedSheet = sheets.length > 0 ? sheets[0] : ''

		// Use the first sheet instead of an empty string
		const { data } = await readSheetData(file, selectedSheet, true)
		relatedFileData[fieldId] = data

		// Initialize mapping if not exists
		if (!relatedFileMappings[fieldId]) {
			relatedFileMappings[fieldId] = {}
		}
	} catch (error) {
		console.error('Error processing related file:', error)
	}
}

// Add function to auto-populate transformations for a field
function autoPopulateTransformations(fieldId: string) {
	try {
		const field = schemaFields.value.find(f => f.id === fieldId)
		if (!field) {
			console.error('Field not found:', fieldId)
			return
		}

		// Ensure options are available either directly or from attrs
		let fieldOptions = field.options || (field.attrs && field.attrs.options)

		if (!fieldMapping[fieldId] || !fileData.value || fileData.value.length === 0) {
			console.warn('No data or mapping found for field:', fieldId)
			return
		}

		if (!fieldOptions) {
			console.warn('No options found for field:', fieldId)
			return
		}

		// Ensure options is an array
		if (!Array.isArray(fieldOptions)) {
			console.warn('Options is not an array for field:', fieldId)
			fieldOptions = []
		}

		const uniqueValues = getUniqueSourceValues(fieldId)

		// Initialize transformations array if needed
		if (!transformations[fieldId]) {
			transformations[fieldId] = []
		}

		let matchCount = 0
		const unmatchedValues: string[] = []

		// For each unique value, try to find a matching target
		uniqueValues.forEach((sourceValue) => {
			const strSourceValue = String(sourceValue)
			const matchedValue = findMatchingTargetValue(strSourceValue, fieldOptions)

			if (matchedValue) {
				// Check if this source value already has a transformation
				const existingIndex = transformations[fieldId].findIndex(t => t.sourceValue === strSourceValue
				)

				if (existingIndex >= 0) {
					// Update existing transformation
					transformations[fieldId][existingIndex].targetValue = matchedValue
				} else {
					// Add new transformation
					transformations[fieldId].push({
						fieldId,
						sourceValue: strSourceValue,
						targetValue: matchedValue
					})
				}

				matchCount++
			} else {
				unmatchedValues.push(strSourceValue)
			}
		})

		// Provide some feedback about how many matches were found
		if (matchCount === 0) {
			alert(`No automatic matches found. Try creating transformations manually.`)
		} else {
			alert(`Automatically created ${matchCount} transformations by matching source values to target labels.\n\n${unmatchedValues.length} values could not be automatically matched.`)
		}
	} catch (error) {
		console.error('Error auto-populating transformations:', error)
		alert('An error occurred while trying to auto-populate transformations. See console for details.')
	}
}

// Function to detect date format from string
function detectDateFormat(value: string): RegExp | null {
	// Common date formats
	const formats = [
		/^\d{4}-\d{2}-\d{2}$/, // YYYY-MM-DD
		/^\d{2}\/\d{2}\/\d{4}$/, // MM/DD/YYYY
		/^\d{2}\.\d{2}\.\d{4}$/, // DD.MM.YYYY
		/^\d{1,2}\s[a-z]{3}\s\d{4}$/i, // D MMM YYYY
		/^\d{1,2}\s[a-z]{3,9}\s\d{4}$/i, // D MMMM YYYY
	]

	for (const format of formats) {
		if (format.test(value)) {
			return format
		}
	}
	return null
}

// Function to parse date string based on detected format
function parseDate(value: string): Date | null {
	if (!value) return null

	// Try parsing ISO format first
	const isoDate = new Date(value)
	if (!Number.isNaN(isoDate.getTime())) {
		return isoDate
	}

	// Try parsing other formats
	const format = detectDateFormat(value)
	if (format) {
		// Handle specific formats
		if (/^\d{2}\/\d{2}\/\d{4}$/.test(value)) {
			// MM/DD/YYYY
			const [month, day, year] = value.split('/').map(Number)
			return new Date(year, month - 1, day)
		} else if (/^\d{2}\.\d{2}\.\d{4}$/.test(value)) {
			// DD.MM.YYYY
			const [day, month, year] = value.split('.').map(Number)
			return new Date(year, month - 1, day)
		} else if (/^\d{1,2}\s[a-z]{3,9}\s\d{4}$/i.test(value)) {
			// D MMM YYYY or D MMMM YYYY
			return new Date(value)
		}
	}

	return null
}

// Replace convertValueByType function with simplified version using composable utilities
function convertValueByType(value: any, dataType: string): any {
	if (value === null || value === undefined || value === '') {
		return null
	}

	try {
		switch (dataType) {
			case DATA_TYPES.STRING:
				return String(value)

			case DATA_TYPES.NUMBER: {
				const num = Number(value)
				return Number.isNaN(num) ? null : num
			}

			case DATA_TYPES.BOOLEAN:
				if (typeof value === 'boolean') return value
				if (typeof value === 'string') {
					const lowercased = value.toLowerCase().trim()
					if (['true', 'yes', '1', 'on'].includes(lowercased)) return true
					if (['false', 'no', '0', 'off'].includes(lowercased)) return false
				}
				return Boolean(value)

			case DATA_TYPES.DATE:
			case DATA_TYPES.DATETIME:
				// Handle Excel serial dates
				if (isExcelSerialDate(value)) {
					const date = excelSerialDateToJSDate(value)
					return formatDate(date, dataType === DATA_TYPES.DATETIME)
				}

				// Handle string dates
				if (typeof value === 'string') {
					const dateObj = parseDate(value)
					if (dateObj) {
						return formatDate(dateObj, dataType === DATA_TYPES.DATETIME)
					}
				}

				// Handle Date objects
				if (value instanceof Date) {
					return formatDate(value, dataType === DATA_TYPES.DATETIME)
				}

				return null

			default:
				return value
		}
	} catch (error) {
		console.error('Error converting value:', value, 'to type:', dataType, error)
		return null
	}
}

function detectDataType(value: any): string {
	if (value === null || value === undefined) {
		return DATA_TYPES.STRING
	}
	if (typeof value === 'number' || (typeof value === 'string' && !Number.isNaN(Number(value)))) {
		if (isExcelSerialDate(Number(value))) {
			return DATA_TYPES.DATE
		}
		return DATA_TYPES.NUMBER
	}
	if (typeof value === 'boolean' || (typeof value === 'string' && ['true', 'false', 'yes', 'no'].includes(value.toLowerCase()))) {
		return DATA_TYPES.BOOLEAN
	}
	if (value instanceof Date) {
		return DATA_TYPES.DATETIME
	}

	if (typeof value === 'string') {
		// Check if it's a date string
		if (detectDateFormat(value) || !Number.isNaN(new Date(value).getTime())) {
			return DATA_TYPES.DATE
		}
	}

	// Default to string
	return DATA_TYPES.STRING
}

// Function to guess data types for all mapped fields
function guessDataTypes(): void {
	schemaFields.value.forEach((field) => {
		if (!fieldDataTypes[field.id] && fieldMapping[field.id]) {
			// Get sample values from the first few rows
			const sampleValues = fileData.value
				.slice(0, 5)
				.map(row => row[fieldMapping[field.id]])
				.filter(value => value !== undefined && value !== null && value !== '')

			if (sampleValues.length > 0) {
				// Detect most common type
				const types = sampleValues.map(detectDataType)
				const typeCount: Record<string, number> = {}

				types.forEach((type: any) => {
					typeCount[type] = (typeCount[type] || 0) + 1
				})

				// Get the most common type
				let maxCount = 0
				let mostCommonType = DATA_TYPES.STRING

				Object.entries(typeCount).forEach(([type, count]) => {
					if (count > maxCount) {
						maxCount = count
						mostCommonType = type
					}
				})

				fieldDataTypes[field.id] = mostCommonType
			} else {
				fieldDataTypes[field.id] = DATA_TYPES.STRING
			}
		}
	})
}

// Function to open transformation dialog for related fields
function openRelatedTransformDialog(parentId: string, field: SchemaItem) {
	try {
		console.log('Opening related transform dialog for field:', field.id, 'in parent:', parentId)

		// Make sure to set options property from attrs if needed
		if (!field.options) {
			field.options = []
		}

		if (field.attrs && field.attrs.options) {
			console.log('Copying options from attrs for related field:', field.id)
			field.options = Array.isArray(field.attrs.options)
				? field.attrs.options
				: []
		}

		selectedRelatedTransformField.value = { parentId, field }

		// Initialize transformations structure if needed
		if (!relatedTransformations[parentId]) {
			relatedTransformations[parentId] = {}
		}

		if (!relatedTransformations[parentId][field.id]) {
			relatedTransformations[parentId][field.id] = []
		}

		showRelatedTransformDialog.value = true

		// Reset selected values
		selectedRelatedSourceValue.value = ''
		selectedRelatedTargetValue.value = ''
	} catch (error) {
		console.error('Error opening related transform dialog:', error)
		alert('An error occurred while opening the related transform dialog. See console for details.')
	}
}

// Function to get unique source values for a related field
function getRelatedUniqueSourceValues(parentId: string, fieldId: string): any[] {
	if (!relatedFileMappings[parentId][fieldId]
		|| !relatedFileData[parentId]
		|| relatedFileData[parentId].length === 0) {
		return []
	}

	// Get all values from the mapped column in the related file
	const allValues = relatedFileData[parentId]
		.map(row => row[relatedFileMappings[parentId][fieldId]])
		.filter(value => value !== undefined && value !== null && value !== '')

	// Create a unique set of values
	const uniqueValues = [...new Set(allValues)]

	// Filter out values that already have transformations
	return uniqueValues.filter((value) => {
		if (!relatedTransformations[parentId][fieldId]
			|| relatedTransformations[parentId][fieldId].length === 0) {
			return true
		}

		// Return false if this value already has a transformation
		return !relatedTransformations[parentId][fieldId].some(t => t.sourceValue == value || t.sourceValue === value.toString()
		)
	})
}

// Computed for available related source values
const availableRelatedSourceValues = computed(() => {
	if (!selectedRelatedTransformField.value) {
		return []
	}

	const { parentId, field } = selectedRelatedTransformField.value
	return getRelatedUniqueSourceValues(parentId, field.id)
})

// Create options array from unique related values
const relatedSourceValueOptions = computed(() => {
	return availableRelatedSourceValues.value.map(value => ({
		value: String(value),
		label: String(value)
	}))
})

// Function to add a related transformation
function addRelatedTransformation(parentId: string, fieldId: string) {
	if (!relatedTransformations[parentId]) {
		relatedTransformations[parentId] = {}
	}

	if (!relatedTransformations[parentId][fieldId]) {
		relatedTransformations[parentId][fieldId] = []
	}

	if (selectedRelatedSourceValue.value && selectedRelatedTargetValue.value) {
		// Check if this source value already has a transformation
		const existingIndex = relatedTransformations[parentId][fieldId].findIndex(
			t => t.sourceValue === selectedRelatedSourceValue.value
		)

		if (existingIndex >= 0) {
			// Update existing transformation
			relatedTransformations[parentId][fieldId][existingIndex].targetValue = selectedRelatedTargetValue.value
		} else {
			// Add new transformation
			relatedTransformations[parentId][fieldId].push({
				fieldId,
				sourceValue: selectedRelatedSourceValue.value,
				targetValue: selectedRelatedTargetValue.value
			})
		}

		// Reset selection
		selectedRelatedSourceValue.value = ''
		selectedRelatedTargetValue.value = ''
	}
}

// Function to remove a related transformation
function removeRelatedTransformation(parentId: string, fieldId: string, index: number) {
	if (relatedTransformations[parentId][fieldId]
		&& relatedTransformations[parentId][fieldId].length > index) {
		relatedTransformations[parentId][fieldId].splice(index, 1)
	}
}

// Function to auto-populate transformations for a related field
function autoPopulateRelatedTransformations(parentId: string, fieldId: string) {
	try {
		const field = selectedRelatedTransformField.value?.field
		if (!field) {
			console.error('Field not found for auto-populate')
			return
		}

		// Ensure options are available either directly or from attrs
		let fieldOptions = field.options || (field.attrs && field.attrs.options)

		if (!relatedFileMappings[parentId][fieldId]
			|| !relatedFileData[parentId]
			|| relatedFileData[parentId].length === 0) {
			console.warn('No data or mapping found for related field:', fieldId)
			return
		}

		if (!fieldOptions) {
			console.warn('No options found for related field:', fieldId)
			return
		}

		// Ensure options is an array
		if (!Array.isArray(fieldOptions)) {
			console.warn('Options is not an array for related field:', fieldId)
			fieldOptions = []
		}

		const uniqueValues = getRelatedUniqueSourceValues(parentId, fieldId)

		// Initialize transformations array if needed
		if (!relatedTransformations[parentId]) {
			relatedTransformations[parentId] = {}
		}

		if (!relatedTransformations[parentId][fieldId]) {
			relatedTransformations[parentId][fieldId] = []
		}

		let matchCount = 0
		const unmatchedValues: string[] = []

		// For each unique value, try to find a matching target
		uniqueValues.forEach((sourceValue) => {
			const strSourceValue = String(sourceValue)

			const matchedValue = findMatchingTargetValue(strSourceValue, fieldOptions)

			if (matchedValue) {
				// Check if this source value already has a transformation
				const existingIndex = relatedTransformations[parentId][fieldId].findIndex(
					t => t.sourceValue === strSourceValue
				)

				if (existingIndex >= 0) {
					// Update existing transformation
					relatedTransformations[parentId][fieldId][existingIndex].targetValue = matchedValue
				} else {
					// Add new transformation
					relatedTransformations[parentId][fieldId].push({
						fieldId,
						sourceValue: strSourceValue,
						targetValue: matchedValue
					})
				}

				matchCount++
			} else {
				unmatchedValues.push(strSourceValue)
			}
		})

		// Provide feedback about matches
		if (matchCount === 0) {
			alert(`No automatic matches found. Try creating transformations manually.`)
		} else {
			alert(`Automatically created ${matchCount} transformations by matching source values to target labels.\n\n${unmatchedValues.length} values could not be automatically matched.`)
		}
	} catch (error) {
		console.error('Error auto-populating related transformations:', error)
		alert('An error occurred while trying to auto-populate related transformations. See console for details.')
	}
}

// Initialize default value structure for a field
function initDefaultValue(fieldId: string) {
	// Make sure the field has an entry in defaultValues
	if (defaultValues[fieldId] === undefined) {
		defaultValues[fieldId] = null
	}
}

// Initialize related default value structure
function initRelatedDefaultValue(parentId: string, fieldId: string) {
	if (!relatedDefaultValues[parentId]) {
		relatedDefaultValues[parentId] = {}
	}
	if (relatedDefaultValues[parentId][fieldId] === undefined) {
		relatedDefaultValues[parentId][fieldId] = null
	}
}

// Helper function to prepare a field with default values for rendering
function getFieldWithDefaults(field: any) {
	// Get the current field type to render proper input
	let fieldType = field.$el || 'text'

	// If datatype is set, adjust the field type accordingly
	if (fieldDataTypes[field.id]) {
		switch (fieldDataTypes[field.id]) {
			case DATA_TYPES.NUMBER:
				fieldType = 'number'
				break
			case DATA_TYPES.DATE:
				fieldType = 'date'
				break
			case DATA_TYPES.BOOLEAN:
				fieldType = 'toggle'
				break
		}
	}

	// Update formData with current value to support the renderField function
	if (field.id) {
		formData.value[field.id] = defaultValues[field.id]
	}

	return {
		...field,
		$el: fieldType,
		placeholder: 'Set default...'
	}
}

// Add a helper function for related fields too
function getRelatedFieldWithDefaults(parentId: string, field: any) {
	// Get the current field type to render proper input
	let fieldType = field.$el || 'text'

	// If datatype is set, adjust the field type accordingly
	const fullFieldId = `${parentId}.${field.id}`
	if (relatedFieldDataTypes[fullFieldId]) {
		switch (relatedFieldDataTypes[fullFieldId]) {
			case DATA_TYPES.NUMBER:
				fieldType = 'number'
				break
			case DATA_TYPES.DATE:
				fieldType = 'date'
				break
			case DATA_TYPES.BOOLEAN:
				fieldType = 'toggle'
				break
		}
	}

	// Create a modified id to avoid collision with main fields
	const modifiedField = {
		...field,
		id: fullFieldId,
		$el: fieldType,
		placeholder: 'Set default...'
	}

	// Update formData with current value
	formData.value[fullFieldId] = relatedDefaultValues[parentId][field.id]

	return modifiedField
}
</script>

<template>
	<Card class="upload-data-container h-100p grid overflow-hidden list-wrap ">
		<h2 class="line-height-1 m-0 pb-2 txt-center" v-text="props.title || 'Upload and Map Data'" />
		<div v-if="!file" class="h-100p flex column justify-content-center">
			<DragOver
				accept=".csv,.xls,.xlsx"
				class="max-h300px w-500px"
				browse
				@addFiles="handleFilesUploaded"
			>
				<Card class="flex flex-column items-center justify-center outline-dashed outline-3 bg-input hover h-100p justify-content-center txt-center">
					<Icon name="upload" size="5" />
					<p>Drag and drop an Excel or CSV file here</p>
					<u>or click to select a file</u>
					<p class="txt-12 color-gray">
						Accepts .xlsx, .xls, and .csv files
					</p>
				</Card>
			</DragOver>
		</div>

		<!-- Loading indicator -->
		<div v-if="isLoading" class="loading-container">
			<div class="spinner" />
			<p>Processing your file...</p>
		</div>

		<!-- Step 2: Sheet selection and configuration -->
		<div class="overflow h-100p">
			<div v-if="file && !isLoading && sheetNames.length > 0" class="config-section flex gap-05 pb-2 m_flex-wrap">
				<Btn v-tooltip="'Change File'" class="px-1" color="gray" @click="file = null">
					<Icon icon="draft" size="1.5" weight="300" />
					<p>
						{{ file.name }}
					</p>
					<!-- <Icon icon="edit" size="0.75" /> -->
				</Btn>
				<SelectInput v-if="sheetNames.length > 1" v-model="selectedSheet" :options="sheetNames" label="Select Sheet" />
				<CheckInput
					v-model="hasHeaders" label="Mark this if file has a header row" class="m-0"
					style="--bgl-accent-color: var(--bgl-black); --bgl-primary: var(--bgl-black);"
				/>
			</div>

			<!-- Step 3: Field Mapping -->
			<div v-if="file && !isLoading && fileHeaders.length > 0">
				<!-- <h3 class="mt-0">
					Map Fields
				</h3> -->
				<p class="label pb-1 border-bottom mb-1">
					Match each required field to a column from your file, set default values, or configure transformations
				</p>

				<div class="mapping-table">
					<div class="grid grid-wrap-5 gap-1 bold pb-1 m_none">
						<p>Schema Field</p>
						<p>Column from File</p>
						<p>Default Value</p>
						<p>Data Type</p>
						<p>Actions</p>
					</div>

					<div v-for="field in schemaFields" :key="field.id" class="grid grid-wrap-5 gap-1  m_gap-025 m_pb-1-5" :class="{ 'array-field-row': field.isArrayField || field.$el === 'array' }">
						<div>
							<div class="field-label">
								<p class="grid-span-2 input-size line-height-1 inline-block">
									{{ field.label }}
								</p>
								<span v-if="field.isArrayField">↳</span>
								<Pill v-if="field.$el === 'array'" class="txt10 ms-05" round thin value="Array" />
								<!-- <span v-if="field.$el === 'array'" class="array-parent-indicator">[Array]</span> -->
								<span v-if="isFieldRequired(field)">*</span>
								<span v-if="getFieldDescription(field).isConditional">†</span>
							</div>
							<div v-if="field.disabled" class="field-disabled-reason">
								{{ field.disabledReason }}
							</div>
							<div v-if="getFieldDescription(field).isConditional">
								{{ getFieldDescription(field).description }}
							</div>
						</div>
						<div class="fileColSelect">
							<SelectInput
								v-model="fieldMapping[field.id]"
								icon="table_chart"
								:options="fileHeaders"
								searchable
								:required="isFieldRequired(field)"
								:disabled="field.disabled"
								@change="handleSelectChange($event, field.id)"
							/>
						</div>
						<div>
							<!-- Default Value Input -->
							<div class="default-value-container hideLabel">
								{{ initDefaultValue(field.id) }}
								<component
									:is="renderField(getFieldWithDefaults(field))"
								/>
							</div>
						</div>
						<div>
							<SelectInput
								v-model="fieldDataTypes[field.id]"
								:options="dataTypeOptions"
								:disabled="!fieldMapping[field.id] && !defaultValues[field.id]"
							/>
						</div>
						<div>
							<div class="flex gap-05 my-05">
								<Btn
									v-tooltip="'Transform'"
									thin
									:disabled="field.disabled"
									icon="transform"
									@click="openTransformDialog(field)"
								/>
								<Btn v-if="field.$el === 'array'" v-tooltip="'Related File'" thin icon="attach_file" :disabled="field.disabled" @click="openRelatedDialog(field)" />
							</div>
						</div>
					</div>
				</div>

				<div v-if="mappingComplete" class="action-buttons">
					<Btn @click="showPreview">
						Preview Data
					</Btn>
				</div>
				<div v-else class="action-buttons">
					<div class="mapping-incomplete-message">
						Please map the required fields to continue
					</div>
				</div>
			</div>
		</div>

		<!-- Transformation Modal -->
		<Modal v-model:visible="showTransformDialog" title="Configure Transformations" width="800">
			<div v-if="selectedTransformField">
				<div class="flex space-between gap-1 mb-1 border-bottom pb-05 m_flex-wrap">
					<p>Create transformations for <strong>{{ selectedTransformField.label }}</strong></p>
					<Btn icon="auto_awesome" thin value="Autodetect" @click="autoPopulateTransformations(selectedTransformField.id)" />
				</div>

				<div>
					<div class="grid grid-wrap-7 gap-1 bold pb-05 m_none">
						<p class="grid-span-2">
							Source Value
						</p>
						<p class="grid-span-4">
							Target Value
						</p>
						<p>Action</p>
					</div>
					<div>
						<div v-for="(transform, index) in transformations[selectedTransformField.id] || []" :key="index" class="grid grid-wrap-7 gap-1 align-items-center m_gap-025  m_pb-1-5">
							<p class="grid-span-2 input-size line-height-1">
								{{ transform.sourceValue }}
							</p>
							<p class="grid-span-4 input-size line-height-1 ellipsis-1">
								{{ transform.targetValue }}
							</p>
							<Btn
								v-tooltip="'Remove'"
								class="mb-05"
								thin
								icon="delete"
								color="red"
								@click="removeTransformation(selectedTransformField.id, index)"
							/>
						</div>
						<div class="grid grid-wrap-7 gap-1 align-items-center m_gap-025  m_pb-1-5">
							<div class="grid-span-2">
								<SelectInput
									v-if="fieldMapping[selectedTransformField.id]"
									v-model="selectedSourceValue"
									searchable
									:options="sourceValueOptions"
									placeholder="Select source value"
								/>
								<TextInput v-else v-model="selectedSourceValue" type="text" placeholder="Source value" />
							</div>
							<div class="grid-span-4">
								<SelectInput
									v-if="selectedTransformField.options && selectedTransformField.options.length > 0"
									v-model="selectedTargetValue"
									searchable
									:options="selectedTransformField.options"
									placeholder="Select target value"
								/>
								<TextInput v-else v-model="selectedTargetValue" type="text" placeholder="Target value" />
							</div>
							<Btn
								v-tooltip="'Add'"
								class="mb-05"
								thin
								icon="add"
								color="primary"
								@click="addTransformation(selectedTransformField.id)"
							/>
						</div>
					</div>
				</div>

				<div class="flex pt-05">
					<Btn class="ms-auto" value="Close" @click="showTransformDialog = false" />
				</div>
			</div>
		</Modal>

		<!-- Related File Modal -->
		<Modal v-model:visible="showRelatedDialog" title="Configure Related Data" width="900">
			<div v-if="selectedRelationField">
				<p class="pb-05">
					Upload a file with related data for {{ selectedRelationField.label }}
				</p>

				<div v-if="!relatedFiles[selectedRelationField.id]" class="mb-05">
					<DragOver
						accept=".csv,.xls,.xlsx"
						browse
						@addFiles="(files) => processRelatedFile(selectedRelationField!.id, files)"
					>
						<Card class="flex flex-column items-center justify-center outline-dashed outline-3 hover bg-input">
							<Icon name="upload" size="5" />
							<p>Drag and drop an Excel or CSV file here</p>
							<p>or click to select a file</p>
							<p class="txt-12 color-gray">
								Accepts .xlsx, .xls, and .csv files
							</p>
						</Card>
					</DragOver>
				</div>

				<div v-else>
					<div class="mb-1">
						<Pill>
							{{ relatedFiles[selectedRelationField.id]!.name }}
						</Pill>
						<Btn thin round value="Change File" @click="relatedFiles[selectedRelationField.id] = null" />
					</div>

					<div v-if="relatedFileData[selectedRelationField.id]">
						<h4>Configure Relationship</h4>

						<div class="flex gap-1">
							<SelectInput v-model="parentKeyField[selectedRelationField.id]" :options="fileHeaders" label="Source Key Field (from this file)" />
							<SelectInput v-model="relatedKeyField[selectedRelationField.id]" :options="Object.keys(relatedFileData[selectedRelationField.id][0] || {})" label="Related Key Field (from related file)" />
						</div>

						<h4>Map Related Fields</h4>

						<table>
							<thead>
								<tr>
									<th>Child Field</th>
									<th>Related File Column</th>
									<th>Default Value</th>
									<th>Data Type</th>
									<th>Actions</th>
								</tr>
							</thead>
							<tbody>
								<tr v-for="schemaItem in (selectedRelationField.attrs?.schema || [])" :key="schemaItem.id">
									<td>{{ schemaItem.label }}</td>
									<td>
										<SelectInput
											v-model="relatedFileMappings[selectedRelationField.id][schemaItem.id]"
											:options="Object.keys(relatedFileData[selectedRelationField.id][0] || {})"
											placeholder="Select column..."
										/>
									</td>
									<td>
										<!-- Default Value Input for Related Fields -->
										<div class="default-value-container">
											{{ initRelatedDefaultValue(selectedRelationField.id, schemaItem.id) }}
											<component
												:is="renderField(getRelatedFieldWithDefaults(selectedRelationField.id, schemaItem))"
											/>
										</div>
									</td>
									<td>
										<SelectInput
											v-model="relatedFieldDataTypes[`${selectedRelationField.id}.${schemaItem.id}`]"
											:options="dataTypeOptions"
											:disabled="!relatedFileMappings[selectedRelationField.id]?.[schemaItem.id] && !relatedDefaultValues[selectedRelationField.id]?.[schemaItem.id]"
										/>
									</td>
									<td>
										<div class="action-buttons-cell">
											<Btn
												thin
												icon="transform"
												@click="openRelatedTransformDialog(selectedRelationField.id, schemaItem)"
											>
												Transform
											</Btn>
										</div>
									</td>
								</tr>
							</tbody>
						</table>
					</div>
				</div>
				<div class="flex pt-05">
					<Btn class="ms-auto" value="Close" @click="showRelatedDialog = false" />
				</div>
			</div>
		</Modal>

		<!-- Preview Modal -->
		<Modal v-model:visible="showPreviewModal" title="Data Preview & Edit" width="90vw">
			<div>
				<Spreadsheet
					v-model="previewData"
					class="popupPreviewSpreadsheet"
					:column-config="spreadsheetColumns"
					allow-add-row
				/>
			</div>
			<div>
				<p class="mt-1">
					Showing all {{ previewData.length }} records. You can edit values directly.
				</p>
				<div class="flex gap-1 mt-1 space-between">
					<Btn flat thin value="Cancel" @click="showPreviewModal = false" />
					<Btn value="Import Data" @click="processData()" />
				</div>
			</div>
		</Modal>

		<!-- Related Transformation Modal -->
		<Modal v-model:visible="showRelatedTransformDialog" title="Configure Related Transformations" width="800">
			<div v-if="selectedRelatedTransformField">
				<p>Create transformations for <strong>{{ selectedRelatedTransformField.field.label }}</strong> in {{ selectedRelationField?.label }}</p>

				<div>
					<div>
						<Btn
							thin
							icon="auto_awesome"
							value="Autolink"
							color="primary"
							@click="autoPopulateRelatedTransformations(
								selectedRelatedTransformField.parentId,
								selectedRelatedTransformField.field.id,
							)"
						/>
					</div>
				</div>

				<div>
					<table>
						<thead>
							<tr>
								<th>Source Value</th>
								<th>Target Value</th>
								<th>Action</th>
							</tr>
						</thead>
						<tbody>
							<tr
								v-for="(transform, index) in relatedTransformations[selectedRelatedTransformField.parentId]?.[selectedRelatedTransformField.field.id] || []"
								:key="index"
							>
								<td>{{ transform.sourceValue }}</td>
								<td>{{ transform.targetValue }}</td>
								<td>
									<Btn
										v-tooltip="'Remove'"
										thin
										icon="delete"
										color="red"
										@click="removeRelatedTransformation(
											selectedRelatedTransformField.parentId,
											selectedRelatedTransformField.field.id,
											index,
										)"
									/>
								</td>
							</tr>
							<tr>
								<td>
									<SelectInput
										v-if="relatedFileMappings[selectedRelatedTransformField.parentId]?.[selectedRelatedTransformField.field.id]"
										v-model="selectedRelatedSourceValue"
										searchable
										:options="relatedSourceValueOptions"
										placeholder="Select source value"
									/>
									<input v-else v-model="selectedRelatedSourceValue" type="text" placeholder="Source value">
								</td>
								<td>
									<SelectInput
										v-if="selectedRelatedTransformField.field.options && selectedRelatedTransformField.field.options.length > 0"
										v-model="selectedRelatedTargetValue"
										searchable
										:options="selectedRelatedTransformField.field.options"
										placeholder="Select target value"
									/>
									<input v-else v-model="selectedRelatedTargetValue" type="text" placeholder="Target value">
								</td>
								<td>
									<Btn
										v-tooltip="'Add'"
										thin
										icon="add"
										color="primary"
										@click="addRelatedTransformation(
											selectedRelatedTransformField.parentId,
											selectedRelatedTransformField.field.id,
										)"
									/>
								</td>
							</tr>
						</tbody>
					</table>
				</div>

				<div>
					<Btn @click="showRelatedTransformDialog = false">
						Close
					</Btn>
				</div>
			</div>
		</Modal>
	</Card>
</template>

<style>
.fileColSelect{
	--input-bg: var(--bgl-green-light);
}
.fileColSelect .bgl_icon-font{
	color: var(--bgl-green);
	line-height: 0;
}
.hideLabel label, .hideLabel .label{
	font-size: 0 !important;
}
.mapping-table .selectinput-btn:disabled{
	background: var(--input-bg) !important;
	cursor: not-allowed !important;
}
.field-label{
	--pill-height: 20px;
}
.popupPreviewSpreadsheet .spreadsheet{
	width: 100%;
	overflow: auto;
	height: calc(100vh - 300px);
}
</style>
