using IamService.Domain.Events;
using IamService.Domain.SeedWork;

namespace IamService.Domain.AggregatesModel.GroupAggregate;

/// <summary>
/// EN: Group aggregate root for team management within organization.
/// VI: Aggregate root nhóm để quản lý team trong tổ chức.
/// </summary>
public class Group : Entity, IAggregateRoot
{
    // EN: Private fields for encapsulation
    // VI: Fields private để đóng gói
    private string _name = null!;
    private string? _description;
    private Guid _organizationId;
    private DateTime _createdAt;
    private DateTime? _updatedAt;
    private bool _isDeleted;

    private readonly List<GroupMember> _members = [];
    private readonly List<GroupPermission> _permissions = [];


    /// <summary>
    /// EN: Group name.
    /// VI: Tên nhóm.
    /// </summary>
    public string Name => _name;

    /// <summary>
    /// EN: Group description.
    /// VI: Mô tả nhóm.
    /// </summary>
    public string? Description => _description;

    /// <summary>
    /// EN: Organization ID this group belongs to.
    /// VI: ID tổ chức mà nhóm thuộc về.
    /// </summary>
    public Guid OrganizationId => _organizationId;

    /// <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: Whether the group is soft deleted.
    /// VI: Nhóm có bị xóa mềm không.
    /// </summary>
    public bool IsDeleted => _isDeleted;

    /// <summary>
    /// EN: Group members.
    /// VI: Thành viên nhóm.
    /// </summary>
    public IReadOnlyCollection<GroupMember> Members => _members.AsReadOnly();

    /// <summary>
    /// EN: Group permissions.
    /// VI: Quyền nhóm.
    /// </summary>
    public IReadOnlyCollection<GroupPermission> Permissions => _permissions.AsReadOnly();


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

    /// <summary>
    /// EN: Create a new group.
    /// VI: Tạo nhóm mới.
    /// </summary>
    public Group(Guid organizationId, string name, string? description = null) : this()
    {
        if (organizationId == Guid.Empty)
            throw new ArgumentException("Organization ID cannot be empty", nameof(organizationId));
        if (string.IsNullOrWhiteSpace(name))
            throw new ArgumentException("Group name cannot be empty", nameof(name));

        Id = Guid.NewGuid();
        _organizationId = organizationId;
        _name = name.Trim();
        _description = description?.Trim();
        _createdAt = DateTime.UtcNow;
        _isDeleted = false;

        AddDomainEvent(new GroupCreatedEvent(Id, _organizationId, _name));
    }

    /// <summary>
    /// EN: Factory method to create group.
    /// VI: Factory method để tạo nhóm.
    /// </summary>
    public static Group Create(Guid organizationId, string name, string? description = null)
    {
        return new Group(organizationId, name, description);
    }

    /// <summary>
    /// EN: Update group information.
    /// VI: Cập nhật thông tin nhóm.
    /// </summary>
    public void Update(string name, string? description)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new ArgumentException("Group name cannot be empty", nameof(name));

        _name = name.Trim();
        _description = description?.Trim();
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Add a member to the group.
    /// VI: Thêm thành viên vào nhóm.
    /// </summary>
    public GroupMember AddMember(Guid userId, GroupRole? role = null, Guid? addedByUserId = null)
    {
        if (userId == Guid.Empty)
            throw new ArgumentException("User ID cannot be empty", nameof(userId));

        // EN: Check if user is already a member
        // VI: Kiểm tra user đã là thành viên chưa
        if (_members.Any(m => m.UserId == userId))
            throw new InvalidOperationException($"User {userId} is already a member of this group");

        var actualRole = role ?? GroupRole.Member;
        var member = new GroupMember(Id, userId, actualRole, addedByUserId);
        _members.Add(member);
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new MemberAddedToGroupEvent(Id, userId, actualRole.Name));

        return member;
    }

    /// <summary>
    /// EN: Remove a member from the group.
    /// VI: Xóa thành viên khỏi nhóm.
    /// </summary>
    public void RemoveMember(Guid userId)
    {
        var member = _members.FirstOrDefault(m => m.UserId == userId);
        if (member == null)
            throw new InvalidOperationException($"User {userId} is not a member of this group");

        // EN: Cannot remove the last owner
        // VI: Không thể xóa owner cuối cùng
        if (member.IsOwner() && _members.Count(m => m.IsOwner()) <= 1)
            throw new InvalidOperationException("Cannot remove the last owner of the group");

        _members.Remove(member);
        _updatedAt = DateTime.UtcNow;

        AddDomainEvent(new MemberRemovedFromGroupEvent(Id, userId));
    }

    /// <summary>
    /// EN: Change a member's role.
    /// VI: Thay đổi vai trò của thành viên.
    /// </summary>
    public void ChangeMemberRole(Guid userId, GroupRole newRole)
    {
        var member = _members.FirstOrDefault(m => m.UserId == userId);
        if (member == null)
            throw new InvalidOperationException($"User {userId} is not a member of this group");

        // EN: If demoting from owner, ensure at least one owner remains
        // VI: Nếu giáng chức từ owner, đảm bảo còn ít nhất một owner
        if (member.IsOwner() && newRole != GroupRole.Owner && _members.Count(m => m.IsOwner()) <= 1)
            throw new InvalidOperationException("Cannot demote the last owner of the group");

        member.ChangeRole(newRole);
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Add a permission to the group.
    /// VI: Thêm quyền cho nhóm.
    /// </summary>
    public GroupPermission AddPermission(string permission, string? resource = null, Guid? grantedByUserId = null)
    {
        if (string.IsNullOrWhiteSpace(permission))
            throw new ArgumentException("Permission cannot be empty", nameof(permission));

        // EN: Check if permission already exists
        // VI: Kiểm tra quyền đã tồn tại chưa
        if (_permissions.Any(p => p.Permission == permission.ToLowerInvariant() && p.Resource == resource))
            throw new InvalidOperationException($"Permission '{permission}' for resource '{resource}' already exists");

        var groupPermission = new GroupPermission(Id, permission, resource, grantedByUserId);
        _permissions.Add(groupPermission);
        _updatedAt = DateTime.UtcNow;

        return groupPermission;
    }

    /// <summary>
    /// EN: Remove a permission from the group.
    /// VI: Xóa quyền khỏi nhóm.
    /// </summary>
    public void RemovePermission(string permission, string? resource = null)
    {
        var groupPermission = _permissions.FirstOrDefault(p =>
            p.Permission == permission.ToLowerInvariant() &&
            p.Resource == resource);

        if (groupPermission == null)
            throw new InvalidOperationException($"Permission '{permission}' for resource '{resource}' not found");

        _permissions.Remove(groupPermission);
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Check if the group has a specific permission.
    /// VI: Kiểm tra nhóm có quyền cụ thể không.
    /// </summary>
    public bool HasPermission(string permission, string? resource = null)
    {
        return _permissions.Any(p => p.Matches(permission, resource));
    }

    /// <summary>
    /// EN: Soft delete the group.
    /// VI: Xóa mềm nhóm.
    /// </summary>
    public void Delete()
    {
        _isDeleted = true;
        _updatedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Restore a soft-deleted group.
    /// VI: Khôi phục nhóm đã xóa mềm.
    /// </summary>
    public void Restore()
    {
        _isDeleted = false;
        _updatedAt = DateTime.UtcNow;
    }


}
