<script setup lang="ts">
/**
 * MultiStepForm - Multi-step form renderer
 *
 * Renders multi-step forms with:
 * - Step navigation
 * - Conditional steps (.if() on schema)
 * - Progress tracking
 * - Data persistence across steps
 */

import type { Slots } from 'vue'
import type { FieldBuilder, SchemaDefinition } from './form-flow'
import { computed, ref, useSlots, watch } from 'vue'

import { useI18n } from '../i18n'
import { parseQuery } from '../utils/queryFilter'

import FormFlow from './FormFlow.vue'

const props = withDefaults(defineProps<Props>(), {
	validateOnNext: true
})

const emit = defineEmits<{
	'update:modelValue': [value: Record<string, any>]
	'update:currentStep': [step: string]
	'stepChange': [step: string]
	'complete': []
	'validationError': [errors: Record<string, string>]
}>()

const { $t } = useI18n()
const slots: Slots = useSlots()
const slotNames = computed((): string[] => Object.keys(slots))

export interface Props {
	schema: Record<string, SchemaDefinition>
	modelValue?: Record<string, any>
	currentStep?: string
	components?: Record<string, any>
	/** Whether to validate required fields before allowing next step */
	validateOnNext?: boolean
}

// Local form state - initialize with empty object if undefined
const formData = ref({ ...(props.modelValue || {}) }) as any

// Current step state
const activeStep = ref((props.currentStep != null && props.currentStep !== '') ? props.currentStep : Object.keys(props.schema)[0])

// Track if we're updating internally to prevent loops
let isInternalUpdate = false

// Sync with parent
watch(() => props.modelValue, (newVal) => {
	if (!isInternalUpdate) {
		formData.value = { ...(newVal || {}) }
	}
}, { deep: true })

watch(formData, (newVal) => {
	isInternalUpdate = true
	emit('update:modelValue', newVal)
	// Reset flag on next tick
	setTimeout(() => {
		isInternalUpdate = false
	}, 0)
}, { deep: true })

watch(() => props.currentStep, (newStep) => {
	if (newStep != null) {
		activeStep.value = newStep
	}
})

watch(activeStep, (newStep) => {
	emit('update:currentStep', newStep)
	emit('stepChange', newStep)
})

// Get nested value
function getNestedValue(obj: any, path: string): any {
	const keys = path.split('.')
	let value = obj
	for (const key of keys) {
		value = value?.[key]
	}
	return value
}

// Evaluate SCIM condition
function evaluateCondition(condition: string, data: any): boolean {
	if (!condition) return true

	try {
		const conditions = parseQuery(condition)
		if (conditions.length === 0) return true

		let result = true
		let previousResult: boolean | null = null

		for (let i = 0; i < conditions.length; i++) {
			const cond = conditions[i]
			const fieldValue = getNestedValue(data, cond.field)
			const { op, value, connector } = cond

			let conditionResult = false

			switch (op) {
				case 'eq':
					conditionResult = fieldValue === value
					break
				case 'ne':
					conditionResult = fieldValue !== value
					break
				case 'gt':
					conditionResult = value != null && fieldValue > value
					break
				case 'ge':
					conditionResult = value != null && fieldValue >= value
					break
				case 'lt':
					conditionResult = value != null && fieldValue < value
					break
				case 'le':
					conditionResult = value != null && fieldValue <= value
					break
				case 'co':
					conditionResult = value != null && String(fieldValue).includes(String(value))
					break
				case 'sw':
					conditionResult = value != null && String(fieldValue).startsWith(String(value))
					break
				case 'ew':
					conditionResult = value != null && String(fieldValue).endsWith(String(value))
					break
				case 'pr':
					conditionResult = fieldValue != null
					break
				default:
					conditionResult = false
			}

			// First condition
			if (i === 0) {
				result = conditionResult
				previousResult = conditionResult
			} else {
				// Apply connector from current condition
				if (connector === 'or') {
					result = (previousResult ?? false) || conditionResult
				} else if (connector === 'and') {
					result = (previousResult ?? false) && conditionResult
				}
				previousResult = result
			}
		}

		return result
	} catch (error) {
		console.warn('Failed to evaluate condition:', condition, error)
		return true
	}
}

// Check if step should be shown
function shouldShowStep(stepSchema: SchemaDefinition): boolean {
	if (stepSchema._condition == null) return true
	return evaluateCondition(stepSchema._condition, formData.value)
}

