namespace ChatService.Domain.AggregatesModel.ConversationAggregate;

using ChatService.Domain.Events;
using ChatService.Domain.Exceptions;
using ChatService.Domain.SeedWork;

/// <summary>
/// EN: Conversation aggregate root - represents a chat conversation.
/// VI: Conversation aggregate root - đại diện một cuộc hội thoại chat.
/// </summary>
public class Conversation : Entity, IAggregateRoot
{
#pragma warning disable CS0169 // Field is used by EF Core
    private int _typeId;
#pragma warning restore CS0169
    private ConversationType _type;
    private string? _name;
    private string? _avatarUrl;
    private readonly List<ConversationParticipant> _participants;
    private readonly List<Message> _messages;
    private DateTime _createdAt;
    private DateTime _updatedAt;
    private Guid? _lastMessageId;
    private DateTime? _lastMessageAt;

    /// <summary>
    /// EN: Type of conversation (Direct or Group).
    /// VI: Loại cuộc hội thoại (Direct hoặc Group).
    /// </summary>
    public ConversationType Type => _type;

    /// <summary>
    /// EN: Conversation name (optional for direct, required for group).
    /// VI: Tên cuộc hội thoại (tùy chọn cho direct, bắt buộc cho group).
    /// </summary>
    public string? Name => _name;

    /// <summary>
    /// EN: Conversation avatar URL (for group chats).
    /// VI: URL avatar cuộc hội thoại (cho group chats).
    /// </summary>
    public string? AvatarUrl => _avatarUrl;

    /// <summary>
    /// EN: Participants in this conversation.
    /// VI: Các thành viên trong cuộc hội thoại này.
    /// </summary>
    public IReadOnlyCollection<ConversationParticipant> Participants => _participants.AsReadOnly();

    /// <summary>
    /// EN: Messages in this conversation (should be loaded explicitly).
    /// VI: Các tin nhắn trong cuộc hội thoại này (nên load rõ ràng).
    /// </summary>
    public IReadOnlyCollection<Message> Messages => _messages.AsReadOnly();

    /// <summary>
    /// EN: ID of the last message in this conversation.
    /// VI: ID của tin nhắn cuối cùng trong cuộc hội thoại này.
    /// </summary>
    public Guid? LastMessageId => _lastMessageId;

    /// <summary>
    /// EN: Timestamp of the last message.
    /// VI: Thời điểm của tin nhắn cuối cùng.
    /// </summary>
    public DateTime? LastMessageAt => _lastMessageAt;

    public DateTime CreatedAt => _createdAt;
    public DateTime UpdatedAt => _updatedAt;

    protected Conversation()
    {
        // EN: Required by EF Core
        // VI: Yêu cầu bởi EF Core
        _type = ConversationType.Direct;
        _participants = new List<ConversationParticipant>();
        _messages = new List<Message>();
    }

    /// <summary>
    /// EN: Create a direct (1:1) conversation.
    /// VI: Tạo cuộc hội thoại trực tiếp (1:1).
    /// </summary>
    public static Conversation CreateDirect(Guid user1Id, Guid user2Id)
    {
        if (user1Id == user2Id)
            throw new ChatDomainException("Cannot create conversation with yourself");

        var conversation = new Conversation
        {
            Id = Guid.NewGuid(),
            _type = ConversationType.Direct,
            _createdAt = DateTime.UtcNow,
            _updatedAt = DateTime.UtcNow
        };

        conversation._participants.Add(new ConversationParticipant(conversation.Id, user1Id));
        conversation._participants.Add(new ConversationParticipant(conversation.Id, user2Id));

        conversation.AddDomainEvent(new ConversationCreatedDomainEvent(conversation));

        return conversation;
    }

    /// <summary>
    /// EN: Create a group conversation.
    /// VI: Tạo cuộc hội thoại nhóm.
    /// </summary>
    public static Conversation CreateGroup(string name, Guid creatorId, IEnumerable<Guid> participantIds, string? avatarUrl = null)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new ChatDomainException("Group name is required");

        var allParticipants = participantIds.Append(creatorId).Distinct().ToList();
        if (allParticipants.Count < 2)
            throw new ChatDomainException("Group must have at least 2 participants");

        var conversation = new Conversation
        {
            Id = Guid.NewGuid(),
            _type = ConversationType.Group,
            _name = name,
            _avatarUrl = avatarUrl,
            _createdAt = DateTime.UtcNow,
            _updatedAt = DateTime.UtcNow
        };

        // EN: Creator is admin
        // VI: Creator là admin
        conversation._participants.Add(new ConversationParticipant(conversation.Id, creatorId, isAdmin: true));

        foreach (var participantId in allParticipants.Where(p => p != creatorId))
        {
            conversation._participants.Add(new ConversationParticipant(conversation.Id, participantId));
        }

        conversation.AddDomainEvent(new ConversationCreatedDomainEvent(conversation));

