<script setup lang="ts" generic="T">
import type { BglFormSchemaT, Option } from '@bagelink/vue'
import type { MaybeRefOrGetter } from 'vue'
import type { Field } from '../types/BagelForm'
import {
	Btn,
	Card,
	CheckInput,
	Icon,
	Modal,
	Pill,
	SelectInput,
	Spreadsheet,
	useExcel,
	DragOver,
	useI18n,
} from '@bagelink/vue'
import { computed, reactive, ref, watch, watchEffect, toValue } from 'vue'
import { useSchemaField } from '../composables/useSchemaField'
import { formatString } from '../utils/strings'
import TextInput from './form/inputs/TextInput.vue'

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

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

const { $t } = useI18n()

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
}

// 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' }
]

const file = ref<File | null>(null)
const fileData = ref<any[]>([])
const sheetNames = ref<string[]>([])
const selectedSheet = ref<string>('')
const hasHeaders = ref(true)
const isLoading = ref(false)
const showPreviewModal = ref(false)
const previewData = ref<any[]>([])
const mappingComplete = ref(false)
const fileHeaders = ref<string[]>([])
const showTransformDialog = ref(false)
const selectedTransformField = ref<SchemaItem | null>(null)
const showRelatedDialog = ref(false)
const selectedRelationField = ref<SchemaItem | null>(null)
const selectedSourceValue = ref('')
const selectedTargetValue = ref('')
const showRelatedTransformDialog = ref(false)
const selectedRelatedTransformField = ref<{ parentId: string, field: SchemaItem } | null>(null)
const selectedRelatedSourceValue = ref('')
const selectedRelatedTargetValue = ref('')

const fieldMapping = reactive<Record<string, string>>({})
const defaultValues = reactive<Record<string, any>>({})
const transformations = reactive<Record<string, Transformation[]>>({})
const fieldDataTypes = reactive<Record<string, string>>({})
const relatedFiles = reactive<Record<string, File | null>>({})
const relatedFileData = reactive<Record<string, any[]>>({})
const relatedFileMappings = reactive<Record<string, Record<string, string>>>({})
const relatedKeyField = reactive<Record<string, string>>({})
const parentKeyField = reactive<Record<string, string>>({})
const relatedFieldDataTypes = reactive<Record<string, string>>({})
const relatedDefaultValues = reactive<Record<string, Record<string, any>>>({})
const relatedTransformations = reactive<Record<string, Record<string, Transformation[]>>>({})

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

		if (field.id.includes('.')) {
			const [parentId, childId] = field.id.split('.')
			if (!relatedDefaultValues[parentId]) {
				relatedDefaultValues[parentId] = {}
			}
			relatedDefaultValues[parentId][childId] = value
		} else {
			defaultValues[field.id] = value
		}
	}
})

function getUniqueSourceValues(fieldId: string): any[] {
	if (!fieldMapping[fieldId] || !fileData.value || fileData.value.length === 0) {
		return []
	}
	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)
})

const sourceValueOptions = computed(() => {
	return availableSourceValues.value.map(value => ({
		value: String(value),
		label: String(value)
	}))
})

