using MktXService.Domain.Events;
using MktXService.Domain.Exceptions;
using MktXService.Domain.SeedWork;

namespace MktXService.Domain.AggregatesModel.ConversationAggregate;

/// <summary>
/// EN: Conversation aggregate root managing message threads between contacts and merchants.
/// VI: Aggregate root hội thoại quản lý các luồng tin nhắn giữa liên hệ và merchant.
/// </summary>
public class Conversation : Entity, IAggregateRoot
{
    // EN: Private fields for encapsulation
    // VI: Fields private để đóng gói
    private Guid _contactId;
    private Guid _accountId;
    private ConversationStatus _status = null!;
    private string _channel = null!;
    private Guid? _assignedToUserId;
    private List<Message> _messages = new();
    private DateTime _startedAt;
    private DateTime? _closedAt;
    private DateTime _createdAt;
    private DateTime? _updatedAt;

    /// <summary>
    /// EN: Contact ID this conversation is with.
    /// VI: ID liên hệ của hội thoại này.
    /// </summary>
    public Guid ContactId => _contactId;

    /// <summary>
    /// EN: Twitter account ID handling this conversation.
    /// VI: ID tài khoản Twitter xử lý hội thoại này.
    /// </summary>
    public Guid AccountId => _accountId;

    /// <summary>
    /// EN: Current status.
    /// VI: Trạng thái hiện tại.
    /// </summary>
    public ConversationStatus Status => _status;

    /// <summary>
    /// EN: Status ID for EF Core mapping.
    /// VI: ID trạng thái cho EF Core mapping.
    /// </summary>
    public int StatusId { get; private set; }

    /// <summary>
    /// EN: Channel type (direct_message).
    /// VI: Loại kênh (direct_message).
    /// </summary>
    public string Channel => _channel;

    /// <summary>
    /// EN: User ID this conversation is assigned to.
    /// VI: ID người dùng được gán hội thoại này.
    /// </summary>
    public Guid? AssignedToUserId => _assignedToUserId;

    /// <summary>
    /// EN: Messages in this conversation.
    /// VI: Các tin nhắn trong hội thoại này.
    /// </summary>
    public IReadOnlyCollection<Message> Messages => _messages.AsReadOnly();

    /// <summary>
    /// EN: When the conversation started.
    /// VI: Thời điểm hội thoại bắt đầu.
    /// </summary>
    public DateTime StartedAt => _startedAt;

    /// <summary>
    /// EN: When the conversation was closed.
    /// VI: Thời điểm hội thoại được đóng.
    /// </summary>
    public DateTime? ClosedAt => _closedAt;

    /// <summary>
    /// EN: Creation timestamp.
    /// VI: Thời gian tạo.
    /// </summary>
    public DateTime CreatedAt => _createdAt;

    /// <summary>
    /// EN: Last update timestamp.
    /// VI: Thời gian cập nhật cuối.
    /// </summary>
    public DateTime? UpdatedAt => _updatedAt;

    /// <summary>
    /// EN: Private constructor for EF Core.
    /// VI: Constructor private cho EF Core.
    /// </summary>
    protected Conversation()
    {
    }

    /// <summary>
    /// EN: Start a new conversation with a contact.
    /// VI: Bắt đầu một hội thoại mới với liên hệ.
    /// </summary>
    public Conversation(Guid contactId, Guid accountId, string channel = "direct_message") : this()
    {
        if (contactId == Guid.Empty)
            throw new ConversationDomainException("Contact ID cannot be empty");
        if (accountId == Guid.Empty)
            throw new ConversationDomainException("Account ID cannot be empty");
        if (string.IsNullOrWhiteSpace(channel))
            throw new ConversationDomainException("Channel cannot be empty");

        Id = Guid.NewGuid();
        _contactId = contactId;
        _accountId = accountId;
        _channel = channel;
        _status = ConversationStatus.Open;
        StatusId = ConversationStatus.Open.Id;
        _startedAt = DateTime.UtcNow;
        _createdAt = DateTime.UtcNow;

        // EN: Add domain event for creation
        // VI: Thêm domain event cho việc tạo
        AddDomainEvent(new ConversationStartedDomainEvent(this));
    }

    /// <summary>
    /// EN: Add a message to this conversation.
    /// VI: Thêm một tin nhắn vào hội thoại này.
    /// </summary>
    public Message AddMessage(
        string direction,
        string type,
        string content,
        bool isFromBot = false,
        string? twitterMessageId = null,
        List<MessageAttachment>? attachments = null)
    {
        if (_status == ConversationStatus.Closed)
            throw new ConversationDomainException("Cannot add message to a closed conversation");

        var message = new Message(
            Id,
            direction,
            type,
            content,
            isFromBot,
            twitterMessageId,
            attachments);

        _messages.Add(message);
        _updatedAt = DateTime.UtcNow;

        // EN: Add domain event based on direction
        // VI: Thêm domain event dựa trên hướng
        if (direction == "inbound")
        {
            AddDomainEvent(new MessageReceivedDomainEvent(message));
        }
        else
        {
            AddDomainEvent(new MessageSentDomainEvent(message));
        }

        return message;
    }

    /// <summary>
    /// EN: Close this conversation.
    /// VI: Đóng hội thoại này.
    /// </summary>
    public void Close(string? reason = null)
    {
        if (_status == ConversationStatus.Closed)
            throw new ConversationDomainException("Conversation is already closed");

        _status = ConversationStatus.Closed;
        StatusId = ConversationStatus.Closed.Id;
        _closedAt = DateTime.UtcNow;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Reopen a closed conversation.
    /// VI: Mở lại một hội thoại đã đóng.
    /// </summary>
    public void Reopen()
    {
        if (_status != ConversationStatus.Closed)
            throw new ConversationDomainException("Only closed conversations can be reopened");

        _status = ConversationStatus.Open;
        StatusId = ConversationStatus.Open.Id;
        _closedAt = null;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Assign this conversation to a user/agent.
    /// VI: Gán hội thoại này cho một người dùng/agent.
    /// </summary>
    public void AssignTo(Guid userId)
    {
        if (userId == Guid.Empty)
            throw new ConversationDomainException("User ID cannot be empty");

        _assignedToUserId = userId;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Unassign this conversation.
    /// VI: Bỏ gán hội thoại này.
    /// </summary>
    public void Unassign()
    {
        _assignedToUserId = null;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Mark conversation as pending.
    /// VI: Đánh dấu hội thoại đang chờ.
    /// </summary>
    public void MarkAsPending()
    {
        if (_status == ConversationStatus.Closed)
            throw new ConversationDomainException("Cannot mark closed conversation as pending");

        _status = ConversationStatus.Pending;
        StatusId = ConversationStatus.Pending.Id;
        _updatedAt = DateTime.UtcNow;
    }
}