// Get visible steps with their keys
const visibleSteps = computed(() => {
	return Object.entries(props.schema)
		.filter(([, stepSchema]) => shouldShowStep(stepSchema))
		.map(([key, stepSchema]) => ({ key, schema: stepSchema }))
})

// Get current step index
const currentStepIndex = computed(() => {
	return visibleSteps.value.findIndex(s => s.key === activeStep.value)
})

// Check if at first/last step
const isFirstStep = computed(() => currentStepIndex.value === 0)
const isLastStep = computed(() => currentStepIndex.value === visibleSteps.value.length - 1)

// Current step schema
const currentStepSchema = computed(() => {
	const step = visibleSteps.value[currentStepIndex.value]
	return step?.schema
})

// Validation errors for current step
const stepErrors = ref<Record<string, string>>({})

// Check if a field should be validated (visible based on condition)
function shouldValidateField(field: FieldBuilder): boolean {
	if (field._condition == null) return true
	return evaluateCondition(field._condition, formData.value)
}

// Get field value, supporting nested paths
function getFieldValue(key: string): any {
	// Handle array notation like 'items[0].name'
	const normalizedKey = key.replace(/\[(\d+)\]/g, '.$1')
	return getNestedValue(formData.value, normalizedKey)
}

// Check if a value is empty
function isEmpty(value: any): boolean {
	if (value == null) return true
	if (typeof value === 'string' && value.trim() === '') return true
	if (Array.isArray(value) && value.length === 0) return true
	return false
}

// Validate current step fields
function validateCurrentStep(): boolean {
	if (!props.validateOnNext) return true
	if (currentStepSchema.value == null) return true

	const errors: Record<string, string> = {}
	const fields = currentStepSchema.value._fields

	for (const [key, field] of Object.entries(fields)) {
		// Skip display-only fields
		if (field._type === 'display') continue

		// Skip fields that shouldn't be shown (conditional)
		if (!shouldValidateField(field)) continue

		const value = getFieldValue(key)
		const config = field._config

		// Check required
		if (config.required === true && isEmpty(value)) {
			const label = typeof config.label === 'string' ? config.label : key
			errors[key] = `${label} is required`
			continue
		}

		// Run custom validations if value is present
		if (field._validations && !isEmpty(value)) {
			for (const validateFn of field._validations) {
				const result = validateFn(value)
				if (result !== true) {
					errors[key] = result
					break
				}
			}
		}
	}

	stepErrors.value = errors

	if (Object.keys(errors).length > 0) {
		emit('validationError', errors)
		return false
	}

	return true
}

// Generate label from step key
function generateStepLabel(key: string, stepSchema: SchemaDefinition): string {
	if (stepSchema._label != null) return stepSchema._label

	return key
		.split(/[._-]/)
		.map(word => word.charAt(0).toUpperCase() + word.slice(1))
		.join(' ')
}

// Navigation
function goToStep(stepKey: string) {
	const targetIndex = visibleSteps.value.findIndex(s => s.key === stepKey)
	if (targetIndex === -1) return

	// Allow going backwards without validation
	if (targetIndex < currentStepIndex.value) {
		activeStep.value = stepKey
		stepErrors.value = {} // Clear errors when going back
		return
	}

	// For forward navigation, validate current step first
	if (targetIndex > currentStepIndex.value) {
		if (!validateCurrentStep()) return
	}

	activeStep.value = stepKey
}

function nextStep() {
	// Validate before moving forward
	if (!validateCurrentStep()) return

	if (!isLastStep.value) {
		const nextIndex = currentStepIndex.value + 1
		activeStep.value = visibleSteps.value[nextIndex].key
		stepErrors.value = {} // Clear errors for new step
	} else {
		emit('complete')
	}
}

function prevStep() {
	if (!isFirstStep.value) {
		const prevIndex = currentStepIndex.value - 1
		activeStep.value = visibleSteps.value[prevIndex].key
		stepErrors.value = {} // Clear errors when going back
	}
}

// Expose validation method for external use
defineExpose({
	validateCurrentStep,
	stepErrors,
	nextStep,
	prevStep,
	goToStep,
	visibleSteps,
	activeStep,
	currentStepIndex,
	isFirstStep,
	isLastStep
})
</script>

