using System.Numerics;
using Managing.ABI.GmxV2.SyntheticsReader.ContractDefinition;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Candles;
using Managing.Domain.MoneyManagements;
using Managing.Domain.Trades;
using Managing.Domain.Users;
using Managing.Infrastructure.Evm.Models.Gmx.v2;
using Managing.Infrastructure.Evm.Models.Proxy;
using Nethereum.Web3;
using static Managing.Common.Enums;

namespace Managing.Infrastructure.Evm.Services.Gmx;

internal static class GmxV2Mappers
{
    public static Trade Map(GmxV2Position position, Ticker ticker)
    {
        var entryPrice = GmxV2Helpers.GetEntryPrice(position.SizeInUsd, position.SizeInTokens,
            position.TokenData.Decimals);
        var parsedEntryPrice = GmxHelpers.FormatAmount(entryPrice, Constants.GMX.Config.Decimals.USD - 2, 4);

        var collateralLeverage = CalculateCollateralAndLeverage(position.SizeInUsd, position.CollateralAmount);
        var trade = new Trade(
            DateHelpers.GetFromUnixTimestamp((int)position.IncreasedAtTime),
            position.IsLong ? TradeDirection.Long : TradeDirection.Short,
            TradeStatus.Filled,
            TradeType.Limit,
            ticker,
            collateralLeverage.collateral / parsedEntryPrice,
            parsedEntryPrice,
            collateralLeverage.leverage,
            position.Key,
            ""
        );

        return trade;
    }

    public static List<Trade> Map(List<GmxV2Order> orders)
    {
        var trades = new List<Trade>();

        foreach (var order in orders)
        {
            var shortToken = TokenV2Service.GetTokenByAddress(order.InitialCollateralTokenAddress);
            var ticker = GmxV2Helpers.GetTicker(order.MarketAddress);
            var indexToken = TokenV2Service.GetByTicker(ticker);
            var sizeDelta = GmxHelpers.FormatAmount(order.SizeDeltaUsd, Constants.GMX.Config.Decimals.USD, 4);
            var triggerPrice = GmxV2Helpers.ParseContractPrice(order.ContractTriggerPrice, indexToken.Decimals, true);
            var quantity = sizeDelta / triggerPrice;
            var initialCollateral =
                GmxV2Helpers.ParseContractPrice(order.InitialCollateralDeltaAmount, shortToken.Decimals);
            var leverage = sizeDelta == 0 || initialCollateral == 0 ? 0 : sizeDelta / initialCollateral;

            var trade = new Trade(
                order.Date,
                order.IsLong ? TradeDirection.Long : TradeDirection.Short,
                TradeStatus.PendingOpen,
                GmxV2Helpers.GetTradeType(order.OrderType),
                ticker,
                Convert.ToDecimal(quantity),
                Convert.ToDecimal(triggerPrice),
                leverage,
                "",
                ""
            );

            trades.Add(trade);
        }

        return trades;
    }
    //
    // public static GmxV2Position MapPosition(GmxV2Service.PositionInfoDTO positionInfo)
    // {
    //     return new GmxV2Position
    //     {
    //         PositionKey = GetPositionKeyV2(
    //             positionInfo.Position.Account,
    //             positionInfo.Position.Market,
    //             positionInfo.Position.CollateralToken,
    //             positionInfo.Position.IsLong
    //         ),
    //         SizeInUsd = positionInfo.Position.SizeInUsd,
    //         CollateralAmount = positionInfo.Position.CollateralAmount,
    //         IncreasedAtTime = positionInfo.Position.IncreasedAtTime,
    //         IsLong = positionInfo.Position.IsLong,
    //         Leverage = CalculateLeverage(positionInfo.Position.SizeInUsd, positionInfo.Position.CollateralAmount)
    //     };
    // }

    private static (decimal collateral, decimal leverage) CalculateCollateralAndLeverage(BigInteger sizeInUsd,
        BigInteger collateralAmount)
    {
        if (collateralAmount == 0) return (0m, 0m);
        const int collateralDecimals = 6;
        var size = Web3.Convert.FromWei(sizeInUsd, 30);
        var collateral = Web3.Convert.FromWei(collateralAmount, collateralDecimals);
        return (collateral, collateral == 0 ? 0 : Math.Round(size / collateral));
    }

    public static List<MarketPrices> Map(List<ABI.GmxV2.Reader.ContractDefinition.MarketPrices> marketPrices)
    {
        return marketPrices.Select(mp => new MarketPrices
        {
            IndexTokenPrice = mp.IndexTokenPrice,
            LongTokenPrice = mp.LongTokenPrice,
            ShortTokenPrice = mp.ShortTokenPrice
        }).ToList();
    }

