/**
 * ThrillWiki Form Validation Module
 *
 * Provides client-side form validation helpers with HTMX integration.
 * Works with Alpine.js form components and Django form fields.
 *
 * Features:
 * - Debounced HTMX validation triggers
 * - Field state management (pristine, dirty, valid, invalid)
 * - Real-time validation feedback
 * - Integration with Alpine.js stores
 * - Accessible error announcements
 */

// =============================================================================
// Form Field State Management
// =============================================================================

/**
 * Field state constants
 */
const FieldState = {
    PRISTINE: 'pristine',      // Field has not been touched
    DIRTY: 'dirty',            // Field has been modified
    VALIDATING: 'validating',  // Field is being validated
    VALID: 'valid',            // Field passed validation
    INVALID: 'invalid',        // Field failed validation
};

/**
 * FormValidator class for managing form validation state
 */
class FormValidator {
    constructor(formElement, options = {}) {
        this.form = formElement;
        this.options = {
            validateOnBlur: true,
            validateOnChange: true,
            debounceMs: 500,
            showSuccessState: true,
            scrollToFirstError: true,
            ...options,
        };

        this.fields = new Map();
        this.debounceTimers = new Map();

        this.init();
    }

    /**
     * Initialize form validation
     */
    init() {
        if (!this.form) return;

        // Find all form fields
        const inputs = this.form.querySelectorAll('input, textarea, select');
        inputs.forEach(input => {
            if (input.name) {
                this.registerField(input);
            }
        });

        // Prevent default form submission if validation fails
        this.form.addEventListener('submit', (e) => {
            if (!this.validateAll()) {
                e.preventDefault();
                if (this.options.scrollToFirstError) {
                    this.scrollToFirstError();
                }
            }
        });
    }

    /**
     * Register a field for validation
     */
    registerField(input) {
        const fieldName = input.name;

        this.fields.set(fieldName, {
            element: input,
            state: FieldState.PRISTINE,
            errors: [],
            touched: false,
        });

        // Add event listeners
        if (this.options.validateOnBlur) {
            input.addEventListener('blur', () => this.onFieldBlur(fieldName));
        }

        if (this.options.validateOnChange) {
            input.addEventListener('input', () => this.onFieldChange(fieldName));
        }
    }

    /**
     * Handle field blur event
     */
    onFieldBlur(fieldName) {
        const field = this.fields.get(fieldName);
        if (!field) return;

        field.touched = true;
        this.validateField(fieldName);
    }

    /**
     * Handle field change event with debouncing
     */
    onFieldChange(fieldName) {
        const field = this.fields.get(fieldName);
        if (!field) return;

        field.state = FieldState.DIRTY;
        this.updateFieldUI(fieldName);

        // Debounce validation
        clearTimeout(this.debounceTimers.get(fieldName));
        this.debounceTimers.set(fieldName, setTimeout(() => {
            if (field.touched) {
                this.validateField(fieldName);
            }
        }, this.options.debounceMs));
    }

    /**
     * Validate a single field
     */
    validateField(fieldName) {
        const field = this.fields.get(fieldName);
        if (!field) return true;

        field.state = FieldState.VALIDATING;
        this.updateFieldUI(fieldName);

        const input = field.element;
        const errors = [];

        // Built-in HTML5 validation
        if (!input.validity.valid) {
            if (input.validity.valueMissing) {
                errors.push(`${this.getFieldLabel(fieldName)} is required`);
            }
            if (input.validity.typeMismatch) {
                errors.push(`Please enter a valid ${input.type}`);
            }
            if (input.validity.tooShort) {
                errors.push(`Must be at least ${input.minLength} characters`);
            }
            if (input.validity.tooLong) {
                errors.push(`Must be no more than ${input.maxLength} characters`);
            }
            if (input.validity.patternMismatch) {
                errors.push(input.title || 'Please match the requested format');
            }
            if (input.validity.rangeUnderflow) {
                errors.push(`Must be at least ${input.min}`);
            }
            if (input.validity.rangeOverflow) {
                errors.push(`Must be no more than ${input.max}`);
            }
        }

        // Custom validation rules from data attributes
        if (input.dataset.validateUrl && input.value) {
            try {
                new URL(input.value);
            } catch {
                errors.push('Please enter a valid URL');
            }
        }

        if (input.dataset.validateMatch) {
            const matchField = this.form.querySelector(`[name="${input.dataset.validateMatch}"]`);
            if (matchField && input.value !== matchField.value) {
                errors.push(`Must match ${this.getFieldLabel(input.dataset.validateMatch)}`);
            }
        }

        // Update field state
        field.errors = errors;
        field.state = errors.length > 0 ? FieldState.INVALID : FieldState.VALID;
        this.updateFieldUI(fieldName);

        return errors.length === 0;
    }