<template>
	<div class="multi-step-form">
		<!-- Step Progress -->
		<div class="step-progress">
			<div
				v-for="(step, idx) in visibleSteps" :key="step.key" class="step-indicator" :class="{
					active: step.key === activeStep,
					completed: idx < currentStepIndex,
				}" @click="goToStep(step.key)"
			>
				<div class="step-number">
					{{ idx + 1 }}
				</div>
				<div class="step-label">
					{{ generateStepLabel(step.key, step.schema) }}
				</div>
			</div>
		</div>

		<!-- Current Step Content -->
		<div v-if="currentStepSchema != null">
			<h2 v-if="currentStepSchema._label" class="step-title">
				{{ currentStepSchema._label }}
			</h2>

			<!-- Validation errors summary -->
			<div v-if="Object.keys(stepErrors).length > 0" class="validation-errors">
				<ul>
					<li v-for="(error, key) in stepErrors" :key="key">
						{{ error }}
					</li>
				</ul>
			</div>

			<!-- Slot for custom step rendering -->
			<slot
				:name="`step:${activeStep}`" :schema="currentStepSchema" :formData="formData" :stepKey="activeStep"
				:errors="stepErrors"
			>
				<!-- Default: Use FormFlow -->
				<FormFlow v-model="formData" :schema="currentStepSchema" :components="components" :errors="stepErrors">
					<!-- Pass through field slots -->
				<template v-for="slotName in slotNames" :key="slotName" #[slotName]="slotProps">
					<slot :name="slotName" v-bind="slotProps" />
					</template>
				</FormFlow>
			</slot>
		</div>

		<!-- Navigation Buttons -->
		<div class="step-navigation">
			<button type="button" class="btn-secondary" :disabled="isFirstStep" @click="prevStep">
				{{ $t('multiStep.previous') }}
			</button>

			<div class="step-counter">
				{{ $t('multiStep.step', { current: currentStepIndex + 1, total: visibleSteps.length }) }}
			</div>

			<button type="button" class="btn-primary" @click="nextStep">
				{{ isLastStep ? $t('multiStep.complete') : $t('multiStep.next') }}
			</button>
		</div>
	</div>
</template>

<style scoped>

.multi-step-form {
display: flex;
flex-direction: column;
gap: 2rem;
}

.step-progress {
display: flex;
justify-content: space-between;
position: relative;
padding: 0 1rem;
}

.step-progress::before {
content: '';
position: absolute;
top: 20px;
left: 0;
right: 0;
height: 2px;
background: #e0e0e0;
z-index: 0;
}

.step-indicator {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
cursor: pointer;
position: relative;
z-index: 1;
flex: 1;
min-width: 0;
}

.step-number {
width: 40px;
height: 40px;
border-radius: 50%;
background: white;
border: 2px solid #e0e0e0;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
transition: all 0.2s;
}

.step-indicator.active .step-number {
background: var(--primary-color, #007bff);
color: white;
border-color: var(--primary-color, #007bff);
}

.step-indicator.completed .step-number {
background: var(--success-color, #28a745);
color: white;
border-color: var(--success-color, #28a745);
}

.step-label {
font-size: 0.875rem;
color: #666;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}

.step-indicator.active .step-label {
color: var(--primary-color, #007bff);
font-weight: 600;
}

.step-title {
margin: 0 0 1.5rem 0;
font-size: 1.5rem;
color: #333;
}

.step-navigation {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
}

.step-counter {
font-size: 0.875rem;
color: #666;
}

button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}

.btn-primary {
background: var(--primary-color, #007bff);
color: white;
}

.btn-primary:hover:not(:disabled) {
background: var(--primary-dark, #0056b3);
}

.btn-secondary {
background: white;
color: var(--primary-color, #007bff);
border: 1px solid var(--primary-color, #007bff);
}

.btn-secondary:hover:not(:disabled) {
background: var(--primary-light, #f0f7ff);
}

button:disabled {
opacity: 0.5;
cursor: not-allowed;
}

.validation-errors {
background: #fff5f5;
border: 1px solid #fc8181;
border-radius: 4px;
padding: 1rem;
margin-bottom: 1rem;
}

.validation-errors ul {
margin: 0;
padding-left: 1.25rem;
color: #c53030;
}

.validation-errors li {
margin-bottom: 0.25rem;
}

.validation-errors li:last-child {
margin-bottom: 0;
}

@media (max-width: 768px) {
.step-label {
	display: none;
}
}

</style>
