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

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

        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="executor"></param>
        public KnokDao(ILogger<KnokDao> logger, IProcExecutor executor)
        {
            _executor = executor;
            _logger = logger;
        }

        public async Task<Knok> CreateKnok(CreateKnokArgs create, decimal currentPrice)
        {
            _logger.LogDebug("CreateKnok : {0}", create);
            var knok = create;
            var a = knok?.Technical;
            var teh = new { HasTechnical = knok.Technical != null, Technical_Header = a?.Header, Technical_Description = a?.Description, Technical_References = a?.References };//, TechnicalImageUrl = a?.Image?.Url, Technical_ImageContent = a?.Image?.Content, Technical_ImageContentType = a?.Image?.ContentType };
            a = knok?.Fundamental;
            var func = new { HasFundamental = knok.Fundamental != null, Fundamental_Header = a?.Header, Fundamental_Description = a?.Description, Fundamental_References = a?.References };//, Fundamental_ImageUrl = a?.Image?.Url, Fundamental_ImageContent = a?.Image?.Content, Fundamental_ImageContentType = a?.Image?.ContentType };
            var row = (await _executor.Go("BO_CreateKnok", create/*, knok*/, teh, func, new { CurrentPrice = currentPrice })).Тables[0][0];
            return ParseFullKnok(row);
        }

        public async Task<Knok> GetKnokDetails(long knokId, int knokFeedExpirationHours) {
            var row = (await _executor.Go("BO_GetKnoksFull", new { KnokId = knokId, KnokFeedExpirationHours = knokFeedExpirationHours})).Тables[0][0];
            return ParseFullKnok(row);

        }

        public async Task<KnokFullData> GetKnokFullDetails(long knokId, long userId, int knokFeedExpirationHours, List<IDurationCRTAggrItem> durationCRTAggrItems)
        {
            var fullKnok = (await _executor.Go("BO_GetKnoksFull", new { KnokId = knokId, UserId = userId, KnokFeedExpirationHours = knokFeedExpirationHours, RangeSettings = durationCRTAggrItems.ToDataTable() }));
            var row = fullKnok.Тables[0][0];
            var chartData = fullKnok.Тables[1].Rows
                    .Select(crt =>
                    {
                        return crt.ToMapped<ChartItem>();
                    }).ToArray();

            var knok =  ParseFullKnok(row);
            var config = durationCRTAggrItems.FirstOrDefault(x => x.MaxRange >= knok.Duration && x.MinRange <= knok.Duration);
            int ratesGranularityHours = 2;
            if (config != null)
            {
                if (config.Period.ToUpper().Contains("H"))
                {
                    short.TryParse(config.Period.ToUpper().Replace("H", string.Empty), out var period);
                    ratesGranularityHours = period;
                }
                else if (config.Period.ToUpper().Contains("D"))
                {
                    short.TryParse(config.Period.ToUpper().Replace("D", string.Empty), out var period);
                    ratesGranularityHours = period * 24;
                }
            }
            knok.RatesGranularityHours = (short)(ratesGranularityHours == 0 ? 1 : ratesGranularityHours);
            var result = await _executor.Go("API_GetUserAccounts", new { UserId = knok.CreatorUserId });

            var user = result.Тables[0][0].To<User>();
            var stat = (await _executor.Go("BO_GetKnoksFull", new { KnokId = knokId, RangeSettings = durationCRTAggrItems.ToDataTable() }));
            var st = stat.Тables[0][0].To<FeedStatus>();
            return new KnokFullData
            {
                Knok = knok,
                Knoker = user,
                Status = st,
                ChartData = chartData
            };
        }

        private Knok ParseFullKnok(Row row)
        {
            var knok = row.To<Knok>();
            // knok.CurrentProfit = knok.PrevPrice.HasValue && knok.CurrentPrice.HasValue ? knok.CurrentPrice.Value - knok.PrevPrice.Value : 0m;
            knok.CurrentProfitPercent = knok.PrevPrice.HasValue && knok.CurrentPrice.HasValue ? Math.Round(knok.CurrentPrice.Value / (knok.PrevPrice.Value / 100) - 100, 1) : 0m;
            if (knok.HasTechnical)
            {
                knok.Technical = row.ToMapped<KnokAnalysis>(p => "Technical_" + p);
                knok.Technical.Image = row.ToMapped<KnokImage>(p => "Technical_Image_" + p);
            }
            if (knok.HasFundamental)
            {
                knok.Fundamental = row.ToMapped<KnokAnalysis>(p => "Fundamental_" + p);
            }
            return knok;
        }

        public async Task<KnokImage> CreateImage(long knokId, KnokImage image) {
            await _executor.Go("BO_SetKnokImage", new
            {
                KnokId = knokId,
                image.Url,
                image.FileName
            });
            return image;
        }

        public Task<KnokImage> LoadImage(long knokId, Knok knok) {
            var image = knok?.Technical?.Image;
            return Task.FromResult(image);
        }

        public async Task<IEnumerable<KnokFeed>> GetPublicKnoks(KnokFeedArgs args, int knokFeedExpirationHours)
        {
            var result = await _executor.Go("BO_GetPublicKnoks", args, new { KnokFeedExpirationHours = knokFeedExpirationHours });
            return result.Тables[0].Rows.Select(row => new KnokFeed() { Knok = row.ToMapped<FeedKnok>(), Knoker = row.ToMapped<FeedUser>(), Status = row.ToMapped<FeedStatus>() });
        }

        public async Task<IEnumerable<KnokOngoing>> GetAliveKnoks(KnokOngoingArgs args, List<IDurationCRTAggrItem> durationCRTAggrItems)
        {
            var result = await _executor.Go("BO_GetAliveKnoks", args, new { RangeSettings = durationCRTAggrItems.ToDataTable() });
            return result.Тables[0].Rows.Select(row => {
                var knok = row.ToMapped<OngoingKnok>();
                //knok.CurrentPricePercent = knok.PrevPrice.HasValue && knok.CurrentPrice.HasValue ? 
                //    Math.Round((knok.CurrentPrice.Value - knok.PrevPrice.Value) / knok.PrevPrice.Value, 1) : 0m;
                //knok.CurrentProfitPercent = 100 * knok.CurrentProfit;
                //knok.PotentialProfitPercent = 100 * knok.PotentialProfitValue;
                return new KnokOngoing()
                {
                    Knok = knok,
                    Knokser = row.ToMapped<Knokser>(),
                    Status = row.ToMapped<OngoingStatus>(),
                    ChartData = result.Тables[1].Rows
                    .Select(crt => {
                        return crt.ToMapped<ChartItem>();
                    }).Where(x => knok.KnokId == x.KnokId).OrderBy(or => or.X).ToArray()
                };
            });
        }

        public async Task<IEnumerable<KnokFeed>> GetTickersAliveKnoks(KnokFeedArgs args)
        {
            var result = await _executor.Go("BO_GetAliveKnoks", args);
            return result.Тables[0].Rows.Select(row => new KnokFeed() { Knok = row.ToMapped<FeedKnok>(), Knoker = row.ToMapped<FeedUser>(), Status = row.ToMapped<FeedStatus>() });
        }

        public async Task<float> GetKnokPrice(long userId)
        {
            var pW = await _executor.Go("GetProbabilityOfWinning", new { UserId = userId });
            var wP = await _executor.Go("GetWinPayoff", new { UserId = userId });
            var lP = await _executor.Go("GetLossPayoff", new { UserId = userId });
            var result = pW.ReturnValue.Value * wP.ReturnValue.Value + (1 - pW.ReturnValue.Value) * lP.ReturnValue.Value;
            //return result;
            return 0;
        }

        public async Task<KnokOwnData> GetOwnData(long userId)
        {
            var result = await _executor.Go("BO_GetPublicKnoks_OwnData", new { UserId = userId });
            var data = result.Тables[0][0];
            return new KnokOwnData() { Knoker = data.ToMapped<FeedUser>(), Status = data.ToMapped<FeedStatus>() };
        }
        
        public async Task<Knok> PublishKnok(long knokId, long userId, long accountId, decimal publishPrice, decimal knokRating)
        {
            var row = (await _executor.Go("BO_PublishKnok", new
            {
                knokId,
                userId,
                accountId,
                publishPrice,
                knokRating

            })).Тables[2][0];
            return ParseFullKnok(row);
        }

        public Task PurchaseKnok(long knokId, long userId, long accountId)
        {
            return _executor.Go("BO_AddUserKnok", new { KnokId = knokId, UserId =userId, AccountId = accountId });
        }

        public async Task<KnokserProfile> GetKnokserProfile(long knokserId)
        {
            var result = (await _executor.Go("BO_GetKnokserProfile", new { KnokserId = knokserId }));
            return result.Тables[0][0].ToMapped<KnokserProfile>();
        }

        public async Task<List<Knok>> GetKnoks(bool? activeOnly = null, long? greaterThanSignalId = null)
        {
            var result = (await _executor.Go("KST_GetKnoks", new { ActiveOnly = activeOnly, GreaterThanSignalId = greaterThanSignalId }));
            return result.Тables[0].Rows.Select(row => row.ToMapped<Knok>()).ToList();

        }

        public async Task<Knok> UpdateKnok(IDictionary<string, object> changes, decimal knokerWinRate, decimal userRefundRate)
        {
            changes.Add("KnokerWinRate", knokerWinRate);
            changes.Add("UserRefundRate", userRefundRate);
            var result = await _executor.Go("KST_UpdateKnok", changes);
            return result.Тables[0][0].To<Knok>();
        }

        public async Task<IEnumerable<KnokEnded>> GetEndedKnoks(KnokEndedArgs args, List<IDurationCRTAggrItem> durationCRTAggrItems)
        {
            var result = await _executor.Go("BO_GetEndedKnoks", args, new { RangeSettings = durationCRTAggrItems.ToDataTable() });
            return result.Тables[0].Rows.Select(row => {
                var knok = row.ToMapped<EndedKnok>();
                return new KnokEnded()
                {
                    Knok = knok,
                    Knokser = row.ToMapped<Knokser>(),
                    Status = row.ToMapped<EndedStatus>(),
                    ChartData = result.Тables[1].Rows
                    .Select(crt => {
                        return crt.ToMapped<ChartItem>();
                    }).Where(x => knok.KnokId == x.KnokId).OrderBy(or => or.X).ToArray()
                };
            });
        }

        public async Task<IEnumerable<KnokOngoing>> GetCurrentlyPublishedKnoks(KnokCurrentlyPublishedArgs args, long userId, int knokFeedExpirationHours, List<IDurationCRTAggrItem> durationCRTAggrItems)
        {
            var result = await _executor.Go("BO_GetCurrentlyPublishedKnoks", args, new { KnokFeedExpirationHours = knokFeedExpirationHours, RangeSettings = durationCRTAggrItems.ToDataTable() });
            return result.Тables[0].Rows.Select(row => {
                var knok = row.ToMapped<OngoingKnok>();
                return new KnokOngoing()
                {
                    Knok = knok,
                    Knokser = row.ToMapped<Knokser>(),
                    Status = row.ToMapped<OngoingStatus>(),
                    ChartData = result.Тables[1].Rows
                    .Select(crt => {
                        return crt.ToMapped<ChartItem>();
                    }).Where(x => knok.KnokId == x.KnokId).OrderBy(or => or.X).ToArray()
                };
            });
        }

        public async Task<IEnumerable<KnokEnded>> GetPastPublishedKnoks(KnokPastPublishedArgs args, long userId, List<IDurationCRTAggrItem> durationCRTAggrItems)
        {
            var result = await _executor.Go("BO_GetPastPublishedKnoks", args, new { RangeSettings = durationCRTAggrItems.ToDataTable() });
            return result.Тables[0].Rows.Select(row => {
                var knok = row.ToMapped<EndedKnok>();
                return new KnokEnded()
                {
                    Knok = knok,
                    Knokser = row.ToMapped<Knokser>(),
                    Status = row.ToMapped<EndedStatus>(),
                    ChartData = result.Тables[1].Rows
                    .Select(crt => {
                        return crt.ToMapped<ChartItem>();
                    }).Where(x => knok.KnokId == x.KnokId).OrderBy(or => or.X).ToArray()
                };
            });
        }
    }
}