    /**
     * Validate all fields
     */
    validateAll() {
        let isValid = true;

        this.fields.forEach((field, fieldName) => {
            field.touched = true;
            if (!this.validateField(fieldName)) {
                isValid = false;
            }
        });

        return isValid;
    }

    /**
     * Update field UI based on state
     */
    updateFieldUI(fieldName) {
        const field = this.fields.get(fieldName);
        if (!field) return;

        const input = field.element;
        const wrapper = input.closest('.form-field');
        const feedback = wrapper?.querySelector('.field-feedback');

        // Remove all state classes
        input.classList.remove(
            'border-red-500', 'border-green-500', 'border-gray-300',
            'focus:ring-red-500', 'focus:ring-green-500', 'focus:ring-blue-500'
        );

        // Set aria attributes
        input.setAttribute('aria-invalid', field.state === FieldState.INVALID);

        switch (field.state) {
            case FieldState.VALIDATING:
                input.classList.add('border-blue-300');
                break;

            case FieldState.INVALID:
                input.classList.add('border-red-500', 'focus:ring-red-500');
                if (feedback) {
                    feedback.innerHTML = this.renderErrors(field.errors);
                }
                break;

            case FieldState.VALID:
                if (this.options.showSuccessState && field.touched) {
                    input.classList.add('border-green-500', 'focus:ring-green-500');
                    if (feedback) {
                        feedback.innerHTML = this.renderSuccess();
                    }
                }
                break;

            default:
                input.classList.add('border-gray-300', 'focus:ring-blue-500');
        }
    }

    /**
     * Render error messages HTML
     */
    renderErrors(errors) {
        if (errors.length === 0) return '';

        return `
            <ul class="text-sm text-red-600 dark:text-red-400 space-y-1 animate-slide-down" role="alert" aria-live="assertive">
                ${errors.map(error => `
                    <li class="flex items-start gap-1.5">
                        <i class="fas fa-exclamation-circle mt-0.5 flex-shrink-0" aria-hidden="true"></i>
                        <span>${error}</span>
                    </li>
                `).join('')}
            </ul>
        `;
    }

    /**
     * Render success indicator HTML
     */
    renderSuccess() {
        return `
            <div class="text-sm text-green-600 dark:text-green-400 flex items-center gap-1.5 animate-slide-down" role="status">
                <i class="fas fa-check-circle flex-shrink-0" aria-hidden="true"></i>
            </div>
        `;
    }

    /**
     * Get field label text
     */
    getFieldLabel(fieldName) {
        const field = this.fields.get(fieldName);
        if (!field) return fieldName;

        const wrapper = field.element.closest('.form-field');
        const label = wrapper?.querySelector('label');
        return label?.textContent?.replace('*', '').trim() || fieldName;
    }

    /**
     * Scroll to first error
     */
    scrollToFirstError() {
        for (const [fieldName, field] of this.fields) {
            if (field.state === FieldState.INVALID) {
                field.element.scrollIntoView({ behavior: 'smooth', block: 'center' });
                field.element.focus();
                break;
            }
        }
    }

    /**
     * Set external errors (from server)
     */
    setServerErrors(errors) {
        Object.entries(errors).forEach(([fieldName, messages]) => {
            const field = this.fields.get(fieldName);
            if (field) {
                field.errors = Array.isArray(messages) ? messages : [messages];
                field.state = FieldState.INVALID;
                field.touched = true;
                this.updateFieldUI(fieldName);
            }
        });
    }

    /**
     * Clear all errors
     */
    clearErrors() {
        this.fields.forEach((field, fieldName) => {
            field.errors = [];
            field.state = FieldState.PRISTINE;
            this.updateFieldUI(fieldName);
        });
    }

    /**
     * Reset form to initial state
     */
    reset() {
        this.clearErrors();
        this.fields.forEach(field => {
            field.touched = false;
        });
        this.form.reset();
    }
}

// =============================================================================
// HTMX Validation Integration
// =============================================================================

/**
 * Setup HTMX validation for a field
 * @param {HTMLElement} input - The input element
 * @param {string} validateUrl - URL for validation endpoint
 * @param {object} options - Configuration options
 */
