package dev.lions.unionflow.server.api.dto.solidarite;

import jakarta.validation.constraints.*;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * DTO pour les créneaux de disponibilité du proposant
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreneauDisponibiliteDTO {

  /** Identifiant unique du créneau */
  private String id;

  /** Jour de la semaine (pour créneaux récurrents) */
  private DayOfWeek jourSemaine;

  /** Date spécifique (pour créneaux ponctuels) */
  private LocalDate dateSpecifique;

  /** Heure de début */
  @NotNull(message = "L'heure de début est obligatoire")
  private LocalTime heureDebut;

  /** Heure de fin */
  @NotNull(message = "L'heure de fin est obligatoire")
  private LocalTime heureFin;

  /** Type de créneau */
  @NotNull(message = "Le type de créneau est obligatoire")
  @Builder.Default
  private TypeCreneau type = TypeCreneau.RECURRENT;

  /** Indique si le créneau est actif */
  @Builder.Default private Boolean estActif = true;

  /** Fuseau horaire */
  @Builder.Default private String fuseauHoraire = "Africa/Abidjan";

  /** Commentaires sur le créneau */
  @Size(max = 200, message = "Les commentaires ne peuvent pas dépasser 200 caractères")
  private String commentaires;

  /** Priorité du créneau (1 = haute, 5 = basse) */
  @Min(value = 1, message = "La priorité doit être au moins 1")
  @Max(value = 5, message = "La priorité ne peut pas dépasser 5")
  @Builder.Default
  private Integer priorite = 3;

  /** Durée maximale d'intervention en minutes */
  @Min(value = 15, message = "La durée doit être au moins 15 minutes")
  @Max(value = 480, message = "La durée ne peut pas dépasser 8 heures")
  private Integer dureeMaxMinutes;

  /** Indique si des pauses sont nécessaires */
  @Builder.Default private Boolean pausesNecessaires = false;

  /** Durée des pauses en minutes */
  @Min(value = 5, message = "La durée de pause doit être au moins 5 minutes")
  private Integer dureePauseMinutes;

  /** Énumération des types de créneaux */
  public enum TypeCreneau {
    RECURRENT("Récurrent"),
    PONCTUEL("Ponctuel"),
    URGENCE("Urgence"),
    FLEXIBLE("Flexible");

    private final String libelle;

    TypeCreneau(String libelle) {
      this.libelle = libelle;
    }

    public String getLibelle() {
      return libelle;
    }
  }

  // === MÉTHODES UTILITAIRES ===

  /** Vérifie si le créneau est valide (heure fin > heure début) */
  public boolean isValide() {
    return heureDebut != null && heureFin != null && heureFin.isAfter(heureDebut);
  }

  /** Calcule la durée du créneau en minutes */
  public long getDureeMinutes() {
    if (!isValide()) return 0;
    return java.time.Duration.between(heureDebut, heureFin).toMinutes();
  }

  /** Vérifie si le créneau est disponible à une date donnée */
  public boolean isDisponibleLe(LocalDate date) {
    if (!estActif) return false;

    return switch (type) {
      case PONCTUEL -> dateSpecifique != null && dateSpecifique.equals(date);
      case RECURRENT -> jourSemaine != null && date.getDayOfWeek() == jourSemaine;
      case URGENCE, FLEXIBLE -> true;
    };
  }

  /** Vérifie si une heure est dans le créneau */
  public boolean contientHeure(LocalTime heure) {
    if (!isValide()) return false;
    return !heure.isBefore(heureDebut) && !heure.isAfter(heureFin);
  }

  /** Retourne le libellé du créneau */
  public String getLibelle() {
    StringBuilder sb = new StringBuilder();

    boolean recurrentAvecJour = type == TypeCreneau.RECURRENT && jourSemaine != null;
    boolean ponctuelAvecDate = type == TypeCreneau.PONCTUEL && dateSpecifique != null;
    if (recurrentAvecJour) {
      sb.append(jourSemaine.name()).append(" ");
    } else if (ponctuelAvecDate) {
      sb.append(dateSpecifique.toString()).append(" ");
    }

    sb.append(heureDebut.toString()).append(" - ").append(heureFin.toString());

    return sb.toString();
  }
}
