using Knoks.Core.Data.Interfaces;
using Knoks.Core.Entities;
using Knoks.Core.Entities.Args;
using Knoks.Framework.DataAccess;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Knoks.Core.Data.Dao
{
    public class UserDao : IUserDao
    {
        private readonly IProcExecutor _executor;
        private readonly ILogger<UserDao> _logger;

        public UserDao(ILogger<UserDao> logger, IProcExecutor executor)
        {
            _logger = logger;
            _executor = executor;
        }

        public async Task<bool> UserNameExists(string userName, long? userId) {
            var result = await _executor.Go("USR_UserNameExists", new { UserName = userName, UserId = userId });
            return (result.ReturnValue ?? 0) == 1;
        }

        public async Task<bool> UserEmailExists(string email)
        {
            var result = await _executor.Go("USR_UserEmailExists", new { Email = email });
            return (result.ReturnValue ?? 0) == 1;
        }

        public async Task<(User user, Account account)> CreateUser(CreateUserArgs args, string password, decimal freeCreditAmount)
        {
            var dbTables = await _executor.Go("USR_Users_RegisterNewUser", args, new { Password = password, NewUserFreeCreditAmount = freeCreditAmount });
            return (
             dbTables.Тables[2][0].To<User>(),
             dbTables.Тables[3][0].To<Account>()
            );
        }

        public async Task<User> UpdateUser(UpdateUserArgs args)
        {
            return (await _executor.Go("USR_UpdateUser", args)).Тables[0][0].To<User>();
        }

        public async Task<User> UpdateUserSucsess(long args)
        {
            return (await _executor.Go("UpdateUserSucsess", args)).Тables[0][0].To<User>();
        }


        public async Task<IEnumerable<User>> GetUsers(long? userId = null, byte? userTypeId = null)
        {
            _logger.LogDebug("GetUsers : {0}", userId);
            return (await _executor.Go("REG_GetUsers", new { userId, userTypeId })).Тables[0].Rows.Select(row => row.To<User>());
        }

        public async Task<User> GetUser(long userId)
        {
            var dbTables = await _executor.Go("BO_GetUser");
           var user = dbTables.Тables[0][0].To<User>();
            return user;
        }

        public async Task<User> GetUserByEmail(string email)
        {
            var dbTables = await _executor.Go("BO_GetUserByEmail", new { Email = email});
            var user = dbTables.Тables[0][0].To<User>();
            return user;
        }

        public async Task<UserCredential> GetUserCredential(int apiConsumerId, string email)
        {
            _logger.LogDebug($"GetUserCredential: {nameof(apiConsumerId)}='{apiConsumerId}', {nameof(email)}='{email}'");

            var dataResult = await _executor.Go("REG_GetUserCredential", new { apiConsumerId, email });
            return dataResult.Тables[0].Rows.Count != 1 ? null : new UserCredential
            {
                User = dataResult.Тables[0][0].To<User>(),
                Password = dataResult.OutputParams["password"].ToString()
            };
        }

        public async Task<UserAccounts> GetUserInfo(string accountWalletAddress)
        {
            _logger.LogDebug($"Get User Info for wallet address: {accountWalletAddress}");

            var result = await _executor.Go("API_GetUserInfo", new { accountWalletAddress });

            return new UserAccounts
            {
                Accounts = result.Тables[0].Rows.Select(r => r.To<Account>()),
                User = result.Тables.Count > 1 ? result.Тables[1][0].To<User>() : null
            };
        }

        public async Task<bool> CheckPasswordResetToken(string token)
        {
            var result = await _executor.Go("BO_CheckPasswordResetToken", new { Token = token });
            return result.ReturnValue == 1;
        }

        public async Task SendPasswordResetLink(string email, string token)
        {
            await _executor.Go("BO_SetPasswordResetToken", new { Email = email, Token = token });
        }

        public async Task<bool> PasswordReset(string token, string newPassword)
        {
            var result = await _executor.Go("BO_PasswordReset", new { Token = token, NewPassword = newPassword });
            return result.ReturnValue == 1;
        }
    }
}
