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 org.jsoup.Jsoup;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonBackReference;

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

@Entity
@Table(name = "stories")
public class Story {
    
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
    
    @NotBlank(message = "Story title is required")
    @Size(max = 255, message = "Story title must not exceed 255 characters")
    @Column(nullable = false)
    private String title;
    
    @Column(name = "summary", columnDefinition = "TEXT")
    private String summary;
    
    @Size(max = 1000, message = "Story description must not exceed 1000 characters")
    @Column(length = 1000)
    private String description;
    
    @Column(name = "content_html", columnDefinition = "TEXT")
    private String contentHtml;
    
    @Column(name = "content_plain", columnDefinition = "TEXT")
    private String contentPlain;
    
    @Column(name = "source_url")
    private String sourceUrl;
    
    @Column(name = "cover_path")
    private String coverPath;
    
    @Column(name = "word_count")
    private Integer wordCount = 0;
    
    @Column(name = "rating")
    private Integer rating;
    
    @Column(name = "volume", columnDefinition = "float8")
    private Double volume;
    
    @Column(name = "is_read")
    private Boolean isRead = false;
    
    @Column(name = "reading_position")
    private Integer readingPosition = 0;
    
    @Column(name = "last_read_at")
    private LocalDateTime lastReadAt;

    @Column(name = "last_completed_at")
    private LocalDateTime lastCompletedAt;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id")
    @JsonBackReference("author-stories")
    private Author author;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "series_id")
    @JsonBackReference("series-stories")
    private Series series;
    
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
        name = "story_tags",
        joinColumns = @JoinColumn(name = "story_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    @JsonManagedReference("story-tags")
    private Set<Tag> tags = new HashSet<>();
    
    @CreationTimestamp
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;
    
    @UpdateTimestamp
    @Column(name = "updated_at", nullable = false)
    private LocalDateTime updatedAt;
    
    public Story() {}
    
    public Story(String title) {
        this.title = title;
    }
    
    public Story(String title, String contentHtml) {
        this.title = title;
        this.contentHtml = contentHtml;
        updateWordCount();
    }
    
    public void addTag(Tag tag) {
        tags.add(tag);
        tag.getStories().add(this);
    }
    
    public void removeTag(Tag tag) {
        tags.remove(tag);
        tag.getStories().remove(this);
    }
    
    
    public void updateWordCount() {
        if (contentPlain != null) {
            String[] words = contentPlain.trim().split("\\s+");
            this.wordCount = words.length;
        } else if (contentHtml != null) {
            String cleanText = contentHtml.replaceAll("<[^>]*>", "");
            String[] words = cleanText.trim().split("\\s+");
            this.wordCount = words.length;
        }
    }
    
    
    public boolean isPartOfSeries() {
        return series != null && volume != null;
    }
    
    // Getters and Setters
    public UUID getId() {
        return id;
    }
    
    public void setId(UUID id) {
        this.id = id;
    }
    
    public String getTitle() {
        return title;
    }
    
    public void setTitle(String title) {
        this.title = title;
    }
    
    public String getSummary() {
        return summary;
    }
    
    public void setSummary(String summary) {
        this.summary = summary;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    public String getContentHtml() {
        return contentHtml;
    }
    
    public void setContentHtml(String contentHtml) {
        this.contentHtml = contentHtml;
        this.setContentPlain(Jsoup.parse(contentHtml).text());
        updateWordCount();
    }
    
    public String getContentPlain() {
        return contentPlain;
    }
    
    protected void setContentPlain(String contentPlain) {
        this.contentPlain = contentPlain;
    }
    
    public String getSourceUrl() {
        return sourceUrl;
    }
    
    public void setSourceUrl(String sourceUrl) {
        this.sourceUrl = sourceUrl;
    }
    
    public String getCoverPath() {
        return coverPath;
    }
    
    public void setCoverPath(String coverPath) {
        this.coverPath = coverPath;
    }
    
    public Integer getWordCount() {
        return wordCount;
    }
    
    public void setWordCount(Integer wordCount) {
        this.wordCount = wordCount;
    }
    
    public Integer getRating() {
        return rating;
    }
    
    public void setRating(Integer rating) {
        this.rating = rating;
    }
    
    public Double getVolume() {
        return volume;
    }

    public void setVolume(Double volume) {
        this.volume = volume;
    }
    
    public Boolean getIsRead() {
        return isRead;
    }
    
    public void setIsRead(Boolean isRead) {
        this.isRead = isRead;
    }
    
    public Integer getReadingPosition() {
        return readingPosition;
    }
    
    public void setReadingPosition(Integer readingPosition) {
        this.readingPosition = readingPosition;
    }
    
    public LocalDateTime getLastReadAt() {
        return lastReadAt;
    }
    
    public void setLastReadAt(LocalDateTime lastReadAt) {
        this.lastReadAt = lastReadAt;
    }

    public LocalDateTime getLastCompletedAt() {
        return lastCompletedAt;
    }

    public void setLastCompletedAt(LocalDateTime lastCompletedAt) {
        this.lastCompletedAt = lastCompletedAt;
    }
    
    public Author getAuthor() {
        return author;
    }
    
    public void setAuthor(Author author) {
        this.author = author;
    }
    
    public Series getSeries() {
        return series;
    }
    
    public void setSeries(Series series) {
        this.series = series;
    }
    
    public Set<Tag> getTags() {
        return tags;
    }
    
    public void setTags(Set<Tag> tags) {
        this.tags = tags;
    }
    
    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;
    }
    
    /**
     * Updates the reading progress and timestamp
     * When position is 0 or null, resets lastReadAt to null so the story won't appear in "last read" sorting
     */
    public void updateReadingProgress(Integer position) {
        // Capture completion timestamp when resetting progress that was already in progress
        if ((position == null || position == 0) && this.readingPosition != null && this.readingPosition > 0) {
            this.lastCompletedAt = LocalDateTime.now();
        }
        this.readingPosition = position;
        // Only update lastReadAt if there's actual reading progress
        // Reset to null when position is 0 or null to remove from "last read" sorting
        if (position == null || position == 0) {
            this.lastReadAt = null;
        } else {
            this.lastReadAt = LocalDateTime.now();
        }
    }
    
    /**
     * Marks the story as read and updates the reading position to the end
     */
    public void markAsRead() {
        this.isRead = true;
        this.lastReadAt = LocalDateTime.now();
        this.lastCompletedAt = LocalDateTime.now();
        // Set reading position to the end of content if available
        // ALWAYS use contentHtml for consistency (frontend uses contentHtml for position tracking)
        if (contentHtml != null) {
            this.readingPosition = contentHtml.length();
        } else if (contentPlain != null) {
            this.readingPosition = contentPlain.length();
        }
    }
    
    /**
     * Marks the story as unread and resets reading position
     */
    public void markAsUnread() {
        this.isRead = false;
        this.readingPosition = 0;
        this.lastReadAt = null;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Story)) return false;
        Story story = (Story) o;
        return id != null && id.equals(story.id);
    }
    
    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
    
    @Override
    public String toString() {
        return "Story{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", wordCount=" + wordCount +
                ", rating=" + rating +
                ", isRead=" + isRead +
                ", readingPosition=" + readingPosition +
                '}';
    }
}