using Knot.Contracts.Auth.Domain;
using Knot.Shared.Kernel;
using Knot.Shared.Kernel.Events;

namespace Knot.Modules.Auth.Domain;

/// <summary>
/// �������� ������������ � ��������� ������������� (Identity).
/// </summary>
public sealed class User : AggregateRoot<Guid>
{
    public string Username { get; private set; }
    public string PasswordHash { get; private set; }
    public string DisplayName { get; private set; }
    public string? Email { get; private set; }
    public string? Bio { get; private set; }
    public string? Avatar { get; private set; }
    public DateTime? Birthday { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public bool HideStoryViews { get; private set; }
    public bool IsExternal { get; private set; }
    public string? Domain { get; private set; }
    public bool IsOnline { get; private set; }
    public DateTime? LastSeen { get; private set; }
    public bool HideStatus { get; private set; }
    public bool IsBanned { get; private set; }
    public string? PhoneNumber { get; private set; }
    public string? RefreshToken { get; private set; }
    public DateTime? RefreshTokenExpiry { get; private set; }
    public DateTime? BannedUntil { get; private set; }

    public void Ban() {
        IsBanned = true;
        RaiseDomainEvent(new UserBannedDomainEvent(Id, true));
    }
    public void Unban() {
        IsBanned = false;
        RaiseDomainEvent(new UserBannedDomainEvent(Id, false));
    }

    public void SetOnline(bool isOnline, DateTime? lastSeen = null)
    {
        IsOnline = isOnline;
        LastSeen = lastSeen ?? DateTime.UtcNow;
    }

    public void SetPhoneNumber(string? phoneNumber)
    {
        PhoneNumber = phoneNumber;
    }

    public void SetRefreshToken(string? refreshToken, DateTime? expiry = null)
    {
        RefreshToken = refreshToken;
        RefreshTokenExpiry = expiry;
    }

    public void SetBannedUntil(DateTime? bannedUntil)
    {
        BannedUntil = bannedUntil;
    }

    public void SetAsExternal(string domain)
    {
        IsExternal = true;
        UserDomain = domain;
    }

    /// <summary>
    /// Updates user from contract (used by repository).
    /// </summary>
    public void UpdateFromContract(UserContract contract)
    {
        Username = contract.Username;
        DisplayName = contract.DisplayName;
        PhoneNumber = contract.PhoneNumber;
        Bio = contract.Bio;
        Avatar = contract.Avatar;
        Birthday = contract.Birthday;
        IsBanned = contract.IsBanned;
        BannedUntil = contract.BannedUntil;
        SetOnline(contract.IsOnline, contract.LastSeen);
        UserDomain = contract.Domain;
        RefreshToken = contract.RefreshToken;
        RefreshTokenExpiry = contract.RefreshTokenExpiry;
    }

    private User(Guid id, string username, string passwordHash, string displayName, string? email, string? bio = null)
        : base(id)
    {
        Username = username;
        PasswordHash = passwordHash;
        DisplayName = displayName;
        Email = email;
        Bio = bio;
        CreatedAt = DateTime.UtcNow;
        IsBanned = false;
    }

    /// <summary>
    /// ��������� ����� ��� �������� ������ ������������.
    /// </summary>
    public static User Create(string username, string passwordHash, string displayName, string? email = null, string? bio = null)
    {
        var user = new User(Guid.NewGuid(), username, passwordHash, displayName, email, bio);
        user.RaiseDomainEvent(new UserRegisteredDomainEvent(user.Id, user.Username, user.DisplayName, user.Bio));
        return user;
    }

    /// <summary>
    /// ������� "��������" ������������ ��� �������� � ������� �������.
    /// </summary>
    public static User CreateExternal(Guid id, string username, string displayName, string domain, string? avatar = null)
    {
        var user = new User(id, username, "EXTERNAL_USER", displayName, null, null);
        user.SetAsExternal(domain);
        if (avatar != null)
            user.UpdateAvatar(avatar);
        return user;
    }

    private string? _domain;
    public string? UserDomain
    {
        get => _domain;
        set => _domain = value;
    }

    public void UpdateProfile(string displayName, string? bio, DateTime? birthday)
    {
        DisplayName = displayName;
        Bio = bio;
        Birthday = birthday;
    }

    public void UpdateAvatar(string? avatarUrl)
    {
        Avatar = avatarUrl;
    }

    public void UpdateSettings(bool hideStoryViews)
    {
        HideStoryViews = hideStoryViews;
    }

    public void ChangePassword(string newPasswordHash)
    {
        PasswordHash = newPasswordHash;
    }

    public void UpdateStatus(bool isOnline)
    {
        IsOnline = isOnline;
        LastSeen = DateTime.UtcNow;


        if (!HideStatus)
        {
            RaiseDomainEvent(new UserStatusChangedDomainEvent(Id, IsOnline, LastSeen.Value));
        }
    }

    public void SetHideStatus(bool hide)
    {
        HideStatus = hide;
    }

    public UserContract ToContract()
    {
        return new UserContract
        {
            Id = Id,
            Username = Username,
            DisplayName = DisplayName,
            PasswordHash = PasswordHash,
            PhoneNumber = PhoneNumber,
            Email = Email,
            Bio = Bio,
            Avatar = Avatar,
            Birthday = Birthday,
            IsBot = false,
            CreatedAt = CreatedAt,
            IsBanned = IsBanned,
            BannedUntil = BannedUntil,
            IsOnline = IsOnline,
            IsExternal = IsExternal,
            Domain = _domain,
            LastSeen = LastSeen,
            RefreshToken = RefreshToken,
            RefreshTokenExpiry = RefreshTokenExpiry
        };
    }
}
