using Books.Api.Domain.UserAccess.Events;
using EventFlow.Aggregates;

namespace Books.Api.Domain.UserAccess;

public class UserCompanyAccessAggregate : AggregateRoot<UserCompanyAccessAggregate, UserCompanyAccessId>,
    IEmit<UserCompanyAccessGrantedEvent>,
    IEmit<UserCompanyAccessRoleChangedEvent>,
    IEmit<UserCompanyAccessRevokedEvent>
{
    public string UserId { get; private set; } = string.Empty;
    public string CompanyId { get; private set; } = string.Empty;
    public CompanyRole Role { get; private set; }
    public string GrantedBy { get; private set; } = string.Empty;
    public DateTimeOffset GrantedAt { get; private set; }
    public bool IsActive { get; private set; }
    public DateTimeOffset? RevokedAt { get; private set; }
    public string? RevokedBy { get; private set; }

    public UserCompanyAccessAggregate(UserCompanyAccessId id) : base(id) { }

    /// <summary>
    /// Grant access to a user for a company.
    /// </summary>
    public void GrantAccess(string userId, string companyId, CompanyRole role, string grantedBy)
    {
        if (!IsNew && IsActive)
        {
            throw new DomainException(
                "ACCESS_ALREADY_GRANTED",
                $"User '{userId}' already has access to company '{companyId}'",
                $"Brugeren '{userId}' har allerede adgang til virksomheden");
        }

        // If previously revoked, we're re-granting
        Emit(new UserCompanyAccessGrantedEvent(userId, companyId, role, grantedBy));
    }

    /// <summary>
    /// Change the user's role for this company.
    /// </summary>
    public void ChangeRole(CompanyRole newRole, string changedBy)
    {
        if (!IsActive)
        {
            throw new DomainException(
                "ACCESS_NOT_ACTIVE",
                "Cannot change role - access is not active",
                "Kan ikke ændre rolle - adgang er ikke aktiv");
        }

        if (Role == newRole)
        {
            throw new DomainException(
                "ROLE_UNCHANGED",
                $"User already has role '{newRole}'",
                $"Brugeren har allerede rollen '{newRole}'");
        }

        Emit(new UserCompanyAccessRoleChangedEvent(Role, newRole, changedBy));
    }

    /// <summary>
    /// Revoke the user's access to this company.
    /// </summary>
    public void RevokeAccess(string revokedBy)
    {
        if (!IsActive)
        {
            throw new DomainException(
                "ACCESS_ALREADY_REVOKED",
                "Access is already revoked",
                "Adgang er allerede tilbagekaldt");
        }

        Emit(new UserCompanyAccessRevokedEvent(revokedBy));
    }

    public void Apply(UserCompanyAccessGrantedEvent e)
    {
        UserId = e.UserId;
        CompanyId = e.CompanyId;
        Role = e.Role;
        GrantedBy = e.GrantedBy;
        GrantedAt = DateTimeOffset.UtcNow;
        IsActive = true;
        RevokedAt = null;
        RevokedBy = null;
    }

    public void Apply(UserCompanyAccessRoleChangedEvent e)
    {
        Role = e.NewRole;
    }

    public void Apply(UserCompanyAccessRevokedEvent e)
    {
        IsActive = false;
        RevokedAt = DateTimeOffset.UtcNow;
        RevokedBy = e.RevokedBy;
    }
}
