using System.Text.Json;
using Femto.Common.Domain;
using Femto.Modules.Blog.Domain.Posts.Events;
using Femto.Modules.Blog.Emoji;

namespace Femto.Modules.Blog.Domain.Posts;

internal class Post : Entity
{
    public Guid Id { get; private set; }
    public Guid AuthorId { get; private set; }
    public string Content { get; private set; } = null!;
    public IList<PostMedia> Media { get; private set; }

    public IList<PostReaction> Reactions { get; private set; } = [];
    
    public IList<PostComment> Comments { get; private set; } = [];
    public bool IsPublic { get; private set; }

    public DateTimeOffset PostedOn { get; private set; }

    private string PossibleReactionsJson { get; set; } = null!;

    public IEnumerable<string> PossibleReactions
    {
        get => JsonSerializer.Deserialize<IEnumerable<string>>(this.PossibleReactionsJson)!;
        init => PossibleReactionsJson = JsonSerializer.Serialize(value);
    }

    private Post() { }

    public Post(Guid authorId, string content, IList<PostMedia> media, bool isPublic)
    {
        this.Id = Guid.CreateVersion7();
        this.AuthorId = authorId;
        this.Content = content;
        this.Media = media;
        this.PossibleReactions = AllEmoji.GetRandomEmoji(5);
        this.PostedOn = DateTimeOffset.UtcNow;
        this.IsPublic = isPublic;

        this.AddDomainEvent(new PostCreated(this));
    }

    public void AddReaction(Guid reactorId, string emoji)
    {
        if (!this.PossibleReactions.Contains(emoji))
            return;

        if (this.Reactions.Any(r => r.AuthorId == reactorId && r.Emoji == emoji))
            return;

        this.Reactions.Add(new PostReaction(reactorId, this.Id, emoji));
    }

    public void RemoveReaction(Guid reactorId, string emoji)
    {
        this.Reactions = this
            .Reactions.Where(r => r.AuthorId != reactorId || r.Emoji != emoji)
            .ToList();
    }
    
    public void AddComment(Guid authorId, string content)
    {
        // XXX just ignore empty comments for now. we may want to upgrade this to an error
        //     but it is probably suitable to just consider it a no-op
        if (string.IsNullOrWhiteSpace(content))
            return;
        
        this.Comments.Add(new PostComment(authorId, content));
    }
}
