using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Knoks.Api.Nswag.Client;
using Knoks.Core.Data.Dao;
using Knoks.Core.Data.Interfaces;
using Knoks.Core.Logic.Interfaces;
using Knoks.Core.Logic.Managers;
using Knoks.Framework.DataAccess;
using Knoks.Framework.Extentions;
using Knoks.Framework.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Xunit;

namespace Knoks.Test.Integrations.AccountTransactions
{
    public class AccountTransactionTests
    {
        private static ILoggerFactory _loggerFactory = new LoggerFactory();
        private static ProcExecutor _procExecutor;
        private static IConfigurationRoot _configuration;
        private static IAccountTransactionManager _accountTransactionManager;
        private static IUserManager _userManager;
        private static IUserDao _userDao;
        private static IAccountDao _accountDao;
        private static IKnokDao _knokDao;
        private static IKnokManager _knokManager;

        public AccountTransactionTests()
        {
            _configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .Build();

            var loggingSection = _configuration.GetSection("Logging");
            _loggerFactory.AddConsole(loggingSection);
            _loggerFactory.AddDebug();

            _procExecutor = new ProcExecutor(_configuration.Load<Dictionary<string, string>>("DbConnections"), _loggerFactory.CreateLogger<ProcExecutor>());

            _userDao = new UserDao(_loggerFactory.CreateLogger<UserDao>(), _procExecutor);
            _accountDao = new AccountDao(_loggerFactory.CreateLogger<AccountDao>(), _procExecutor);
            _knokDao = new KnokDao(_loggerFactory.CreateLogger<KnokDao>(), _procExecutor);

            _userManager = new UserManager(_loggerFactory.CreateLogger<UserManager>(), _userDao, null, null, null, null, _accountDao, null, _configuration.Load<AccountTransactionsSettings>());
            _userManager.Initialize().GetAwaiter().GetResult();

            _accountTransactionManager = new AccountTransactionManager(_userManager,
                new AccountTransactionDao(_loggerFactory.CreateLogger<AccountTransactionDao>(), _procExecutor));

            _knokManager = new KnokManager(null, _configuration.Load<KnokSettings>(), _knokDao, null, null, null, null);
        }

        [Fact(DisplayName = "KnokerWinsKnokTest"/*, Skip = "Manual"*/)]
        public async Task KnokerWinsKnokTest()
        {
            //1. Publish knok
            decimal publishKnokFee = 30;
            var platformUser = await _accountDao.GetUserAccounts(6);
            var knokerUser = await _accountDao.GetUserAccounts(1);
            var knok = (await _knokDao.GetKnoks()).First();

            await _knokManager.PublishKnok(knok.KnokId.Value, knokerUser.User, knokerUser.Accounts.First(), publishKnokFee, null);
            //await _accountTransactionManager.ExecutePublishKnokTransaction(knokerUser.User, knokerUser.Accounts.First(),
            //    knok.KnokId.Value, publishKnokFee);

            var knokerUser2 = await _accountDao.GetUserAccounts(1);
            var platformUser2 = await _accountDao.GetUserAccounts(6);

            Assert.Equal(knokerUser.Accounts.First().CurrentBalance - publishKnokFee, knokerUser2.Accounts.First().CurrentBalance);
            Assert.Equal(platformUser.Accounts.First().CurrentBalance + publishKnokFee, platformUser2.Accounts.First().CurrentBalance);

            //2. Follow knok
            var followerUser = await _accountDao.GetUserAccounts(2);

            await _knokManager.PurchaseKnok(knok.KnokId.Value, followerUser.User, followerUser.Accounts.First());
            //await _accountTransactionManager.ExecuteUserBuyKnokTransaction(followerUser.User, followerUser.Accounts.First(),
            //    knok.KnokId.Value, knok.TokenPrice.Value);

            var followerUser2 = await _accountDao.GetUserAccounts(2);
            var platformUser22 = await _accountDao.GetUserAccounts(6);
            Assert.Equal(followerUser.Accounts.First().CurrentBalance - knok.Price.Value, followerUser2.Accounts.First().CurrentBalance);
            Assert.Equal(platformUser2.Accounts.First().CurrentBalance + knok.Price.Value, platformUser22.Accounts.First().CurrentBalance);


            //3. Complete knok succesfully
            Dictionary<string, object> knokChanges = new Dictionary<string, object>();         
            knokChanges.Add(nameof(knok.KnokId), knok.KnokId);
            knokChanges.Add(nameof(knok.KnokStatusId), 5); //Resolved
            knokChanges.Add(nameof(knok.EntryPriceTouched), true);
            knokChanges.Add(nameof(knok.ExitPriceTouched), true);
            decimal knokerWinRate = 0.5m;
            decimal userRefuldRate = 0.9m;

           await _knokDao.UpdateKnok(knokChanges, knokerWinRate, userRefuldRate);
            //knoker gets X% of the Sum of Purchased Total Amount + Get Back His Publishing Payment
            var knokerUser31 = await _accountDao.GetUserAccounts(1);
            var platformUser31 = await _accountDao.GetUserAccounts(6);

            decimal winAmount = knok.Price.Value * knokerWinRate + publishKnokFee;

            Assert.Equal(knokerUser2.Accounts.First().CurrentBalance + winAmount, knokerUser31.Accounts.First().CurrentBalance);
            Assert.Equal(platformUser22.Accounts.First().CurrentBalance - winAmount, platformUser31.Accounts.First().CurrentBalance);
        }

