using NPin.Framework.Core.Crypt.BCrypt;
using NPin.Framework.SqlSugarCore.Abstractions.Data;
using NPin.Framework.Upms.Domain.Shared.Enums;
using SqlSugar;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;

namespace NPin.Framework.Upms.Domain.Entities;

[SugarTable("User", "用户表")]
public class UserEntity : Entity<Guid>, ISoftDelete, IAuditedObject, IEnabled, IOrderNum
{
    #region User

    [SugarColumn(IsPrimaryKey = true)] public override Guid Id { get; protected set; }

    [SugarColumn(ColumnDescription = "用户名")]
    public string Username { get; set; } = string.Empty;

    [SugarColumn(ColumnDescription = "手机号码")]
    public string PhoneNumber { get; set; }

    [SugarColumn(ColumnDescription = "邮箱")]
    public string? Email { get; set; }

    [SugarColumn(ColumnDescription = "昵称")]
    public string? Nickname { get; set; }

    [SugarColumn(ColumnDescription = "密码")]
    public string Password { get; set; } = string.Empty;

    [SugarColumn(ColumnDescription = "密码加盐值")]
    public string Salt { get; set; } = string.Empty;

    [SugarColumn(ColumnDescription = "简介")]
    public string? Introduction { get; set; }

    [SugarColumn(ColumnDescription = "性别")]
    public GenderEnum Gender { get; set; } = GenderEnum.Secrecy;

    [SugarColumn(ColumnDescription = "注册IP地址")]
    public string? IpAddr { get; set; }

    [SugarColumn(ColumnDescription = "头像")]
    public string? Avatar { get; set; }

    #endregion

    #region Implements

    /// <summary>
    /// 逻辑删除
    /// </summary>
    public bool IsDeleted { get; }

    public DateTime CreationTime { get; } = DateTime.Now;
    public Guid? CreatorId { get; }
    public DateTime? LastModificationTime { get; }
    public Guid? LastModifierId { get; }

    /// <summary>
    /// 是否启用
    /// </summary>
    public bool IsEnabled { get; set; }

    public int OrderNum { get; set; }

    #endregion

    #region 关联关系（导航）

    /// <summary>
    /// 用户元数据，保存可扩展数据
    /// 这里不使用Json的原因是有些库不是很好支持
    /// </summary>
    [Navigate(NavigateType.OneToMany, nameof(UserMetaEntity.UserId))]
    public List<UserMetaEntity> Metadata { get; set; }

    /// <summary>
    /// 角色列表，多对多
    /// </summary>
    [Navigate(typeof(UserRoleEntity), nameof(UserRoleEntity.UserId), nameof(UserRoleEntity.RoleId))]
    public List<RoleEntity> Roles { get; set; }

    /// <summary>
    /// 岗位列表，多对多
    /// </summary>
    [Navigate(typeof(UserPostEntity), nameof(UserPostEntity.UserId), nameof(UserPostEntity.PostId))]
    public List<PostEntity> Posts { get; set; }

    [Navigate(typeof(UserOrganizationEntity), nameof(UserOrganizationEntity.UserId),
        nameof(UserOrganizationEntity.OrganizationId))]
    public List<OrganizationEntity> Organizations { get; set; }

    #endregion

    public UserEntity()
    {
    }

    public UserEntity(string username, string phoneNumber, string password, string? nickname)
    {
        Username = username;
        PhoneNumber = phoneNumber;
        if (username.IsNullOrEmpty())
        {
            username = $"用户{phoneNumber}";
        }

        Nickname = nickname.IsNullOrWhiteSpace() ? username : nickname;
        EncryptPassword(password);
    }

    /// <summary>
    /// 通过随机盐值给密码加密，使用BCrypt算法
    /// </summary>
    /// <param name="password"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public UserEntity EncryptPassword(string? password)
    {
        // 若传入密码无值，则使用原本Password
        // 若原本Password依然无值，则抛出参数异常
        if (password == null)
        {
            if (Password.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(Password));
            }

            password = Password;
        }

        Salt = BCrypt.GenerateSalt();
        Password = BCrypt.Generate(password, Salt, 0).ToString()!;

        return this;
    }

    /// <summary>
    /// 检查密码是否一致
    /// </summary>
    /// <param name="password"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public bool CheckPassword(string password)
    {
        if (Salt is null)
        {
            throw new ArgumentNullException(nameof(Salt));
        }

        return BCrypt.Check(Password, password, Salt, 0);
    }
}