        return conversation;
    }

    /// <summary>
    /// EN: Send a new message.
    /// VI: Gửi một tin nhắn mới.
    /// </summary>
    public Message SendMessage(
        Guid senderId,
        string encryptedContent,
        string nonce,
        MessageType? type = null,
        string? authTag = null,
        string? metadata = null,
        Guid? replyToMessageId = null)
    {
        // EN: Validate sender is a participant
        // VI: Xác thực người gửi là thành viên
        var participant = _participants.FirstOrDefault(p => p.UserId == senderId && p.IsActive);
        if (participant == null)
            throw new ChatDomainException("Sender is not a participant of this conversation");

        var message = new Message(
            Id,
            senderId,
            encryptedContent,
            nonce,
            type,
            authTag,
            metadata,
            replyToMessageId);

        _messages.Add(message);
        _lastMessageId = message.Id;
        _lastMessageAt = message.CreatedAt;
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new MessageSentDomainEvent(message, this));

        return message;
    }

    /// <summary>
    /// EN: Add a participant to group conversation.
    /// VI: Thêm thành viên vào cuộc hội thoại nhóm.
    /// </summary>
    public void AddParticipant(Guid userId, Guid addedByUserId)
    {
        if (_type == ConversationType.Direct)
            throw new ChatDomainException("Cannot add participants to direct conversation");

        // EN: Check if adder is admin
        // VI: Kiểm tra người thêm có phải admin không
        var adder = _participants.FirstOrDefault(p => p.UserId == addedByUserId && p.IsActive);
        if (adder == null || !adder.IsAdmin)
            throw new ChatDomainException("Only admins can add participants");

        // EN: Check if user already exists
        // VI: Kiểm tra user đã tồn tại chưa
        var existing = _participants.FirstOrDefault(p => p.UserId == userId);
        if (existing != null && existing.IsActive)
            throw new ChatDomainException("User is already a participant");

        _participants.Add(new ConversationParticipant(Id, userId));
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Remove a participant from group conversation.
    /// VI: Xóa thành viên khỏi cuộc hội thoại nhóm.
    /// </summary>
    public void RemoveParticipant(Guid userId, Guid removedByUserId)
    {
        if (_type == ConversationType.Direct)
            throw new ChatDomainException("Cannot remove participants from direct conversation");

        var participant = _participants.FirstOrDefault(p => p.UserId == userId && p.IsActive);
        if (participant == null)
            throw new ChatDomainException("User is not a participant");

        // EN: User can remove themselves, or admin can remove others
        // VI: User có thể tự rời, hoặc admin có thể xóa người khác
        if (userId != removedByUserId)
        {
            var remover = _participants.FirstOrDefault(p => p.UserId == removedByUserId && p.IsActive);
            if (remover == null || !remover.IsAdmin)
                throw new ChatDomainException("Only admins can remove other participants");
        }

        participant.Leave();
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Update group name.
    /// VI: Cập nhật tên nhóm.
    /// </summary>
    public void UpdateName(string name, Guid updatedByUserId)
    {
        if (_type == ConversationType.Direct)
            throw new ChatDomainException("Cannot update name of direct conversation");

        var updater = _participants.FirstOrDefault(p => p.UserId == updatedByUserId && p.IsActive);
        if (updater == null || !updater.IsAdmin)
            throw new ChatDomainException("Only admins can update group name");

        if (string.IsNullOrWhiteSpace(name))
            throw new ChatDomainException("Group name is required");

        _name = name;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Update group avatar.
    /// VI: Cập nhật avatar nhóm.
    /// </summary>
    public void UpdateAvatar(string? avatarUrl, Guid updatedByUserId)
    {
        if (_type == ConversationType.Direct)
            throw new ChatDomainException("Cannot update avatar of direct conversation");

        var updater = _participants.FirstOrDefault(p => p.UserId == updatedByUserId && p.IsActive);
        if (updater == null || !updater.IsAdmin)
            throw new ChatDomainException("Only admins can update group avatar");

        _avatarUrl = avatarUrl;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Get active participant count.
    /// VI: Lấy số lượng thành viên đang hoạt động.
    /// </summary>
    public int GetActiveParticipantCount()
    {
        return _participants.Count(p => p.IsActive);
    }

    /// <summary>
    /// EN: Check if user is a participant.
    /// VI: Kiểm tra user có phải thành viên không.
    /// </summary>
    public bool IsParticipant(Guid userId)
    {
        return _participants.Any(p => p.UserId == userId && p.IsActive);
    }

    /// <summary>
    /// EN: Get the other participant in a direct conversation.
    /// VI: Lấy thành viên kia trong cuộc hội thoại trực tiếp.
    /// </summary>
    public Guid? GetOtherParticipant(Guid userId)
    {
        if (_type != ConversationType.Direct)
            return null;

        return _participants
            .FirstOrDefault(p => p.UserId != userId && p.IsActive)
            ?.UserId;
    }
}
