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

namespace IamService.Domain.AggregatesModel.AccessRequestAggregate;

/// <summary>
/// EN: Access request aggregate root for managing access request workflows.
/// VI: Aggregate root yêu cầu truy cập để quản lý workflow yêu cầu truy cập.
/// </summary>
public class AccessRequest : Entity, IAggregateRoot
{
    private Guid _requesterId;
    private string _resourceType = null!;
    private Guid _resourceId;
    private string _requestedPermission = null!;
    private string? _justification;
    private AccessRequestStatus _status = null!;
    private AccessRequestPriority _priority = null!;
    private DateTime _createdAt;
    private DateTime? _submittedAt;
    private DateTime? _resolvedAt;
    private DateTime? _expiresAt;

    private readonly List<AccessRequestApprover> _approvers = [];

    #region Properties

    public Guid RequesterId => _requesterId;
    public string ResourceType => _resourceType;
    public Guid ResourceId => _resourceId;
    public string RequestedPermission => _requestedPermission;
    public string? Justification => _justification;
    public AccessRequestStatus Status => _status;
    public AccessRequestPriority Priority => _priority;
    public DateTime CreatedAt => _createdAt;
    public DateTime? SubmittedAt => _submittedAt;
    public DateTime? ResolvedAt => _resolvedAt;
    public DateTime? ExpiresAt => _expiresAt;
    public IReadOnlyCollection<AccessRequestApprover> Approvers => _approvers.AsReadOnly();

    #endregion

    protected AccessRequest() { }

    private AccessRequest(
        Guid requesterId,
        string resourceType,
        Guid resourceId,
        string requestedPermission,
        string? justification,
        AccessRequestPriority? priority)
    {
        Id = Guid.NewGuid();
        _requesterId = requesterId;
        _resourceType = resourceType;
        _resourceId = resourceId;
        _requestedPermission = requestedPermission;
        _justification = justification;
        _priority = priority ?? AccessRequestPriority.Medium;
        _status = AccessRequestStatus.Draft;
        _createdAt = DateTime.UtcNow;

        AddDomainEvent(new AccessRequestCreatedEvent(Id, requesterId, resourceType, resourceId, requestedPermission));
    }

    /// <summary>
    /// EN: Factory method to create access request.
    /// VI: Factory method để tạo yêu cầu truy cập.
    /// </summary>
    public static AccessRequest Create(
        Guid requesterId,
        string resourceType,
        Guid resourceId,
        string requestedPermission,
        string? justification = null,
        AccessRequestPriority? priority = null)
    {
        if (requesterId == Guid.Empty)
            throw new ArgumentException("Requester ID cannot be empty", nameof(requesterId));
        if (string.IsNullOrWhiteSpace(resourceType))
            throw new ArgumentException("Resource type cannot be empty", nameof(resourceType));
        if (resourceId == Guid.Empty)
            throw new ArgumentException("Resource ID cannot be empty", nameof(resourceId));
        if (string.IsNullOrWhiteSpace(requestedPermission))
            throw new ArgumentException("Requested permission cannot be empty", nameof(requestedPermission));

        return new AccessRequest(requesterId, resourceType, resourceId, requestedPermission, justification, priority);
    }

    /// <summary>
    /// EN: Add an approver to the approval chain.
    /// VI: Thêm người phê duyệt vào chuỗi phê duyệt.
    /// </summary>
    public AccessRequestApprover AddApprover(Guid userId)
    {
        if (_status != AccessRequestStatus.Draft)
            throw new InvalidOperationException("Cannot add approvers after request is submitted");

        var order = _approvers.Count + 1;
        var approver = new AccessRequestApprover(Id, userId, order);
        _approvers.Add(approver);
        return approver;
    }

    /// <summary>
    /// EN: Submit request for approval.
    /// VI: Gửi yêu cầu để phê duyệt.
    /// </summary>
    public void Submit(int expirationDays = 7)
    {
        if (_status != AccessRequestStatus.Draft)
            throw new InvalidOperationException("Only draft requests can be submitted");

        if (_approvers.Count == 0)
            throw new InvalidOperationException("At least one approver is required");

        _status = AccessRequestStatus.Pending;
        _submittedAt = DateTime.UtcNow;
        _expiresAt = DateTime.UtcNow.AddDays(expirationDays);

        AddDomainEvent(new AccessRequestSubmittedEvent(Id, _requesterId, _approvers.Select(a => a.UserId).ToList()));
    }

    /// <summary>
    /// EN: Approve the request by an approver.
    /// VI: Phê duyệt yêu cầu bởi người phê duyệt.
    /// </summary>
    public void Approve(Guid approverId, string? comments = null)
    {
        if (_status != AccessRequestStatus.Pending)
            throw new InvalidOperationException("Only pending requests can be approved");

        var approver = _approvers.FirstOrDefault(a => a.UserId == approverId && a.Status == ApproverStatus.Pending);
        if (approver == null)
            throw new InvalidOperationException("User is not a pending approver for this request");

        approver.Approve(comments);

        // EN: Check if all approvers have approved
        // VI: Kiểm tra xem tất cả approvers đã approve chưa
        if (_approvers.All(a => a.Status == ApproverStatus.Approved))
        {
            _status = AccessRequestStatus.Approved;
            _resolvedAt = DateTime.UtcNow;
            AddDomainEvent(new AccessRequestApprovedEvent(Id, _requesterId, _resourceType, _resourceId, _requestedPermission));
        }
    }

    /// <summary>
    /// EN: Reject the request.
    /// VI: Từ chối yêu cầu.
    /// </summary>
    public void Reject(Guid approverId, string? reason = null)
    {
        if (_status != AccessRequestStatus.Pending)
            throw new InvalidOperationException("Only pending requests can be rejected");

        var approver = _approvers.FirstOrDefault(a => a.UserId == approverId && a.Status == ApproverStatus.Pending);
        if (approver == null)
            throw new InvalidOperationException("User is not a pending approver for this request");

        approver.Reject(reason);
        _status = AccessRequestStatus.Rejected;
        _resolvedAt = DateTime.UtcNow;

        AddDomainEvent(new AccessRequestRejectedEvent(Id, _requesterId, approverId, reason));
    }

    /// <summary>
    /// EN: Cancel the request (by requester).
    /// VI: Hủy yêu cầu (bởi người yêu cầu).
    /// </summary>
    public void Cancel()
    {
        if (_status.IsTerminal)
            throw new InvalidOperationException("Cannot cancel a terminal request");

        _status = AccessRequestStatus.Cancelled;
        _resolvedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Mark request as expired.
    /// VI: Đánh dấu yêu cầu đã hết hạn.
    /// </summary>
    public void Expire()
    {
        if (_status != AccessRequestStatus.Pending)
            throw new InvalidOperationException("Only pending requests can expire");

        _status = AccessRequestStatus.Expired;
        _resolvedAt = DateTime.UtcNow;
    }

    /// <summary>
    /// EN: Update justification.
    /// VI: Cập nhật lý do.
    /// </summary>
    public void UpdateJustification(string justification)
    {
        if (_status != AccessRequestStatus.Draft)
            throw new InvalidOperationException("Cannot update submitted request");

        _justification = justification;
    }
}
