package com.storycove.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import com.fasterxml.jackson.annotation.JsonManagedReference;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

@Entity
@Table(name = "collections")
public class Collection {
    
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
    
    @NotBlank(message = "Collection name is required")
    @Size(max = 500, message = "Collection name must not exceed 500 characters")
    @Column(nullable = false, length = 500)
    private String name;
    
    @Column(columnDefinition = "TEXT")
    private String description;
    
    @Column(name = "rating")
    private Integer rating;
    
    @Column(name = "cover_image_path", length = 500)
    private String coverImagePath;
    
    @Column(name = "is_archived", nullable = false)
    private Boolean isArchived = false;
    
    @OneToMany(mappedBy = "collection", cascade = CascadeType.ALL, orphanRemoval = true)
    @OrderBy("position ASC")
    @JsonManagedReference("collection-stories")
    private List<CollectionStory> collectionStories = new ArrayList<>();
    
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
        name = "collection_tags",
        joinColumns = @JoinColumn(name = "collection_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private Set<Tag> tags = new HashSet<>();
    
    // Transient field for search results - tag names only to avoid lazy loading issues
    @Transient
    private List<String> tagNames;
    
    @CreationTimestamp
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @UpdateTimestamp
    @Column(name = "updated_at", nullable = false)
    private LocalDateTime updatedAt;
    
    public Collection() {}
    
    public Collection(String name) {
        this.name = name;
    }
    
    public Collection(String name, String description) {
        this.name = name;
        this.description = description;
    }
    
    // Helper methods for managing collection stories
    public void addStory(Story story, int position) {
        CollectionStory collectionStory = new CollectionStory();
        collectionStory.setCollection(this);
        collectionStory.setStory(story);
        collectionStory.setPosition(position);
        collectionStories.add(collectionStory);
    }
    
    public void removeStory(UUID storyId) {
        collectionStories.removeIf(cs -> cs.getStory().getId().equals(storyId));
    }
    
    public void reorderStories(List<UUID> storyIds) {
        for (int i = 0; i < storyIds.size(); i++) {
            UUID storyId = storyIds.get(i);
            final int position = (i + 1) * 1000; // Gap-based positioning
            collectionStories.stream()
                .filter(cs -> cs.getStory().getId().equals(storyId))
                .findFirst()
                .ifPresent(cs -> cs.setPosition(position));
        }
    }
    
    public void addTag(Tag tag) {
        tags.add(tag);
    }
    
    public void removeTag(Tag tag) {
        tags.remove(tag);
    }
    
    // Calculated properties
    public int getStoryCount() {
        return collectionStories.size();
    }
    
    public int getTotalWordCount() {
        return collectionStories.stream()
            .mapToInt(cs -> cs.getStory().getWordCount() != null ? cs.getStory().getWordCount() : 0)
            .sum();
    }
    
    public int getEstimatedReadingTime() {
        // Assuming 200 words per minute reading speed
        return Math.max(1, getTotalWordCount() / 200);
    }
    
    public Double getAverageStoryRating() {
        return collectionStories.stream()
            .filter(cs -> cs.getStory().getRating() != null)
            .mapToInt(cs -> cs.getStory().getRating())
            .average()
            .orElse(0.0);
    }
    
    // Getters and Setters
    public UUID getId() {
        return id;
    }
    
    public void setId(UUID id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    public Integer getRating() {
        return rating;
    }
    
    public void setRating(Integer rating) {
        this.rating = rating;
    }
    
    public String getCoverImagePath() {
        return coverImagePath;
    }
    
    public void setCoverImagePath(String coverImagePath) {
        this.coverImagePath = coverImagePath;
    }
    
    public Boolean getIsArchived() {
        return isArchived;
    }
    
    public void setIsArchived(Boolean isArchived) {
        this.isArchived = isArchived;
    }
    
    public List<CollectionStory> getCollectionStories() {
        return collectionStories;
    }
    
    public void setCollectionStories(List<CollectionStory> collectionStories) {
        this.collectionStories = collectionStories;
    }
    
    public Set<Tag> getTags() {
        return tags;
    }
    
    public void setTags(Set<Tag> tags) {
        this.tags = tags;
    }
    
    public List<String> getTagNames() {
        return tagNames;
    }
    
    public void setTagNames(List<String> tagNames) {
        this.tagNames = tagNames;
    }
    
    public LocalDateTime getCreatedAt() {
        return createdAt;
    }
    
    public void setCreatedAt(LocalDateTime createdAt) {
        this.createdAt = createdAt;
    }
    
    public LocalDateTime getUpdatedAt() {
        return updatedAt;
    }
    
    public void setUpdatedAt(LocalDateTime updatedAt) {
        this.updatedAt = updatedAt;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Collection)) return false;
        Collection collection = (Collection) o;
        return id != null && id.equals(collection.id);
    }
    
    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
    
    @Override
    public String toString() {
        return "Collection{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", storyCount=" + getStoryCount() +
                ", isArchived=" + isArchived +
                '}';
    }
}