function setupHTMXValidation(input, validateUrl, options = {}) {
    const defaults = {
        trigger: 'blur changed delay:500ms',
        indicator: true,
        targetSelector: null,
    };

    const config = { ...defaults, ...options };

    // Generate target ID
    const targetId = config.targetSelector || `#${input.name}-feedback`;

    // Set HTMX attributes
    input.setAttribute('hx-post', validateUrl);
    input.setAttribute('hx-trigger', config.trigger);
    input.setAttribute('hx-target', targetId);
    input.setAttribute('hx-swap', 'innerHTML');

    if (config.indicator) {
        const indicatorId = `#${input.name}-indicator`;
        input.setAttribute('hx-indicator', indicatorId);

        // Create indicator if it doesn't exist
        const wrapper = input.closest('.form-field');
        if (wrapper && !wrapper.querySelector(indicatorId)) {
            const indicator = document.createElement('span');
            indicator.id = `${input.name}-indicator`;
            indicator.className = 'htmx-indicator absolute right-3 top-1/2 -translate-y-1/2';
            indicator.innerHTML = '<i class="fas fa-spinner fa-spin text-gray-400" aria-hidden="true"></i>';
            const inputWrapper = wrapper.querySelector('.relative') || wrapper;
            inputWrapper.appendChild(indicator);
        }
    }

    // Process HTMX attributes
    if (typeof htmx !== 'undefined') {
        htmx.process(input);
    }
}

// =============================================================================
// Alpine.js Integration
// =============================================================================

/**
 * Alpine.js form validation component
 * Usage: x-data="formValidation('/api/validate/')"
 */
document.addEventListener('alpine:init', () => {
    if (typeof Alpine === 'undefined') return;

    Alpine.data('formValidation', (validateUrl = null) => ({
        fields: {},
        errors: {},
        touched: {},
        validating: {},
        submitted: false,

        init() {
            // Find all form fields within this component
            this.$el.querySelectorAll('input, textarea, select').forEach(input => {
                if (input.name) {
                    this.fields[input.name] = input.value;
                    this.errors[input.name] = [];
                    this.touched[input.name] = false;
                    this.validating[input.name] = false;
                }
            });
        },

        async validateField(fieldName) {
            if (!validateUrl) return true;

            this.validating[fieldName] = true;

            try {
                const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content ||
                                  document.querySelector('[name=csrfmiddlewaretoken]')?.value;

                const response = await fetch(validateUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-CSRFToken': csrfToken,
                    },
                    body: JSON.stringify({
                        field: fieldName,
                        value: this.fields[fieldName],
                    }),
                });

                const data = await response.json();

                if (data.errors && data.errors[fieldName]) {
                    this.errors[fieldName] = data.errors[fieldName];
                    return false;
                } else {
                    this.errors[fieldName] = [];
                    return true;
                }
            } catch (error) {
                console.error('Validation error:', error);
                return true; // Don't block on network errors
            } finally {
                this.validating[fieldName] = false;
            }
        },

        onBlur(fieldName) {
            this.touched[fieldName] = true;
            this.validateField(fieldName);
        },

        hasError(fieldName) {
            return this.touched[fieldName] && this.errors[fieldName]?.length > 0;
        },

        isValid(fieldName) {
            return this.touched[fieldName] && this.errors[fieldName]?.length === 0;
        },

        getErrors(fieldName) {
            return this.errors[fieldName] || [];
        },

        setServerErrors(errors) {
            Object.entries(errors).forEach(([field, messages]) => {
                this.errors[field] = Array.isArray(messages) ? messages : [messages];
                this.touched[field] = true;
            });
        },

        clearErrors() {
            Object.keys(this.errors).forEach(field => {
                this.errors[field] = [];
            });
        },

        async validateAll() {
            let isValid = true;

            for (const fieldName of Object.keys(this.fields)) {
                this.touched[fieldName] = true;
                const fieldValid = await this.validateField(fieldName);
                if (!fieldValid) isValid = false;
            }

            return isValid;
        },

        async submitForm(url, options = {}) {
            this.submitted = true;

            const isValid = await this.validateAll();
            if (!isValid) {
                return { success: false, errors: this.errors };
            }

            try {
                const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content ||
                                  document.querySelector('[name=csrfmiddlewaretoken]')?.value;

                const response = await fetch(url, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'X-CSRFToken': csrfToken,
                        ...options.headers,
                    },
                    body: JSON.stringify(this.fields),
                });

                const data = await response.json();

                if (!response.ok) {
                    if (data.errors) {
                        this.setServerErrors(data.errors);
                    }
                    return { success: false, errors: data.errors || data };
                }

                return { success: true, data };
            } catch (error) {
                console.error('Form submission error:', error);
                return { success: false, error: error.message };
            }
        },
    }));
});

// =============================================================================
// Global Exports
// =============================================================================

window.FormValidator = FormValidator;
window.setupHTMXValidation = setupHTMXValidation;
window.FieldState = FieldState;
