using WhatsAppService.Domain.Events;
using WhatsAppService.Domain.Exceptions;
using WhatsAppService.Domain.SeedWork;

namespace WhatsAppService.Domain.AggregatesModel.ConversationAggregate;

/// <summary>
/// EN: Conversation aggregate root for managing WhatsApp conversations with 24h window.
/// VI: Conversation aggregate root để quản lý hội thoại WhatsApp với cửa sổ 24h.
/// </summary>
public class Conversation : Entity, IAggregateRoot
{
    /// <summary>
    /// EN: WhatsApp 24-hour messaging window duration.
    /// VI: Thời gian cửa sổ nhắn tin 24h của WhatsApp.
    /// </summary>
    public static readonly TimeSpan MessagingWindow = TimeSpan.FromHours(24);

    // EN: Private fields for encapsulation
    // VI: Fields private để đóng gói
    private Guid _shopId;
    private string _customerWaId = null!;
    private Guid _whatsAppAccountId;
    private ConversationStatus _status = null!;
    private Guid? _assignedAgentId;
    private DateTime? _lastMessageAt;
    private List<string> _tags = new();
    private readonly List<Message> _messages = new();
    private DateTime _createdAt;
    private DateTime? _expiresAt;

    /// <summary>
    /// EN: Shop ID that owns this conversation.
    /// VI: Shop ID sở hữu conversation này.
    /// </summary>
    public Guid ShopId => _shopId;

    /// <summary>
    /// EN: Customer's WhatsApp ID.
    /// VI: WhatsApp ID của khách hàng.
    /// </summary>
    public string CustomerWaId => _customerWaId;

    /// <summary>
    /// EN: WhatsApp account ID used for this conversation.
    /// VI: ID WhatsApp account sử dụng cho conversation này.
    /// </summary>
    public Guid WhatsAppAccountId => _whatsAppAccountId;

    /// <summary>
    /// EN: Current conversation status.
    /// VI: Trạng thái conversation 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: Assigned agent ID (human agent).
    /// VI: ID agent được gán (nhân viên).
    /// </summary>
    public Guid? AssignedAgentId => _assignedAgentId;

    /// <summary>
    /// EN: Last message timestamp.
    /// VI: Thời gian tin nhắn cuối cùng.
    /// </summary>
    public DateTime? LastMessageAt => _lastMessageAt;

    /// <summary>
    /// EN: Conversation tags.
    /// VI: Tags của conversation.
    /// </summary>
    public IReadOnlyList<string> Tags => _tags.AsReadOnly();

    /// <summary>
    /// EN: Messages in this conversation (read-only).
    /// VI: Các tin nhắn trong conversation này (chỉ đọc).
    /// </summary>
    public IReadOnlyCollection<Message> Messages => _messages.AsReadOnly();

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

    /// <summary>
    /// EN: Expiry timestamp (24h after last customer message).
    /// VI: Thời gian hết hạn (24h sau tin nhắn cuối của khách hàng).
    /// </summary>
    public DateTime? ExpiresAt => _expiresAt;

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

    /// <summary>
    /// EN: Create a new conversation.
    /// VI: Tạo một conversation mới.
    /// </summary>
    /// <param name="shopId">EN: Shop ID / VI: Shop ID</param>
    /// <param name="customerWaId">EN: Customer's WhatsApp ID / VI: WhatsApp ID của khách hàng</param>
    /// <param name="whatsAppAccountId">EN: WhatsApp account ID / VI: ID WhatsApp account</param>
    public Conversation(Guid shopId, string customerWaId, Guid whatsAppAccountId) : this()
    {
        if (shopId == Guid.Empty)
            throw new ConversationDomainException("Shop ID cannot be empty");
        
        if (string.IsNullOrWhiteSpace(customerWaId))
            throw new ConversationDomainException("Customer WhatsApp ID cannot be empty");
        
        if (whatsAppAccountId == Guid.Empty)
            throw new ConversationDomainException("WhatsApp account ID cannot be empty");

        Id = Guid.NewGuid();
        _shopId = shopId;
        _customerWaId = customerWaId.Trim();
        _whatsAppAccountId = whatsAppAccountId;
        _status = ConversationStatus.Active;
        StatusId = ConversationStatus.Active.Id;
        _createdAt = DateTime.UtcNow;
        _expiresAt = DateTime.UtcNow.Add(MessagingWindow);

        AddDomainEvent(new ConversationStartedEvent(Id, _shopId, _customerWaId));
    }