        [Fact(DisplayName = "UserRefundFailedKnok"/*, Skip = "Manual"*/)]
        public async Task UserRefundFailedKnok()
        {
            //1. Publish knok
            decimal publishKnokFee = 30;
            var platformUser = await _accountDao.GetUserAccounts(6);
            var knokerUser = await _accountDao.GetUserAccounts(1);
            var knok = (await _knokDao.GetKnoks()).First();

            await _knokManager.PublishKnok(knok.KnokId.Value, knokerUser.User, knokerUser.Accounts.First(), publishKnokFee, null);
            //await _accountTransactionManager.ExecutePublishKnokTransaction(knokerUser.User, knokerUser.Accounts.First(),
            //    knok.KnokId.Value, publishKnokFee);

            var knokerUser2 = await _accountDao.GetUserAccounts(1);
            var platformUser2 = await _accountDao.GetUserAccounts(6);

            Assert.Equal(knokerUser.Accounts.First().CurrentBalance - publishKnokFee, knokerUser2.Accounts.First().CurrentBalance);
            Assert.Equal(platformUser.Accounts.First().CurrentBalance + publishKnokFee, platformUser2.Accounts.First().CurrentBalance);

            //2. Follow knok
            var followerUser = await _accountDao.GetUserAccounts(2);

            await _knokManager.PurchaseKnok(knok.KnokId.Value, followerUser.User, followerUser.Accounts.First());
            //await _accountTransactionManager.ExecuteUserBuyKnokTransaction(followerUser.User, followerUser.Accounts.First(),
            //    knok.KnokId.Value, knok.TokenPrice.Value); - moved to PurchaseKnok stored procedure

            var followerUser2 = await _accountDao.GetUserAccounts(2);
            var platformUser22 = await _accountDao.GetUserAccounts(6);
            Assert.Equal(followerUser.Accounts.First().CurrentBalance - knok.Price.Value, followerUser2.Accounts.First().CurrentBalance);
            Assert.Equal(platformUser2.Accounts.First().CurrentBalance + knok.Price.Value, platformUser22.Accounts.First().CurrentBalance);


            //3. Complete knok with fail
            Dictionary<string, object> knokChanges = new Dictionary<string, object>();
            knokChanges.Add(nameof(knok.KnokId), knok.KnokId);
            knokChanges.Add(nameof(knok.KnokStatusId), 5); //Resolved

            decimal knokerWinRate = 0.5m;
            decimal userRefuldRate = 0.9m;

            await _knokDao.UpdateKnok(knokChanges, knokerWinRate, userRefuldRate);
            //knoker gets X% of the Sum of Purchased Total Amount + Get Back His Publishing Payment
            var knokerUser31 = await _accountDao.GetUserAccounts(1);
            var platformUser31 = await _accountDao.GetUserAccounts(6);
            var followerUser31 = await _accountDao.GetUserAccounts(2);

            decimal refundAmount = knok.Price.Value * userRefuldRate;

            Assert.Equal(knokerUser2.Accounts.First().CurrentBalance, knokerUser31.Accounts.First().CurrentBalance);
            Assert.Equal(followerUser2.Accounts.First().CurrentBalance + refundAmount, followerUser31.Accounts.First().CurrentBalance);
            Assert.Equal(platformUser22.Accounts.First().CurrentBalance - refundAmount, platformUser31.Accounts.First().CurrentBalance);
        }

        [Fact(DisplayName = "ExternalTransferTest" /*, Skip = "Manual"*/)]
        public async Task ExternalTransferTest()
        {
            decimal amount = 100;
            var platformUser = await _accountDao.GetUserAccounts(6);
            var knokerUser = await _accountDao.GetUserAccounts(1);


            await _accountTransactionManager.Transfer(platformUser.User, platformUser.Accounts.First(), knokerUser.User,
                knokerUser.Accounts.First(), amount, null, "1234", 544);

            var knokerUser2 = await _accountDao.GetUserAccounts(1);
            var platformUser2 = await _accountDao.GetUserAccounts(6);

            Assert.Equal(knokerUser.Accounts.First().CurrentBalance + amount,
                knokerUser2.Accounts.First().CurrentBalance);
            Assert.Equal(platformUser.Accounts.First().CurrentBalance - amount,
                platformUser2.Accounts.First().CurrentBalance);
        }
    }
}