Source: components/UserForm.jsx

/**
 * @fileoverview Composant formulaire d'inscription utilisateur avec validation et appels API.
 * @module UserForm
 */

import React, { useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast, ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { validateAge, validatePostalCode, validateIdentity, validateEmail } from '../validator';
import { useUsers } from '../context/UserContext';

/**
 * @constant {Object.<string, string>} ERROR_MESSAGES
 * @description Mapping des codes d'erreur de validation vers les messages utilisateur affichés dans le formulaire.
 */
const ERROR_MESSAGES = {
    INVALID_INPUT: 'Champ requis',
    AGE_UNDER_18: 'Vous devez être majeur (mineur détecté)',
    INVALID_DATE_FUTURE: 'La date ne peut pas être dans le futur',
    INVALID_DATE_TOO_OLD: 'La date de naissance est invalide',
    INVALID_POSTAL_CODE_FORMAT: 'Code postal invalide (5 chiffres attendus)',
    INVALID_IDENTITY_FORMAT: 'Nom invalide (lettres, accents et tirets uniquement)',
    XSS_DETECTED: 'Contenu XSS détecté',
    INVALID_EMAIL_FORMAT: 'Email invalide'
};

/**
 * Composant de formulaire d'inscription utilisateur.
 * Gère la saisie, la validation en temps réel et la soumission via API.
 * Gère les erreurs serveur (400, 500) avec feedback utilisateur.
 *
 * @component
 * @param {Object} props - Propriétés du composant.
 * @param {function(Object): void} props.onSuccess - Callback obligatoire appelé après une inscription réussie.
 * @returns {React.JSX.Element} Le formulaire d'inscription avec validation intégrée
 */
function UserForm({ onSuccess }) {
    const { addUser } = useUsers();
    const navigate = useNavigate();

    const [formData, setFormData] = useState({
        name: '',
        firstName: '',
        email: '',
        birthDate: '',
        postalCode: '',
        city: ''
    });

    const [errors, setErrors] = useState({});
    const [touched, setTouched] = useState({});
    const [submitting, setSubmitting] = useState(false);
    const [apiError, setApiError] = useState(null);

    const validateField = useCallback((field, value) => {
        let result;
        switch (field) {
            case 'name':
            case 'firstName':
                result = validateIdentity(value);
                break;
            case 'email':
                result = validateEmail(value);
                break;
            case 'birthDate':
                result = validateAge(value);
                break;
            case 'postalCode':
                result = validatePostalCode(value);
                break;
            case 'city':
                if (!value || value.trim() === '') {
                    result = { valid: false, error: 'INVALID_INPUT' };
                } else {
                    result = { valid: true };
                }
                break;
        }
        return result;
    }, []);

    const handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(prev => ({ ...prev, [name]: value }));
        setApiError(null);

        setErrors(prev => {
            if (!prev[name] && !touched[name]) return prev;
            const result = validateField(name, value);
            const newErrors = { ...prev };
            if (result.valid) {
                delete newErrors[name];
            } else {
                newErrors[name] = ERROR_MESSAGES[result.error] || result.error;
            }
            return newErrors;
        });
    };

    const handleBlur = (e) => {
        const { name, value } = e.target;
        setTouched(prev => ({ ...prev, [name]: true }));

        if (value && value.trim() !== '') {
            const result = validateField(name, value);
            setErrors(prev => {
                const newErrors = { ...prev };
                if (result.valid) {
                    delete newErrors[name];
                } else {
                    newErrors[name] = ERROR_MESSAGES[result.error] || result.error;
                }
                return newErrors;
            });
        } else {
            setErrors(prev => {
                const newErrors = { ...prev };
                delete newErrors[name];
                return newErrors;
            });
        }
    };

    const isFormValid = () => {
        const fields = ['name', 'firstName', 'email', 'birthDate', 'postalCode', 'city'];
        for (const field of fields) {
            const result = validateField(field, formData[field]);
            if (!result.valid) return false;
        }
        return true;
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        if (!isFormValid() || submitting) return;

        setSubmitting(true);
        setApiError(null);

        try {
            await addUser(formData);
            onSuccess(formData);
            toast.success('Inscription réussie avec succès !');
            setFormData({ name: '', firstName: '', email: '', birthDate: '', postalCode: '', city: '' });
            setErrors({});
            setTouched({});

            setTimeout(() => {
                navigate('/');
            }, 1500);
        } catch (error) {
            if (error.response) {
                const status = error.response.status;
                if (status === 400) {
                    const message = error.response.data?.message || 'Cet email est déjà utilisé';
                    setApiError(message);
                    toast.error(message);
                } else if (status >= 500) {
                    setApiError('Le serveur est temporairement indisponible. Veuillez réessayer plus tard.');
                    toast.error('Erreur serveur. Veuillez réessayer plus tard.');
                } else {
                    setApiError('Une erreur est survenue');
                    toast.error('Une erreur est survenue');
                }
            } else {
                setApiError('Impossible de contacter le serveur. Vérifiez votre connexion.');
                toast.error('Erreur réseau. Vérifiez votre connexion.');
            }
        } finally {
            setSubmitting(false);
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            {apiError && (
                <div className="api-error" role="alert" data-testid="api-error">
                    {apiError}
                </div>
            )}

            <div>
                <label htmlFor="name">Nom</label>
                <input
                    id="name"
                    name="name"
                    type="text"
                    value={formData.name}
                    onChange={handleChange}
                    onBlur={handleBlur}
                />
                {errors.name && <span className="error" role="alert">{errors.name}</span>}
            </div>

            <div>
                <label htmlFor="firstName">Prénom</label>
                <input
                    id="firstName"
                    name="firstName"
                    type="text"
                    value={formData.firstName}
                    onChange={handleChange}
                    onBlur={handleBlur}
                />
                {errors.firstName && <span className="error" role="alert">{errors.firstName}</span>}
            </div>

            <div>
                <label htmlFor="email">Email</label>
                <input
                    id="email"
                    name="email"
                    type="text"
                    value={formData.email}
                    onChange={handleChange}
                    onBlur={handleBlur}
                />
                {errors.email && <span className="error" role="alert">{errors.email}</span>}
            </div>

            <div>
                <label htmlFor="birthDate">Date de naissance</label>
                <input
                    id="birthDate"
                    name="birthDate"
                    type="date"
                    value={formData.birthDate}
                    onChange={handleChange}
                    onBlur={handleBlur}
                />
                {errors.birthDate && <span className="error" role="alert">{errors.birthDate}</span>}
            </div>

            <div>
                <label htmlFor="postalCode">Code postal</label>
                <input
                    id="postalCode"
                    name="postalCode"
                    type="text"
                    value={formData.postalCode}
                    onChange={handleChange}
                    onBlur={handleBlur}
                />
                {errors.postalCode && <span className="error" role="alert">{errors.postalCode}</span>}
            </div>

            <div>
                <label htmlFor="city">Ville</label>
                <input
                    id="city"
                    name="city"
                    type="text"
                    value={formData.city}
                    onChange={handleChange}
                    onBlur={handleBlur}
                />
                {errors.city && <span className="error" role="alert">{errors.city}</span>}
            </div>

            <button type="submit" disabled={!isFormValid() || submitting}>
                {submitting ? 'Envoi en cours...' : 'Soumettre'}
            </button>

            <ToastContainer />
        </form>
    );
}

export default UserForm;