package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.organisation.TypeOrganisationDTO;
import dev.lions.unionflow.server.entity.TypeOrganisationEntity;
import dev.lions.unionflow.server.repository.TypeOrganisationRepository;
import dev.lions.unionflow.server.service.KeycloakService;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;

  /**
   * Service de gestion du catalogue des types d'organisation.
   *
   * <p>Synchronise les types persistés avec l'enum {@link TypeOrganisation} pour les valeurs
   * par défaut, puis permet un CRUD entièrement dynamique (les nouveaux codes ne sont plus
   * limités aux valeurs de l'enum).
   */
@ApplicationScoped
public class TypeOrganisationService {

  private static final Logger LOG = Logger.getLogger(TypeOrganisationService.class);

  @Inject TypeOrganisationRepository repository;
  @Inject KeycloakService keycloakService;

  // Plus d'initialisation automatique : le catalogue des types est désormais entièrement
  // géré en mode CRUD via l'UI d'administration. Aucune donnée fictive n'est injectée
  // au démarrage ; si nécessaire, utilisez des scripts de migration (Flyway) ou l'UI.

  /** Retourne la liste de tous les types (optionnellement seulement actifs). */
  public List<TypeOrganisationDTO> listAll(boolean onlyActifs) {
    List<TypeOrganisationEntity> entities =
        onlyActifs ? repository.listActifsOrdennes() : repository.listAll();
    return entities.stream().map(this::toDTO).collect(Collectors.toList());
  }

  /** Crée un nouveau type. Le code doit être non vide et unique. */
  @Transactional
  public TypeOrganisationDTO create(TypeOrganisationDTO dto) {
    validateCode(dto.getCode());

    // Si un type existe déjà pour ce code, on retourne simplement l'existant
    // (comportement idempotent côté API) plutôt que de remonter une 400.
    // Le CRUD complet reste possible via l'écran d'édition.
    var existingOpt = repository.findByCode(dto.getCode());
    if (existingOpt.isPresent()) {
      LOG.infof(
          "Type d'organisation déjà existant pour le code %s, retour de l'entrée existante.",
          dto.getCode());
      return toDTO(existingOpt.get());
    }

    TypeOrganisationEntity entity = new TypeOrganisationEntity();
    // métadonnées de création
    entity.setCreePar(keycloakService.getCurrentUserEmail());
    applyToEntity(dto, entity);
    repository.persist(entity);
    return toDTO(entity);
  }

  /** Met à jour un type existant. L'ID est utilisé comme identifiant principal. */
  @Transactional
  public TypeOrganisationDTO update(UUID id, TypeOrganisationDTO dto) {
    TypeOrganisationEntity entity =
        repository
            .findByIdOptional(id)
            .orElseThrow(() -> new IllegalArgumentException("Type d'organisation introuvable"));

    if (dto.getCode() != null && !dto.getCode().equalsIgnoreCase(entity.getCode())) {
      validateCode(dto.getCode());
      repository
          .findByCode(dto.getCode())
          .ifPresent(
              existing -> {
                if (!existing.getId().equals(id)) {
                  throw new IllegalArgumentException(
                      "Un autre type d'organisation utilise déjà le code: " + dto.getCode());
                }
              });
      entity.setCode(dto.getCode());
    }

    // métadonnées de modification
    entity.setModifiePar(keycloakService.getCurrentUserEmail());
    applyToEntity(dto, entity);
    repository.update(entity);
    return toDTO(entity);
  }

  /** Désactive logiquement un type. */
  @Transactional
  public void disable(UUID id) {
    TypeOrganisationEntity entity =
      repository
          .findByIdOptional(id)
          .orElseThrow(() -> new IllegalArgumentException("Type d'organisation introuvable"));
    entity.setActif(false);
    repository.update(entity);
  }

  private void validateCode(String code) {
    if (code == null || code.trim().isEmpty()) {
      throw new IllegalArgumentException("Le code du type d'organisation est obligatoire");
    }
    // Plus aucune contrainte de format technique côté backend pour éviter les 400 inutiles.
    // Le code est simplement normalisé en majuscules dans applyToEntity, ce qui suffit
    // pour garantir la cohérence métier et la clé fonctionnelle.
  }

  private TypeOrganisationDTO toDTO(TypeOrganisationEntity entity) {
    TypeOrganisationDTO dto = new TypeOrganisationDTO();
    dto.setId(entity.getId());
    dto.setDateCreation(entity.getDateCreation());
    dto.setDateModification(entity.getDateModification());
    dto.setActif(entity.getActif());
    dto.setVersion(entity.getVersion());

    dto.setCode(entity.getCode());
    dto.setLibelle(entity.getLibelle());
    dto.setDescription(entity.getDescription());
    dto.setOrdreAffichage(entity.getOrdreAffichage());
    return dto;
  }

  private void applyToEntity(TypeOrganisationDTO dto, TypeOrganisationEntity entity) {
    if (dto.getCode() != null) {
      entity.setCode(dto.getCode().toUpperCase());
    }
    if (dto.getLibelle() != null) {
      entity.setLibelle(dto.getLibelle());
    }
    entity.setDescription(dto.getDescription());
    entity.setOrdreAffichage(dto.getOrdreAffichage());
    if (dto.getActif() != null) {
      entity.setActif(dto.getActif());
    }
  }
}


