package dev.lions.models;

import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

/**
 * Entité représentant un projet dans le système.
 * Gère les informations complètes d'un projet, incluant ses métadonnées,
 * technologies, témoignages et statut.
 */
@Entity
@Table(
    name = "projects",
    indexes = {
        @Index(name = "idx_project_completion_date", columnList = "completionDate"),
        @Index(name = "idx_project_featured", columnList = "featured")
    }
)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Project implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @NotNull(message = "L'identifiant du projet est obligatoire")
  @Pattern(regexp = "^[a-zA-Z0-9-_]+$", message = "L'identifiant ne doit contenir que des lettres, chiffres, tirets et underscores")
  private String id;

  @Column(nullable = false, length = 100)
  @NotNull(message = "Le titre du projet est obligatoire")
  @Size(min = 3, max = 100, message = "Le titre doit contenir entre 3 et 100 caractères")
  private String title;

  @Column(nullable = false, length = 500)
  @NotNull(message = "La description du projet est obligatoire")
  @Size(min = 10, max = 500, message = "La description doit contenir entre 10 et 500 caractères")
  private String description;

  @Column(nullable = false, length = 250)
  @NotNull(message = "La description courte est obligatoire")
  @Size(min = 10, max = 250, message = "La description courte doit contenir entre 10 et 250 caractères")
  private String shortDescription;

  @Column(nullable = false)
  @NotNull(message = "L'URL de l'image est obligatoire")
  @Pattern(regexp = "^[^<>\"']*$", message = "L'URL de l'image contient des caractères non autorisés")
  private String imageUrl;

  @Column(length = 100)
  @Pattern(regexp = "^[^<>\"']*$", message = "Le nom du client contient des caractères non autorisés")
  private String clientName;

  @Column(nullable = false)
  @PastOrPresent(message = "La date de réalisation ne peut pas être dans le futur")
  private LocalDateTime completionDate;

  @ElementCollection
  @CollectionTable(
      name = "project_tags",
      joinColumns = @JoinColumn(name = "project_id")
  )
  @Column(name = "tag", length = 50)
  @Builder.Default
  private List<@Pattern(regexp = "^[a-zA-Z0-9-_]+$") String> tags = new ArrayList<>();

  @ElementCollection
  @CollectionTable(
      name = "project_technologies",
      joinColumns = @JoinColumn(name = "project_id")
  )
  @Column(name = "technology", length = 50)
  @Builder.Default
  private List<@Pattern(regexp = "^[a-zA-Z0-9-_. ]+$") String> technologies = new ArrayList<>();

  @Column(length = 1000)
  @Size(max = 1000, message = "La description du challenge ne doit pas dépasser 1000 caractères")
  private String challenge;

  @Column(length = 1000)
  @Size(max = 1000, message = "La description de la solution ne doit pas dépasser 1000 caractères")
  private String solution;

  @Column(length = 1000)
  @Size(max = 1000, message = "La description des résultats ne doit pas dépasser 1000 caractères")
  private String results;

  @ElementCollection
  @CollectionTable(
      name = "project_testimonials",
      joinColumns = @JoinColumn(name = "project_id")
  )
  @Column(name = "testimonial", length = 1000)
  @Builder.Default
  private List<@Size(max = 1000) String> testimonials = new ArrayList<>();

  @Builder.Default
  private boolean featured = false;

  @Version
  private Long version;

  @Column(name = "created_at", nullable = false, updatable = false)
  @CreationTimestamp
  private LocalDateTime createdAt;

  @Column(name = "updated_at")
  @UpdateTimestamp
  private LocalDateTime updatedAt;

  /**
   * Récupère les tags de manière sécurisée.
   *
   * @return Liste immuable des tags
   */
  public List<String> getTags() {
    return Collections.unmodifiableList(tags);
  }

  /**
   * Récupère les technologies de manière sécurisée.
   *
   * @return Liste immuable des technologies
   */
  public List<String> getTechnologies() {
    return Collections.unmodifiableList(technologies);
  }

  /**
   * Récupère les témoignages de manière sécurisée.
   *
   * @return Liste immuable des témoignages
   */
  public List<String> getTestimonials() {
    return Collections.unmodifiableList(testimonials);
  }

  /**
   * Ajoute un tag au projet.
   *
   * @param tag Tag à ajouter
   * @return true si le tag a été ajouté
   */
  public boolean addTag(String tag) {
    if (tag != null && !tag.isEmpty() && !tags.contains(tag)) {
      return tags.add(tag.trim().toLowerCase());
    }
    return false;
  }

  /**
   * Ajoute une technologie au projet.
   *
   * @param technology Technologie à ajouter
   * @return true si la technologie a été ajoutée
   */
  public boolean addTechnology(String technology) {
    if (technology != null && !technology.isEmpty() && !technologies.contains(technology)) {
      return technologies.add(technology.trim());
    }
    return false;
  }

  /**
   * Ajoute un témoignage au projet.
   *
   * @param testimonial Témoignage à ajouter
   * @return true si le témoignage a été ajouté
   */
  public boolean addTestimonial(String testimonial) {
    if (testimonial != null && !testimonial.isEmpty()) {
      return testimonials.add(testimonial.trim());
    }
    return false;
  }

  /**
   * Récupère le premier témoignage s'il existe.
   *
   * @return Optional contenant le premier témoignage
   */
  public Optional<String> getFirstTestimonial() {
    return testimonials.isEmpty() ? Optional.empty() :
           Optional.of(testimonials.get(0));
  }

  /**
   * Vérifie si le projet est complet et prêt à être publié.
   *
   * @return true si le projet est complet
   */
  public boolean isComplete() {
    return id != null && !id.isEmpty() &&
           title != null && !title.isEmpty() &&
           description != null && !description.isEmpty() &&
           imageUrl != null && !imageUrl.isEmpty() &&
           completionDate != null;
  }
}