package com.social.media.domain.content.aggregate;

import com.social.media.domain.content.valueobject.*;
import com.social.media.domain.company.valueobject.CompanyId;
import com.social.media.domain.user.valueobject.UserId;
import com.social.media.domain.socialaccount.valueobject.SocialAccountId;
import com.social.media.domain.shared.exception.BusinessRuleViolationException;
import com.social.media.domain.shared.kernel.AggregateRoot;

import java.time.LocalDateTime;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

/**
 * Post aggregate representing social media posts
 */
public class Post extends AggregateRoot<PostId> {
    
    private CompanyId companyId;
    private UserId authorId;
    private ContentCategoryId categoryId;
    private PostContent content;
    private PostType postType;
    private PostStatus status;
    private List<MediaId> mediaIds;
    private Set<SocialAccountId> targetSocialAccounts;
    private LocalDateTime scheduledFor;
    private LocalDateTime publishedAt;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    private String tags;
    private boolean isPinned;
    private int viewCount;
    private int likeCount;
    private int shareCount;
    private int commentCount;
    
    // Constructor for new post
    public Post(CompanyId companyId, UserId authorId, PostContent content, 
                PostType postType, ContentCategoryId categoryId) {
        super(PostId.generate());
        this.companyId = validateCompanyId(companyId);
        this.authorId = validateAuthorId(authorId);
        this.content = validateContent(content);
        this.postType = validatePostType(postType);
        this.categoryId = categoryId;
        this.status = PostStatus.DRAFT;
        this.mediaIds = new ArrayList<>();
        this.targetSocialAccounts = new HashSet<>();
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
        this.isPinned = false;
        this.viewCount = 0;
        this.likeCount = 0;
        this.shareCount = 0;
        this.commentCount = 0;
    }
    
    // Constructor for existing post
    public Post(PostId id, CompanyId companyId, UserId authorId, ContentCategoryId categoryId,
                PostContent content, PostType postType, PostStatus status,
                List<MediaId> mediaIds, Set<SocialAccountId> targetSocialAccounts,
                LocalDateTime scheduledFor, LocalDateTime publishedAt,
                LocalDateTime createdAt, LocalDateTime updatedAt, String tags,
                boolean isPinned, int viewCount, int likeCount, int shareCount, int commentCount) {
        super(id);
        this.companyId = companyId;
        this.authorId = authorId;
        this.categoryId = categoryId;
        this.content = content;
        this.postType = postType;
        this.status = status;
        this.mediaIds = mediaIds != null ? new ArrayList<>(mediaIds) : new ArrayList<>();
        this.targetSocialAccounts = targetSocialAccounts != null ? new HashSet<>(targetSocialAccounts) : new HashSet<>();
        this.scheduledFor = scheduledFor;
        this.publishedAt = publishedAt;
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
        this.tags = tags;
        this.isPinned = isPinned;
        this.viewCount = viewCount;
        this.likeCount = likeCount;
        this.shareCount = shareCount;
        this.commentCount = commentCount;
    }
    
