MapperHelper.java

package com.mojosoft.demo.mapper;

import com.mojosoft.demo.dto.FootballResultDTO;
import com.mojosoft.demo.dto.FootballResultJsonDTO;
import com.mojosoft.demo.entity.FootballResult;
import com.mojosoft.demo.util.SeasonHelper;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;

/**
 * Mapper interface for converting between FootballResult entity and its DTOs
 * (FootballResultDTO and FootballResultJsonDTO). This interface leverages MapStruct for generating
 * the mapping implementation at compile time, ensuring efficient and type-safe mappings.
 *
 * <p>The interface is annotated with @Mapper to be identified by MapStruct for
 * implementation generation. The componentModel 'spring' indicates that MapStruct
 * should generate a Spring Bean for this mapper, allowing it to be autowired into
 * other Spring components.
 *
 * <p>The default methods isEqual and updateFootballResultEntityFromDto provide custom logic
 * for comparing entities and updating them from DTOs.
 */
@SuppressWarnings("checkstyle:LineLength")
@Mapper(componentModel = "spring")
public interface MapperHelper {

  /**
   * Maps a FootballResult entity to its corresponding FootballResultDTO.
   *
   * @param footballResult the FootballResult entity to be mapped
   * @return the mapped FootballResultDTO
   */
  FootballResultDTO footballResultToDto(FootballResult footballResult);

  /**
   * Maps a FootballResultDTO to its corresponding FootballResult entity.
   * The id field is ignored during mapping to prevent overwriting it in the entity.
   *
   * @param footballResultDTO the FootballResultDTO to be mapped
   * @return the mapped FootballResult entity
   */
  @SuppressWarnings("checkstyle:AbbreviationAsWordInName")
  @Mapping(target = "id", ignore = true)
  FootballResult footballResultToEntity(FootballResultDTO footballResultDTO);

  /**
   * Maps a FootballResultJsonDTO to a FootballResult entity.
   * Custom mappings are defined for certain fields, and the season is calculated and set in a separate method.
   *
   * @param footballResultJsonDto the FootballResultJsonDTO to be mapped
   * @return the mapped FootballResult entity
   */
  @Mapping(target = "id", ignore = true)
  @Mapping(source = "dateUtc", target = "matchDateTime", qualifiedByName = "stringToLocalDateTime")
  @Mapping(source = "homeTeam", target = "homeTeam")
  @Mapping(source = "awayTeam", target = "awayTeam")
  @Mapping(source = "homeTeamScore", target = "homeScore")
  @Mapping(source = "awayTeamScore", target = "awayScore")
  @Mapping(target = "season", ignore = true)
  FootballResult footballMatchJsonToEntity(FootballResultJsonDTO footballResultJsonDto);

  /**
   * Parses a date-time string into a {@link LocalDateTime} object, supporting two formats:
   * - ISO 8601 format: "yyyy-MM-ddTHH:mm:ssZ" (e.g., "2020-09-12T11:30:00Z")
   * - Custom format: "yyyy-MM-dd HH:mm:ssZ" (e.g., "2020-09-12 11:30:00Z")
   *
   * @param dateTimeStr The date-time string to parse.
   * @return A {@link LocalDateTime} object representing the parsed date and time.
   */
  @org.mapstruct.Named("stringToLocalDateTime")
  default LocalDateTime stringToLocalDateTime(String dateTimeStr) {

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss'Z'");
    try {
      return LocalDateTime.parse(dateTimeStr, formatter);
    } catch (Exception e) {
      return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ISO_DATE_TIME);
    }
  }



  /**
   * AfterMapping method to set the season of a FootballResult entity based on its matchDateTime.
   * Utilizes SeasonHelper to calculate the season.
   *
   * @param footballResult the target FootballResult entity whose season is to be set
   */
  @AfterMapping
  default void setSeason(@MappingTarget FootballResult footballResult) {
    if (footballResult.getMatchDateTime() != null) {
      footballResult.setSeason(SeasonHelper.calculateSeason(footballResult.getMatchDateTime()));
    }
  }

  /**
   * Default method to map a FootballResultJsonDTO to a FootballResultDTO.
   * This method serves as a convenient way to transform JSON data to the standard DTO format.
   *
   * @param footballResultJsonDto the FootballResultJsonDTO to be mapped
   * @return the mapped FootballResultDTO
   */
  default FootballResultDTO footballMatchJsonToDto(FootballResultJsonDTO footballResultJsonDto) {
    return FootballResultDTO.of(
        footballResultJsonDto.getLocation(),
        footballResultJsonDto.getHomeTeam(),
        footballResultJsonDto.getAwayTeam(),
        footballResultJsonDto.getHomeTeamScore(),
        footballResultJsonDto.getAwayTeamScore(),
        stringToLocalDateTime(footballResultJsonDto.getDateUtc()));
  }

  /**
   * Compares a FootballResultDTO with a FootballResult entity to determine if they are equal.
   * This comparison considers all the fields in the DTO and entity.
   *
   * @param dto    the FootballResultDTO to compare
   * @param entity the FootballResult entity to compare
   * @return true if the DTO and entity are equal, false otherwise
   */
  default boolean isEqual(FootballResultDTO dto, FootballResult entity) {
    if (dto == null && entity == null) {
      return true;
    }
    if (dto == null || entity == null) {
      return false;
    }
    return Objects.equals(dto.season(), entity.getSeason())
        && Objects.equals(dto.location(), entity.getLocation())
        && Objects.equals(dto.homeTeam(), entity.getHomeTeam())
        && Objects.equals(dto.awayTeam(), entity.getAwayTeam())
        && dto.homeScore() == entity.getHomeScore()
        && dto.awayScore() == entity.getAwayScore()
        && Objects.equals(dto.matchDateTime(), entity.getMatchDateTime());
  }

  /**
   * Updates a FootballResult entity with data from a FootballResultDTO.
   * This method is used for updating an existing entity with new information from the DTO.
   *
   * @param dto    the FootballResultDTO containing the new data
   * @param entity the FootballResult entity to be updated
   */
  default void updateFootballResultEntityFromDto(FootballResultDTO dto, FootballResult entity) {
    if (dto == null || entity == null) {
      return;
    }
    entity.setSeason(dto.season());
    entity.setLocation(dto.location());
    entity.setHomeTeam(dto.homeTeam());
    entity.setAwayTeam(dto.awayTeam());
    entity.setHomeScore(dto.homeScore());
    entity.setAwayScore(dto.awayScore());
    entity.setMatchDateTime(dto.matchDateTime());
  }
}