package dev.lions.user.manager.server.impl.mapper;

import dev.lions.user.manager.dto.audit.AuditLogDTO;
import dev.lions.user.manager.server.impl.entity.AuditLogEntity;
import org.mapstruct.*;

import java.util.List;

/**
 * Mapper MapStruct pour la conversion entre AuditLogEntity (JPA) et AuditLogDTO (API).
 *
 * <p>Ce mapper gère la transformation bidirectionnelle entre l'entité de persistance
 * et le DTO exposé via l'API REST, avec mapping automatique des champs compatibles.</p>
 *
 * <p><b>Fonctionnalités:</b></p>
 * <ul>
 *   <li>Conversion Entity → DTO pour lecture/API</li>
 *   <li>Conversion DTO → Entity pour persistance</li>
 *   <li>Mapping de listes pour opérations bulk</li>
 *   <li>Gestion automatique des types LocalDateTime</li>
 *   <li>Mapping des enums (TypeActionAudit)</li>
 * </ul>
 *
 * <p><b>Utilisation:</b></p>
 * <pre>
 * {@literal @}Inject
 * AuditLogMapper mapper;
 *
 * // Entity → DTO
 * AuditLogDTO dto = mapper.toDTO(entity);
 *
 * // DTO → Entity
 * AuditLogEntity entity = mapper.toEntity(dto);
 *
 * // Liste Entity → Liste DTO
 * List&lt;AuditLogDTO&gt; dtos = mapper.toDTOList(entities);
 * </pre>
 *
 * @see AuditLogEntity
 * @see AuditLogDTO
 * @author Lions Development Team
 * @version 1.0.0
 * @since 2026-01-02
 */
@Mapper(
    componentModel = MappingConstants.ComponentModel.JAKARTA_CDI,
    injectionStrategy = InjectionStrategy.CONSTRUCTOR,
    unmappedTargetPolicy = ReportingPolicy.IGNORE
)
public interface AuditLogMapper {

    /**
     * Convertit une entité AuditLogEntity en DTO AuditLogDTO.
     *
     * <p>Mapping des champs Entity → DTO:</p>
     * <ul>
     *   <li>id (Long) → id (String)</li>
     *   <li>userId → ressourceId</li>
     *   <li>action → typeAction</li>
     *   <li>details → description</li>
     *   <li>auteurAction → acteurUsername</li>
     *   <li>timestamp → dateAction</li>
     *   <li>ipAddress → ipAddress</li>
     *   <li>userAgent → userAgent</li>
     *   <li>realmName → realmName</li>
     *   <li>success → success</li>
     *   <li>errorMessage → errorMessage</li>
     * </ul>
     *
     * @param entity L'entité JPA à convertir (peut être null)
     * @return Le DTO correspondant, ou null si l'entité est null
     */
    @Mapping(target = "id", source = "id", qualifiedByName = "longToString")
    @Mapping(target = "ressourceId", source = "userId")
    @Mapping(target = "typeAction", source = "action")
    @Mapping(target = "description", source = "details")
    @Mapping(target = "acteurUsername", source = "auteurAction")
    @Mapping(target = "dateAction", source = "timestamp")
    AuditLogDTO toDTO(AuditLogEntity entity);

    /**
     * Convertit un DTO AuditLogDTO en entité AuditLogEntity.
     *
     * <p>Utilisé pour créer une nouvelle entité à persister depuis les données API.</p>
     *
     * <p><b>Note:</b> L'ID de l'entité sera null (auto-généré par la DB),
     * même si l'ID du DTO est renseigné.</p>
     *
     * @param dto Le DTO à convertir (peut être null)
     * @return L'entité JPA correspondante, ou null si le DTO est null
     */
    @Mapping(target = "id", ignore = true) // L'ID sera généré par la DB
    @Mapping(target = "userId", source = "ressourceId")
    @Mapping(target = "action", source = "typeAction")
    @Mapping(target = "details", source = "description")
    @Mapping(target = "auteurAction", source = "acteurUsername")
    @Mapping(target = "timestamp", source = "dateAction")
    AuditLogEntity toEntity(AuditLogDTO dto);

    /**
     * Convertit une liste d'entités en liste de DTOs.
     *
     * <p>Utile pour les recherches qui retournent plusieurs résultats.</p>
     *
     * @param entities Liste des entités à convertir (peut être null ou vide)
     * @return Liste des DTOs correspondants, ou liste vide si entities est null/vide
     */
    List<AuditLogDTO> toDTOList(List<AuditLogEntity> entities);

    /**
     * Convertit une liste de DTOs en liste d'entités.
     *
     * <p>Utile pour les opérations d'import ou de création en masse.</p>
     *
     * @param dtos Liste des DTOs à convertir (peut être null ou vide)
     * @return Liste des entités correspondantes, ou liste vide si dtos est null/vide
     */
    List<AuditLogEntity> toEntityList(List<AuditLogDTO> dtos);

    /**
     * Met à jour une entité existante avec les données d'un DTO.
     *
     * <p>Préserve l'ID de l'entité et ne met à jour que les champs
     * présents dans le DTO.</p>
     *
     * <p><b>Utilisation:</b></p>
     * <pre>
     * AuditLogEntity existingEntity = AuditLogEntity.findById(id);
     * mapper.updateEntityFromDTO(dto, existingEntity);
     * existingEntity.persist();
     * </pre>
     *
     * @param dto Le DTO source contenant les nouvelles valeurs
     * @param entity L'entité cible à mettre à jour
     */
    @Mapping(target = "id", ignore = true) // Préserve l'ID existant
    @Mapping(target = "userId", source = "ressourceId")
    @Mapping(target = "action", source = "typeAction")
    @Mapping(target = "details", source = "description")
    @Mapping(target = "auteurAction", source = "acteurUsername")
    @Mapping(target = "timestamp", source = "dateAction")
    @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    void updateEntityFromDTO(AuditLogDTO dto, @MappingTarget AuditLogEntity entity);

    /**
     * Convertit un Long (ID de l'entité) en String (ID du DTO).
     *
     * <p>MapStruct appelle automatiquement cette méthode pour le mapping de l'ID.</p>
     *
     * @param id L'ID de type Long (peut être null)
     * @return L'ID converti en String, ou null si l'input est null
     */
    @Named("longToString")
    default String longToString(Long id) {
        return id != null ? id.toString() : null;
    }

    /**
     * Convertit un String (ID du DTO) en Long (ID de l'entité).
     *
     * <p>Utilisé lors de la conversion DTO → Entity si nécessaire.</p>
     *
     * @param id L'ID de type String (peut être null)
     * @return L'ID converti en Long, ou null si l'input est null ou invalide
     */
    @Named("stringToLong")
    default Long stringToLong(String id) {
        if (id == null || id.isBlank()) {
            return null;
        }
        try {
            return Long.parseLong(id);
        } catch (NumberFormatException e) {
            // Log warning et retourne null en cas de format invalide
            System.err.println("WARN: Invalid ID format for conversion to Long: " + id);
            return null;
        }
    }
}
