<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 { FieldBuilder, SchemaDefinition } from './form-flow'
import { computed, ref, 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()

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
})
</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="(_, name) in $slots" #[name]="slotProps">
						<slot :name="name" 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>