    public static Candle Map(List<double> marketPrices, Ticker ticker, Timeframe timeframe, int timeBetween)
    {
        return new Candle()
        {
            Date = DateHelpers.GetFromUnixTimestamp((int)marketPrices[0] + timeBetween).AddSeconds(-1),
            OpenTime = DateHelpers.GetFromUnixTimestamp((int)marketPrices[0]),
            Open = Convert.ToDecimal(marketPrices[1]),
            High = Convert.ToDecimal(marketPrices[2]),
            Low = Convert.ToDecimal(marketPrices[3]),
            Close = Convert.ToDecimal(marketPrices[4]),
            Exchange = TradingExchanges.Evm,
            Ticker = ticker.ToString(),
            Timeframe = timeframe
        };
    }

    public static List<Ticker> Map(GmxV2TokenList tokenList)
    {
        var tokens = new List<Ticker>();
        foreach (var t in tokenList.Tokens)
        {
            try
            {
                var ticker = MiscExtensions.ParseEnum<Ticker>(t.Symbol);
                tokens.Add(ticker);
            }
            catch (Exception e)
            {
                //Console.WriteLine($"Could not parse ticker for symbol {t.Symbol}: {e.Message}");
            }
        }

        return tokens;
    }

    /// <summary>
    /// Maps raw GMX positions fetched from the proxy/contract to domain Position objects.
    /// </summary>
    /// <param name="resultPositions">List of GmxPosition objects from the proxy.</param>
    /// <returns>List of domain Position objects.</returns>
    /// <remarks>
    /// Assumes GmxPosition contains necessary details like Account, Market, CollateralToken, IsLong, etc.
    /// Requires resolution of MoneyManagement dependency.
    /// </remarks>
    public static List<Position> Map(List<GmxPosition> resultPositions)
    {
        var positions = new List<Position>();
        foreach (var gmxPosition in resultPositions)
        {
            try
            {
                var direction = MiscExtensions.ParseEnum<TradeDirection>(gmxPosition.Direction);
                var ticker = MiscExtensions.ParseEnum<Ticker>(gmxPosition.Ticker);
                var position = new Position(Guid.NewGuid(), "",
                    direction,
                    ticker,
                    new MoneyManagement(),
                    PositionInitiator.User,
                    gmxPosition.Date,
                    new User());
                position.Open = new Trade(position.Date, direction, TradeStatus.Filled, TradeType.Market, ticker,
                    (decimal)gmxPosition.Quantity, (decimal)gmxPosition.Price, (decimal)gmxPosition.Leverage,
                    gmxPosition.Open.ExchangeOrderId, "");

                if (gmxPosition.TakeProfit1 != null)
                {
                    position.TakeProfit1 = Map(gmxPosition.TakeProfit1);
                }

                if (gmxPosition.StopLoss != null)
                {
                    position.StopLoss = Map(gmxPosition.StopLoss);
                }

                position.ProfitAndLoss = new ProfitAndLoss()
                {
                    Realized = (decimal)gmxPosition.Pnl
                };

                position.Status = MiscExtensions.ParseEnum<PositionStatus>(gmxPosition.Status);
                positions.Add(position);
            }
            catch (Exception ex)
            {
                Console.WriteLine(
                    $"Error mapping GMX position {gmxPosition?.ExchangeOrderId}: {ex.Message} \n StackTrace: {ex.StackTrace}");
            }
        }

        return positions;
    }

    private static Trade Map(GmxTrade gmxPosition)
    {
        return new Trade(gmxPosition.Date,
            MiscExtensions.ParseEnum<TradeDirection>(gmxPosition.Direction),
            MiscExtensions.ParseEnum<TradeStatus>(gmxPosition.Status),
            MiscExtensions.ParseEnum<TradeType>(gmxPosition.TradeType),
            MiscExtensions.ParseEnum<Ticker>(gmxPosition.Ticker),
            (decimal)gmxPosition.Quantity,
            (decimal)gmxPosition.Price,
            gmxPosition.Leverage,
            gmxPosition.ExchangeOrderId, "");
    }

    public static List<Trade> Map(List<GmxTrade> resultPositions)
    {
        var trades = new List<Trade>();
        foreach (var gmxPosition in resultPositions)
        {
            trades.Add(Map(gmxPosition));
        }

        return trades;
    }
}