function findMatchingTargetValue(sourceValue: string, options: Option[]): string | null {
	if (!sourceValue || !options || !Array.isArray(options) || options.length === 0) { return null }
	const lowerSourceValue = String(sourceValue).toLowerCase().trim()
	const exactMatch = options.find((option) => {
		const optionObj = typeof option === 'object' && option !== null ? option : { value: String(option), label: String(option) }
		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()

		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(selectedSourceValue, (newValue) => {
	if (selectedTransformField.value?.options) {
		const matchedValue = findMatchingTargetValue(newValue, selectedTransformField.value.options)
		if (matchedValue) {
			selectedTargetValue.value = matchedValue
		}
	}
})

function addTransformation(fieldId: string) {
	if (!transformations[fieldId]) {
		transformations[fieldId] = []
	}

	if (selectedSourceValue.value && selectedTargetValue.value) {
		const existingIndex = transformations[fieldId].findIndex(t => t.sourceValue === selectedSourceValue.value
		)

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

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)
		}
		if (item && item.children && Array.isArray(item.children)) {
			item.children.forEach((child: any) => {
				if (child && child.id && child.label) {
					if (child.$el === 'array' && child.attrs && child.attrs.schema) {
						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
}

const schemaFields = computed(() => getAllFields(toValue(props.schema) || []))

function isFieldRequired(field: any): boolean {
	if (field.isArrayField && field.parentField) {
		return false
	}

	return (field.attrs && field.attrs.required === true)
		|| (field.required === true)
		|| (field.attrs && field.attrs.attrs && field.attrs.attrs.required === true)
}

function getFieldDescription(field: any): { description: string, isConditional: boolean } {
	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
		}
	}
	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
	}
}

async function loadSheetData() {
	if (!file.value || !selectedSheet.value) { return }
	isLoading.value = true
	try {
		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
	}
}

function checkArrayFieldConflicts() {
	const mappedArrayParents = new Set()
	const mappedArrayChildren = new Map()

	Object.keys(fieldMapping).forEach((fieldId: string) => {
		const field = schemaFields.value.find(f => f.id === fieldId)
		if (field) {
			if (field.$el === 'array') {
				mappedArrayParents.add(field.id)
			} else if (field.isArrayField && field.parentField) {
				if (!mappedArrayChildren.has(field.parentField)) {
					mappedArrayChildren.set(field.parentField, new Set())
				}
				mappedArrayChildren.get(field.parentField).add(field.id)
			}
		}
	})

	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 (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`
			}
		}
	}
}

function resetMapping() {
	Object.keys(fieldMapping).forEach((key: string) => {
		delete fieldMapping[key]
	})

	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
				}
			}
		})
	}

	checkArrayFieldConflicts()
	checkMappingComplete()
}

function checkMappingComplete() {
	if (!file.value || (Object.keys(fieldMapping).length === 0 && Object.keys(defaultValues).length === 0)) {
		mappingComplete.value = false
		return
	}

	const requiredFields = schemaFields.value.filter((field) => {
		if (field.isArrayField && field.parentField) { return false }

		return (field.attrs && field.attrs.required === true)
			|| (field.required === true)
			|| (field.attrs && field.attrs.attrs && field.attrs.attrs.required === true)
	})

	if (requiredFields.length === 0) {
		mappingComplete.value = Object.keys(fieldMapping).some(key => !!fieldMapping[key])
			|| Object.keys(defaultValues).length > 0
		return
	}
	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) => {
			if (field.isArrayField || field.$el === 'array') { return }
			let value: any = null
			let useDefault = false

			if (fieldMapping[field.id] && sourceRow[fieldMapping[field.id]] !== undefined) {
				value = sourceRow[fieldMapping[field.id]]
				if (value === '' && defaultValues[field.id] !== undefined) {
					value = defaultValues[field.id]
					useDefault = true
				}
			} else if (defaultValues[field.id] !== undefined) {
				value = defaultValues[field.id]
				useDefault = true
			}

			if (value === null) { return }

			if (!useDefault && transformations[field.id] && transformations[field.id].length > 0) {
				const transform = transformations[field.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
				)
				if (transform) {
					value = transform.targetValue
				}
			}

			const dataType = fieldDataTypes[field.id] || DATA_TYPES.STRING
			value = convertValueByType(value, dataType)

			if (field.id.includes('.')) {
				const parts = field.id.split('.')
				const rootField = parts[0]

				if (!mappedRow[rootField]) {
					mappedRow[rootField] = {}
				}

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

				current[parts[parts.length - 1]] = value
			} else {
				mappedRow[field.id] = value
			}
		})

		const arrayChildFields = schemaFields.value.filter(field => field.isArrayField && field.parentField)

		arrayChildFields.forEach((childField) => {
			const [parentId, childId] = childField.id.split('.')
			if (!parentId || !childId) { return }

			let value: any = null
			let useDefault = false

			if (fieldMapping[childField.id] && sourceRow[fieldMapping[childField.id]] !== undefined) {
				value = sourceRow[fieldMapping[childField.id]]
				if (value === '' && defaultValues[childField.id] !== undefined) {
					value = defaultValues[childField.id]
					useDefault = true
				}
			} else if (defaultValues[childField.id] !== undefined) {
				value = defaultValues[childField.id]
				useDefault = true
			}

			if (value === null) {
				return
			}

			if (!mappedRow[parentId]) {
				mappedRow[parentId] = []
			}

			if (!useDefault && transformations[childField.id] && transformations[childField.id].length > 0) {
				const transform = transformations[childField.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
				)
				if (transform) {
					value = transform.targetValue
				}
			}

			const dataType = fieldDataTypes[childField.id] || DATA_TYPES.STRING
			value = convertValueByType(value, dataType)

			mappedRow[parentId].push({
				[childId]: value
			})
		})

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

			const parentKeyValue = sourceRow[parentKeyField[fieldId]]
			if (!parentKeyValue) { return }

			if (!mappedRow[fieldId]) {
				mappedRow[fieldId] = []
			}

			const matchingRows = relatedFileData[fieldId].filter(relatedRow => relatedRow[relatedKeyField[fieldId]] == parentKeyValue
				|| relatedRow[relatedKeyField[fieldId]] === parentKeyValue.toString()
			)

			matchingRows.forEach((matchingRow) => {
				const mappedItem: Record<string, any> = {}

				if (selectedRelationField.value?.attrs?.schema) {
					selectedRelationField.value.attrs.schema.forEach((schemaItem: SchemaItem) => {
						if (!schemaItem.id) { return }

						let value: any = null
						let useDefault = false

						if (relatedFileMappings[fieldId][schemaItem.id]
							&& matchingRow[relatedFileMappings[fieldId][schemaItem.id]] !== undefined) {
							value = matchingRow[relatedFileMappings[fieldId][schemaItem.id]]
							if (value === '' && relatedDefaultValues[fieldId][schemaItem.id] !== undefined) {
								value = relatedDefaultValues[fieldId][schemaItem.id]
								useDefault = true
							}
						} else if (relatedDefaultValues[fieldId][schemaItem.id] !== undefined) {
							value = relatedDefaultValues[fieldId][schemaItem.id]
							useDefault = true
						}

						if (value === null) { return }

						if (!useDefault
							&& relatedTransformations[fieldId][schemaItem.id]
							&& relatedTransformations[fieldId][schemaItem.id].length > 0) {
							const transform = relatedTransformations[fieldId][schemaItem.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
							)
							if (transform) {
								value = transform.targetValue
							}
						}

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

						mappedItem[schemaItem.id] = value
					})
				}

				if (Object.keys(mappedItem).length > 0) {
					mappedRow[fieldId].push(mappedItem)
				}
			})
		})

		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
		})

		if (hasData) {
			mappedData.push(mappedRow)
		}
	}

	previewData.value = mappedData
	showPreviewModal.value = true
}

function createSpreadsheetColumns() {
	return schemaFields.value
		.filter((field) => {
			return field.$el !== 'array'
		})
		.map((field) => {
			return {
				key: field.id,
				title: field.label,
				formatter: field.isArrayField ? formatArrayChildValue : undefined
			}
		})
}

function formatArrayChildValue(value: any, row: any, fieldId: string): string {
	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) {
		return parentArray
			.map(item => item[childId])
			.filter(val => val !== undefined)
			.join(', ')
	}

	return ''
}

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
	}
}

watchEffect(() => {
	if (selectedSheet.value) {
		loadSheetData()
	}
})

function hasDefaultValue(fieldId: string): boolean {
	return defaultValues[fieldId] !== undefined
		&& defaultValues[fieldId] !== null
		&& defaultValues[fieldId] !== ''
}

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 removeTransformation(fieldId: string, index: number) {
	if (transformations[fieldId] && transformations[fieldId].length > index) {
		transformations[fieldId].splice(index, 1)
	}
}

function openRelatedDialog(field: SchemaItem) {
	selectedRelationField.value = field
	showRelatedDialog.value = true
}

async function processRelatedFile(fieldId: string, files: File[] | FileList) {
	if (!files) { return }
	const file = files[0]

	relatedFiles[fieldId] = file

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

		const { data } = await readSheetData(file, selectedSheet, true)
		relatedFileData[fieldId] = data

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

function autoPopulateTransformations(fieldId: string) {
	try {
		const field = schemaFields.value.find(f => f.id === fieldId)
		if (!field) {
			console.error('Field not found:', fieldId)
			return
		}

		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
		}

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

		const uniqueValues = getUniqueSourceValues(fieldId)

		if (!transformations[fieldId]) {
			transformations[fieldId] = []
		}

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

		uniqueValues.forEach((sourceValue) => {
			const strSourceValue = String(sourceValue)
			const matchedValue = findMatchingTargetValue(strSourceValue, fieldOptions)

			if (matchedValue) {
				const existingIndex = transformations[fieldId].findIndex(t => t.sourceValue === strSourceValue
				)

				if (existingIndex >= 0) {
					transformations[fieldId][existingIndex].targetValue = matchedValue
				} else {
					transformations[fieldId].push({
						fieldId,
						sourceValue: strSourceValue,
						targetValue: matchedValue
					})
				}

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

		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 detectDateFormat(value: string): RegExp | null {
	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 parseDate(value: string): Date | null {
	if (!value) { return null }

	const isoDate = new Date(value)
	if (!Number.isNaN(isoDate.getTime())) {
		return isoDate
	}
	const format = detectDateFormat(value)
	if (format) {
		if (/^\d{2}\/\d{2}\/\d{4}$/.test(value)) {
			const [month, day, year] = value.split('/').map(Number)
			return new Date(year, month - 1, day)
		} if (/^\d{2}\.\d{2}\.\d{4}$/.test(value)) {
			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)) {
			return new Date(value)
		}
	}

	return null
}

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:
				if (isExcelSerialDate(value)) {
					const date = excelSerialDateToJSDate(value)
					return formatDate(date, dataType === DATA_TYPES.DATETIME)
				}
				if (typeof value === 'string') {
					const dateObj = parseDate(value)
					if (dateObj) {
						return formatDate(dateObj, dataType === DATA_TYPES.DATETIME)
					}
				}
				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') {
		if (detectDateFormat(value) || !Number.isNaN(new Date(value).getTime())) {
			return DATA_TYPES.DATE
		}
	}
	return DATA_TYPES.STRING
}

function guessDataTypes(): void {
	schemaFields.value.forEach((field) => {
		if (!fieldDataTypes[field.id] && fieldMapping[field.id]) {
			const sampleValues = fileData.value
				.slice(0, 5)
				.map(row => row[fieldMapping[field.id]])
				.filter(value => value !== undefined && value !== null && value !== '')

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

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

				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 openRelatedTransformDialog(parentId: string, field: SchemaItem) {
	try {
		console.log('Opening related transform dialog for field:', field.id, 'in parent:', parentId)
		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 }

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

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

		showRelatedTransformDialog.value = true

		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 getRelatedUniqueSourceValues(parentId: string, fieldId: string): any[] {
	if (!relatedFileMappings[parentId][fieldId]
		|| !relatedFileData[parentId]
		|| relatedFileData[parentId].length === 0) {
		return []
	}

	const allValues = relatedFileData[parentId]
		.map(row => row[relatedFileMappings[parentId][fieldId]])
		.filter(value => value !== undefined && value !== null && value !== '')

	const uniqueValues = [...new Set(allValues)]

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

const availableRelatedSourceValues = computed(() => {
	if (!selectedRelatedTransformField.value) {
		return []
	}

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

const relatedSourceValueOptions = computed(() => {
	return availableRelatedSourceValues.value.map(value => ({
		value: String(value),
		label: String(value)
	}))
})

function addRelatedTransformation(parentId: string, fieldId: string) {
	if (!relatedTransformations[parentId]) {
		relatedTransformations[parentId] = {}
	}

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

	if (selectedRelatedSourceValue.value && selectedRelatedTargetValue.value) {
		const existingIndex = relatedTransformations[parentId][fieldId].findIndex(
			t => t.sourceValue === selectedRelatedSourceValue.value
		)

		if (existingIndex >= 0) {
			relatedTransformations[parentId][fieldId][existingIndex].targetValue = selectedRelatedTargetValue.value
		} else {
			relatedTransformations[parentId][fieldId].push({
				fieldId,
				sourceValue: selectedRelatedSourceValue.value,
				targetValue: selectedRelatedTargetValue.value
			})
		}
		selectedRelatedSourceValue.value = ''
		selectedRelatedTargetValue.value = ''
	}
}

function removeRelatedTransformation(parentId: string, fieldId: string, index: number) {
	if (relatedTransformations[parentId][fieldId]
		&& relatedTransformations[parentId][fieldId].length > index) {
		relatedTransformations[parentId][fieldId].splice(index, 1)
	}
}

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

		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
		}

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

		const uniqueValues = getRelatedUniqueSourceValues(parentId, fieldId)

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

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

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

		uniqueValues.forEach((sourceValue) => {
			const strSourceValue = String(sourceValue)

			const matchedValue = findMatchingTargetValue(strSourceValue, fieldOptions)

			if (matchedValue) {
				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)
			}
		})
		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.')
	}
}

function initDefaultValue(fieldId: string) {
	if (defaultValues[fieldId] === undefined) {
		defaultValues[fieldId] = null
	}
}

function initRelatedDefaultValue(parentId: string, fieldId: string) {
	if (!relatedDefaultValues[parentId]) {
		relatedDefaultValues[parentId] = {}
	}
	if (relatedDefaultValues[parentId][fieldId] === undefined) {
		relatedDefaultValues[parentId][fieldId] = null
	}
}

function getFieldWithDefaults(field: any) {
	let fieldType = field.$el || 'text'

	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
		}
	}

	if (field.id) {
		formData.value[field.id] = defaultValues[field.id]
	}

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

function getRelatedFieldWithDefaults(parentId: string, field: any) {
	let fieldType = field.$el || 'text'
	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>
		<div v-if="isLoading" class="loading-container">
			<div class="spinner" />
			<p>Processing your file...</p>
		</div>

		<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 v-text="file.name" />
				</Btn>
				<SelectInput
					v-if="sheetNames.length > 1" v-model="selectedSheet" :options="sheetNames"
					:label="$t('importData.selectSheet')"
				/>
				<CheckInput
					v-model="hasHeaders" :label="$t('importData.hasHeaders')" class="m-0"
					style="--bgl-accent-color: var(--bgl-black); --bgl-primary: var(--bgl-black);"
				/>
			</div>
			<div v-if="file && !isLoading && fileHeaders.length > 0">
				<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>
		<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="$t('importData.selectSourceValue')"
								/>
								<TextInput
									v-else v-model="selectedSourceValue" type="text"
									:placeholder="$t('importData.sourceValue')"
								/>
							</div>
							<div class="grid-span-4">
								<SelectInput
									v-if="selectedTransformField.options && selectedTransformField.options.length > 0"
									v-model="selectedTargetValue" searchable :options="selectedTransformField.options"
									:placeholder="$t('importData.selectTargetValue')"
								/>
								<TextInput
									v-else v-model="selectedTargetValue" type="text"
									:placeholder="$t('importData.targetValue')"
								/>
							</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="$t:importData.close" @click="showTransformDialog = false" />
				</div>
			</div>
		</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="$t:importData.changeFile"
							@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="$t('importData.sourceKeyField')"
							/>
							<SelectInput
								v-model="relatedKeyField[selectedRelationField.id]"
								:options="Object.keys(relatedFileData[selectedRelationField.id][0] || {})"
								:label="$t('importData.relatedKeyField')"
							/>
						</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="$t('importData.selectColumn')"
										/>
									</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="$t:importData.close" @click="showRelatedDialog = false" />
				</div>
			</div>
		</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="$t:importData.cancel" @click="showPreviewModal = false" />
					<Btn value="$t:importData.importData" @click="processData()" />
				</div>
			</div>
		</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="$t('importData.selectSourceValue')"
									/>
									<input
										v-else v-model="selectedRelatedSourceValue" type="text"
										:placeholder="$t('importData.sourceValue')"
									>
								</td>
								<td>
									<SelectInput
										v-if="selectedRelatedTransformField.field.options && selectedRelatedTransformField.field.options.length > 0"
										v-model="selectedRelatedTargetValue" searchable
										:options="selectedRelatedTransformField.field.options"
										:placeholder="$t('importData.selectTargetValue')"
									/>
									<input
										v-else v-model="selectedRelatedTargetValue" type="text"
										:placeholder="$t('importData.targetValue')"
									>
								</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 value="$t:importData.close" @click="showRelatedTransformDialog = false" />
				</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>
