using DotNext;
using LinqToDB;
using LinqToDB.Data;

namespace Database;

public class UserManager
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private readonly AppDataConnection _db;

    public UserManager(AppDataConnection db)
    {
        this._db = db;
    }

    /// <summary>
    /// 添加一个新的用户到数据库
    /// </summary>
    /// <param name="name">用户的名称</param>
    /// <param name="email">用户的电子邮箱地址</param>
    /// <param name="password">用户的密码</param>
    /// <returns>插入的记录数</returns>
    public int AddUser(string name, string email, string password)
    {
        var user = new User()
        {
            Name = name,
            EMail = email,
            Password = password,
            Permission = UserPermission.Normal,
        };
        var result = _db.Insert(user);
        logger.Info($"新用户已添加: {name} ({email})");
        return result;
    }

    /// <summary>
    /// 根据用户名获取用户信息
    /// </summary>
    /// <param name="name">用户名</param>
    /// <returns>包含用户信息的结果，如果未找到或出错则返回相应状态</returns>
    public Result<Optional<User>> GetUserByName(string name)
    {
        var user = _db.UserTable.Where((user) => user.Name == name).ToArray();

        if (user.Length > 1)
        {
            logger.Error($"数据库中存在多个同名用户: {name}");
            return new(new Exception($"数据库中存在多个同名用户: {name}"));
        }

        if (user.Length == 0)
        {
            logger.Info($"未找到用户: {name}");
            return new(Optional<User>.None);
        }

        logger.Debug($"成功获取用户信息: {name}");
        return new(user[0]);
    }

    /// <summary>
    /// 根据电子邮箱获取用户信息
    /// </summary>
    /// <param name="email">用户的电子邮箱地址</param>
    /// <returns>包含用户信息的结果，如果未找到或出错则返回相应状态</returns>
    public Result<Optional<User>> GetUserByEMail(string email)
    {
        var user = _db.UserTable.Where((user) => user.EMail == email).ToArray();

        if (user.Length > 1)
        {
            logger.Error($"数据库中存在多个相同邮箱的用户: {email}");
            return new(new Exception($"数据库中存在多个相同邮箱的用户: {email}"));
        }

        if (user.Length == 0)
        {
            logger.Info($"未找到邮箱对应的用户: {email}");
            return new(Optional<User>.None);
        }

        logger.Debug($"成功获取用户信息: {email}");
        return new(user[0]);
    }

    /// <summary>
    /// 验证用户密码
    /// </summary>
    /// <param name="name">用户名</param>
    /// <param name="password">用户密码</param>
    /// <returns>如果密码正确返回用户信息，否则返回空</returns>
    public Result<Optional<User>> CheckUserPassword(string name, string password)
    {
        var ret = GetUserByName(name);
        if (!ret.IsSuccessful)
            return new(ret.Error);

        if (!ret.Value.HasValue)
            return new(Optional<User>.None);

        var user = ret.Value.Value;

        if (user.Password == password)
        {
            logger.Info($"用户 {name} 密码验证成功");
            return new(user);
        }
        else
        {
            logger.Warn($"用户 {name} 密码验证失败");
            return new(Optional<User>.None);
        }
    }

    /// <summary>
    /// 绑定用户与实验板
    /// </summary>
    /// <param name="userId">用户的唯一标识符</param>
    /// <param name="boardId">实验板的唯一标识符</param>
    /// <param name="expireTime">绑定过期时间</param>
    /// <returns>更新的记录数</returns>
    public int BindUserToBoard(Guid userId, Guid boardId, DateTime expireTime)
    {
        // 获取用户信息
        var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();
        if (user == null)
        {
            logger.Error($"未找到用户: {userId}");
            return 0;
        }

        // 更新用户的板子绑定信息
        var userResult = _db.UserTable
            .Where(u => u.ID == userId)
            .Set(u => u.BoardID, boardId)
            .Set(u => u.BoardExpireTime, expireTime)
            .Update();

        // 更新板子的用户绑定信息
        var boardResult = _db.BoardTable
            .Where(b => b.ID == boardId)
            .Set(b => b.Status, BoardStatus.Busy)
            .Set(b => b.OccupiedUserID, userId)
            .Set(b => b.OccupiedUserName, user.Name)
            .Update();

        logger.Info($"用户 {userId} ({user.Name}) 已绑定到实验板 {boardId}，过期时间: {expireTime}");
        return userResult + boardResult;
    }

    /// <summary>
    /// 解除用户与实验板的绑定
    /// </summary>
    /// <param name="userId">用户的唯一标识符</param>
    /// <returns>更新的记录数</returns>
    public int UnbindUserFromBoard(Guid userId)
    {
        // 获取用户当前绑定的板子ID
        var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();
        Guid boardId = user?.BoardID ?? Guid.Empty;

        // 清空用户的板子绑定信息
        var userResult = _db.UserTable
            .Where(u => u.ID == userId)
            .Set(u => u.BoardID, Guid.Empty)
            .Set(u => u.BoardExpireTime, (DateTime?)null)
            .Update();

        // 如果用户原本绑定了板子，则清空板子的用户绑定信息
        int boardResult = 0;
        if (boardId != Guid.Empty)
        {
            boardResult = _db.BoardTable
                .Where(b => b.ID == boardId)
                .Set(b => b.Status, BoardStatus.Available)
                .Set(b => b.OccupiedUserID, Guid.Empty)
                .Set(b => b.OccupiedUserName, (string?)null)
                .Update();
            logger.Info($"实验板 {boardId} 状态已设置为空闲，用户绑定信息已清空");
        }

        logger.Info($"用户 {userId} 已解除实验板绑定");
        return userResult + boardResult;
    }

    /// <summary>
    /// 自动分配一个未被占用的IP地址
    /// </summary>
    /// <returns>分配的IP地址字符串</returns>
    public string AllocateIpAddr()
    {
        var usedIps = _db.BoardTable.Select(b => b.IpAddr).ToArray();
        for (int i = 1; i <= 254; i++)
        {
            string ip = $"169.254.109.{i}";
            if (!usedIps.Contains(ip))
                return ip;
        }
        throw new Exception("没有可用的IP地址");
    }

    /// <summary>
    /// 自动分配一个未被占用的MAC地址
    /// </summary>
    /// <returns>分配的MAC地址字符串</returns>
    public string AllocateMacAddr()
    {
        var usedMacs = _db.BoardTable.Select(b => b.MacAddr).ToArray();
        // 以 02-00-00-xx-xx-xx 格式分配，02 表示本地管理地址
        for (int i = 1; i <= 0xFFFFFF; i++)
        {
            string mac = $"02-00-00-{(i >> 16) & 0xFF:X2}-{(i >> 8) & 0xFF:X2}-{i & 0xFF:X2}";
            if (!usedMacs.Contains(mac))
                return mac;
        }
        throw new Exception("没有可用的MAC地址");
    }

    /// <summary>
    /// 添加一块新的 FPGA 板子到数据库
    /// </summary>
    /// <param name="name">FPGA 板子的名称</param>
    /// <returns>插入的记录数</returns>
    public Guid AddBoard(string name)
    {
        if (string.IsNullOrWhiteSpace(name) || name.Contains('\'') || name.Contains(';'))
        {
            logger.Error("实验板名称非法，包含不允许的字符");
            throw new ArgumentException("实验板名称非法");
        }
        var board = new Board()
        {
            BoardName = name,
            IpAddr = AllocateIpAddr(),
            MacAddr = AllocateMacAddr(),
            Status = BoardStatus.Disabled,
        };
        var result = _db.Insert(board);
        logger.Info($"新实验板已添加: {name} ({board.IpAddr}:{board.MacAddr})");
        return board.ID;
    }

    /// <summary>
    /// 根据名称删除实验板
    /// </summary>
    /// <param name="name">实验板的名称</param>
    /// <returns>删除的记录数</returns>
    public int DeleteBoardByName(string name)
    {
        // 先获取要删除的板子信息
        var board = _db.BoardTable.Where(b => b.BoardName == name).FirstOrDefault();
        if (board == null)
        {
            logger.Warn($"未找到名称为 {name} 的实验板");
            return 0;
        }

        // 如果板子被占用，先解除绑定
        if (board.OccupiedUserID != Guid.Empty)
        {
            _db.UserTable
                .Where(u => u.ID == board.OccupiedUserID)
                .Set(u => u.BoardID, Guid.Empty)
                .Set(u => u.BoardExpireTime, (DateTime?)null)
                .Update();
            logger.Info($"已解除用户 {board.OccupiedUserID} 与实验板 {name} 的绑定");
        }

        var result = _db.BoardTable.Where(b => b.BoardName == name).Delete();
        logger.Info($"实验板已删除: {name}，删除记录数: {result}");
        return result;
    }

    /// <summary>
    /// 根据ID删除实验板
    /// </summary>
    /// <param name="id">实验板的唯一标识符</param>
    /// <returns>删除的记录数</returns>
    public int DeleteBoardByID(Guid id)
    {
        // 先获取要删除的板子信息
        var board = _db.BoardTable.Where(b => b.ID == id).FirstOrDefault();
        if (board == null)
        {
            logger.Warn($"未找到ID为 {id} 的实验板");
            return 0;
        }

        // 如果板子被占用，先解除绑定
        if (board.OccupiedUserID != Guid.Empty)
        {
            _db.UserTable
                .Where(u => u.ID == board.OccupiedUserID)
                .Set(u => u.BoardID, Guid.Empty)
                .Set(u => u.BoardExpireTime, (DateTime?)null)
                .Update();
            logger.Info($"已解除用户 {board.OccupiedUserID} 与实验板 {id} 的绑定");
        }

        var result = _db.BoardTable.Where(b => b.ID == id).Delete();
        logger.Info($"实验板已删除: {id}，删除记录数: {result}");
        return result;
    }

    /// <summary>
    /// 根据实验板ID获取实验板信息
    /// </summary>
    /// <param name="id">实验板的唯一标识符</param>
    /// <returns>包含实验板信息的结果，如果未找到则返回空</returns>
    public Result<Optional<Board>> GetBoardByID(Guid id)
    {
        var boards = _db.BoardTable.Where(board => board.ID == id).ToArray();

        if (boards.Length > 1)
        {
            logger.Error($"数据库中存在多个相同ID的实验板: {id}");
            return new(new Exception($"数据库中存在多个相同ID的实验板: {id}"));
        }

        if (boards.Length == 0)
        {
            logger.Info($"未找到ID对应的实验板: {id}");
            return new(Optional<Board>.None);
        }

        logger.Debug($"成功获取实验板信息: {id}");
        return new(boards[0]);
    }

    /// <summary>
    /// 根据用户名获取实验板信息
    /// </summary>
    /// <param name="userName">用户名</param>
    /// <returns>包含实验板信息的结果，如果未找到则返回空</returns>
    public Result<Optional<Board>> GetBoardByUserName(string userName)
    {
        var boards = _db.BoardTable.Where(board => board.OccupiedUserName == userName).ToArray();

        if (boards.Length > 1)
        {
            logger.Error($"数据库中存在多个相同用户名的实验板: {userName}");
            return new(new Exception($"数据库中存在多个相同用户名的实验板: {userName}"));
        }

        if (boards.Length == 0)
        {
            logger.Info($"未找到用户名对应的实验板: {userName}");
            return new(Optional<Board>.None);
        }

        logger.Debug($"成功获取实验板信息: {userName}");
        return new(boards[0]);
    }

    /// <summary>
    /// 获取所有实验板信息
    /// </summary>
    /// <returns>所有实验板的数组</returns>
    public Board[] GetAllBoard()
    {
        var boards = _db.BoardTable.ToArray();
        logger.Debug($"获取所有实验板，共 {boards.Length} 块");
        return boards;
    }

    /// <summary>
    /// 获取一块可用的实验板并将其状态设置为繁忙
    /// </summary>
    /// <param name="userId">要分配板子的用户ID</param>
    /// <param name="expireTime">绑定过期时间</param>
    /// <returns>可用的实验板，如果没有可用的板子则返回空</returns>
    public Optional<Board> GetAvailableBoard(Guid userId, DateTime expireTime)
    {
        var boards = _db.BoardTable.Where(
                (board) => board.Status == BoardStatus.Available
            ).ToArray();

        if (boards.Length == 0)
        {
            logger.Warn("没有可用的实验板");
            return new(null);
        }
        else
        {
            var board = boards[0];
            var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();

            if (user == null)
            {
                logger.Error($"未找到用户: {userId}");
                return new(null);
            }

            // 更新板子状态和用户绑定信息
            _db.BoardTable
                .Where(target => target.ID == board.ID)
                .Set(target => target.Status, BoardStatus.Busy)
                .Set(target => target.OccupiedUserID, userId)
                .Set(target => target.OccupiedUserName, user.Name)
                .Update();

            // 更新用户的板子绑定信息
            _db.UserTable
                .Where(u => u.ID == userId)
                .Set(u => u.BoardID, board.ID)
                .Set(u => u.BoardExpireTime, expireTime)
                .Update();

            board.Status = BoardStatus.Busy;
            board.OccupiedUserID = userId;
            board.OccupiedUserName = user.Name;

            logger.Info($"实验板 {board.BoardName} ({board.ID}) 已分配给用户 {user.Name} ({userId})，过期时间: {expireTime}");
            return new(board);
        }
    }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <param name="boardId">[TODO:parameter]</param>
    /// <param name="newName">[TODO:parameter]</param>
    /// <returns>[TODO:return]</returns>
    public int UpdateBoardName(Guid boardId, string newName)
    {
        if (string.IsNullOrWhiteSpace(newName) || newName.Contains('\'') || newName.Contains(';'))
        {
            logger.Error("实验板名称非法，包含不允许的字符");
            return 0;
        }
        var result = _db.BoardTable
            .Where(b => b.ID == boardId)
            .Set(b => b.BoardName, newName)
            .Update();
        logger.Info($"实验板名称已更新: {boardId} -> {newName}");
        return result;
    }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <param name="boardId">[TODO:parameter]</param>
    /// <param name="newStatus">[TODO:parameter]</param>
    /// <returns>[TODO:return]</returns>
    public int UpdateBoardStatus(Guid boardId, BoardStatus newStatus)
    {
        var result = _db.BoardTable
            .Where(b => b.ID == boardId)
            .Set(b => b.Status, newStatus)
            .Update();
        logger.Info($"TODO");
        return result;
    }

}