    /// <summary>
    /// EN: Add a message to the conversation.
    /// VI: Thêm một tin nhắn vào conversation.
    /// </summary>
    /// <param name="content">EN: Message content / VI: Nội dung tin nhắn</param>
    /// <param name="direction">EN: Message direction / VI: Hướng tin nhắn</param>
    /// <param name="whatsAppMessageId">EN: Optional WhatsApp message ID / VI: WhatsApp message ID tùy chọn</param>
    /// <returns>EN: The created message / VI: Tin nhắn đã tạo</returns>
    public Message AddMessage(MessageContent content, string direction, string? whatsAppMessageId = null)
    {
        if (_status != ConversationStatus.Active)
            throw new ConversationDomainException("Cannot add message to a closed or expired conversation");

        var message = new Message(Id, content, direction, whatsAppMessageId);
        _messages.Add(message);
        _lastMessageAt = message.Timestamp;

        // EN: If inbound message, extend the 24h window
        // VI: Nếu là tin nhắn đến, gia hạn cửa sổ 24h
        if (direction == Message.Directions.Inbound)
        {
            _expiresAt = DateTime.UtcNow.Add(MessagingWindow);
            AddDomainEvent(new MessageReceivedEvent(Id, message.Id, _shopId, _customerWaId, content));
        }

        return message;
    }

    /// <summary>
    /// EN: Assign conversation to a human agent.
    /// VI: Gán conversation cho một nhân viên.
    /// </summary>
    /// <param name="agentId">EN: Agent ID / VI: ID nhân viên</param>
    public void AssignToAgent(Guid agentId)
    {
        if (_status != ConversationStatus.Active)
            throw new ConversationDomainException("Cannot assign a closed or expired conversation");

        _assignedAgentId = agentId;
    }

    /// <summary>
    /// EN: Unassign conversation from current agent.
    /// VI: Bỏ gán conversation khỏi nhân viên hiện tại.
    /// </summary>
    public void Unassign()
    {
        _assignedAgentId = null;
    }

    /// <summary>
    /// EN: Add a tag to the conversation.
    /// VI: Thêm một tag vào conversation.
    /// </summary>
    /// <param name="tag">EN: Tag to add / VI: Tag cần thêm</param>
    public void AddTag(string tag)
    {
        if (string.IsNullOrWhiteSpace(tag))
            return;

        var normalizedTag = tag.Trim().ToLowerInvariant();
        if (!_tags.Contains(normalizedTag))
        {
            _tags.Add(normalizedTag);
        }
    }

    /// <summary>
    /// EN: Remove a tag from the conversation.
    /// VI: Xóa một tag khỏi conversation.
    /// </summary>
    /// <param name="tag">EN: Tag to remove / VI: Tag cần xóa</param>
    public void RemoveTag(string tag)
    {
        if (string.IsNullOrWhiteSpace(tag))
            return;

        var normalizedTag = tag.Trim().ToLowerInvariant();
        _tags.Remove(normalizedTag);
    }

    /// <summary>
    /// EN: Close the conversation manually.
    /// VI: Đóng conversation thủ công.
    /// </summary>
    public void Close()
    {
        if (_status != ConversationStatus.Active)
            return; // EN: Already closed / VI: Đã đóng rồi

        _status = ConversationStatus.Closed;
        StatusId = ConversationStatus.Closed.Id;

        AddDomainEvent(new ConversationClosedEvent(Id, _shopId, _customerWaId, _status, _messages.Count));
    }

    /// <summary>
    /// EN: Check if the 24h window has expired.
    /// VI: Kiểm tra xem cửa sổ 24h đã hết hạn chưa.
    /// </summary>
    /// <returns>EN: True if expired / VI: True nếu đã hết hạn</returns>
    public bool CheckExpiry()
    {
        if (_status != ConversationStatus.Active)
            return false;

        if (_expiresAt.HasValue && DateTime.UtcNow > _expiresAt.Value)
        {
            _status = ConversationStatus.Expired;
            StatusId = ConversationStatus.Expired.Id;

            AddDomainEvent(new ConversationClosedEvent(Id, _shopId, _customerWaId, _status, _messages.Count));
            return true;
        }

        return false;
    }

    /// <summary>
    /// EN: Check if conversation is within 24h messaging window.
    /// VI: Kiểm tra xem conversation có trong cửa sổ nhắn tin 24h không.
    /// </summary>
    /// <returns>EN: True if within window / VI: True nếu trong cửa sổ</returns>
    public bool IsWithinMessagingWindow()
    {
        if (_status != ConversationStatus.Active)
            return false;

        return _expiresAt.HasValue && DateTime.UtcNow <= _expiresAt.Value;
    }
}