    // Business methods
    public void updateContent(PostContent content) {
        if (this.status.isPublished()) {
            throw new BusinessRuleViolationException("Cannot update content of published post");
        }
        this.content = validateContent(content);
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateCategory(ContentCategoryId categoryId) {
        this.categoryId = categoryId;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void addMedia(MediaId mediaId) {
        if (mediaId == null) {
            throw new BusinessRuleViolationException("Media ID cannot be null");
        }
        if (this.mediaIds.contains(mediaId)) {
            throw new BusinessRuleViolationException("Media already attached to post");
        }
        this.mediaIds.add(mediaId);
        this.updatedAt = LocalDateTime.now();
    }
    
    public void removeMedia(MediaId mediaId) {
        if (!this.mediaIds.contains(mediaId)) {
            throw new BusinessRuleViolationException("Media not attached to post");
        }
        this.mediaIds.remove(mediaId);
        this.updatedAt = LocalDateTime.now();
    }
    
    public void addTargetSocialAccount(SocialAccountId socialAccountId) {
        if (socialAccountId == null) {
            throw new BusinessRuleViolationException("Social account ID cannot be null");
        }
        this.targetSocialAccounts.add(socialAccountId);
        this.updatedAt = LocalDateTime.now();
    }
    
    public void removeTargetSocialAccount(SocialAccountId socialAccountId) {
        this.targetSocialAccounts.remove(socialAccountId);
        this.updatedAt = LocalDateTime.now();
    }
    
    public void clearTargetSocialAccounts() {
        this.targetSocialAccounts.clear();
        this.updatedAt = LocalDateTime.now();
    }
    
    public void scheduleFor(LocalDateTime scheduledFor) {
        if (scheduledFor != null && scheduledFor.isBefore(LocalDateTime.now())) {
            throw new BusinessRuleViolationException("Cannot schedule post for past date");
        }
        if (this.status.isPublished()) {
            throw new BusinessRuleViolationException("Cannot reschedule published post");
        }
        this.scheduledFor = scheduledFor;
        this.status = scheduledFor != null ? PostStatus.SCHEDULED : PostStatus.DRAFT;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void publish() {
        if (this.status.isPublished()) {
            throw new BusinessRuleViolationException("Post is already published");
        }
        if (this.targetSocialAccounts.isEmpty()) {
            throw new BusinessRuleViolationException("Cannot publish post without target social accounts");
        }
        this.status = PostStatus.PUBLISHED;
        this.publishedAt = LocalDateTime.now();
        this.scheduledFor = null;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void markAsFailed(String errorMessage) {
        this.status = PostStatus.FAILED;
        this.updatedAt = LocalDateTime.now();
        // Could add error message to metadata if needed
    }
    
    public void archive() {
        this.status = PostStatus.ARCHIVED;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void unarchive() {
        if (!this.status.isArchived()) {
            throw new BusinessRuleViolationException("Post is not archived");
        }
        this.status = this.publishedAt != null ? PostStatus.PUBLISHED : PostStatus.DRAFT;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void pin() {
        if (!this.status.isPublished()) {
            throw new BusinessRuleViolationException("Only published posts can be pinned");
        }
        this.isPinned = true;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void unpin() {
        this.isPinned = false;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateTags(String tags) {
        this.tags = tags;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void incrementViewCount() {
        this.viewCount++;
        this.updatedAt = LocalDateTime.now();
    }
    
    public void updateEngagementMetrics(int likeCount, int shareCount, int commentCount) {
        this.likeCount = Math.max(0, likeCount);
        this.shareCount = Math.max(0, shareCount);
        this.commentCount = Math.max(0, commentCount);
        this.updatedAt = LocalDateTime.now();
    }
    
    // Query methods
    public boolean isScheduled() {
        return this.status.isScheduled() && this.scheduledFor != null;
    }
    
    public boolean isPublished() {
        return this.status.isPublished();
    }
    
    public boolean isDraft() {
        return this.status.isDraft();
    }
    
    public boolean isArchived() {
        return this.status.isArchived();
    }
    
    public boolean isFailed() {
        return this.status.isFailed();
    }
    
    public boolean hasMedia() {
        return !this.mediaIds.isEmpty();
    }
    
    public boolean hasTargetAccounts() {
        return !this.targetSocialAccounts.isEmpty();
    }
    
    public boolean belongsToCompany(CompanyId companyId) {
        return this.companyId.equals(companyId);
    }
    
    public boolean wasCreatedBy(UserId userId) {
        return this.authorId.equals(userId);
    }
    
    public boolean isScheduledFor(LocalDateTime dateTime) {
        return this.scheduledFor != null && this.scheduledFor.equals(dateTime);
    }
    
    public boolean isReadyForPublishing() {
        return this.status.canBePublished() && hasTargetAccounts();
    }
    
    public List<String> getHashtags() {
        return this.content.getHashtags();
    }
    
    public List<String> getMentions() {
        return this.content.getMentions();
    }
    
    // Validation methods
    private CompanyId validateCompanyId(CompanyId companyId) {
        if (companyId == null) {
            throw new BusinessRuleViolationException("Company ID is required");
        }
        return companyId;
    }
    
    private UserId validateAuthorId(UserId authorId) {
        if (authorId == null) {
            throw new BusinessRuleViolationException("Author ID is required");
        }
        return authorId;
    }
    
    private PostContent validateContent(PostContent content) {
        if (content == null) {
            throw new BusinessRuleViolationException("Post content is required");
        }
        return content;
    }
    
    private PostType validatePostType(PostType postType) {
        if (postType == null) {
            throw new BusinessRuleViolationException("Post type is required");
        }
        return postType;
    }
    
    // Getters
    public CompanyId getCompanyId() { return companyId; }
    public UserId getAuthorId() { return authorId; }
    public ContentCategoryId getCategoryId() { return categoryId; }
    public PostContent getContent() { return content; }
    public PostType getPostType() { return postType; }
    public PostStatus getStatus() { return status; }
    public List<MediaId> getMediaIds() { return new ArrayList<>(mediaIds); }
    public Set<SocialAccountId> getTargetSocialAccounts() { return new HashSet<>(targetSocialAccounts); }
    public LocalDateTime getScheduledFor() { return scheduledFor; }
    public LocalDateTime getPublishedAt() { return publishedAt; }
    public LocalDateTime getCreatedAt() { return createdAt; }
    public LocalDateTime getUpdatedAt() { return updatedAt; }
    public String getTags() { return tags; }
    public boolean isPinned() { return isPinned; }
    public int getViewCount() { return viewCount; }
    public int getLikeCount() { return likeCount; }
    public int getShareCount() { return shareCount; }
    public int getCommentCount() { return commentCount; }
}
