#nullable disable
using NadekoBot.Modules.Searches.Services;
using System.Globalization;

namespace NadekoBot.Modules.Searches;

public partial class Searches
{
    public partial class FinanceCommands : NadekoModule<CryptoService>
    {
        private readonly IStockDataService _stocksService;
        private readonly IStockChartDrawingService _stockDrawingService;

        public FinanceCommands(IStockDataService stocksService, IStockChartDrawingService stockDrawingService)
        {
            _stocksService = stocksService;
            _stockDrawingService = stockDrawingService;
        }

        [Cmd]
        public async Task Stock([Leftover] string query)
        {
            using var typing = ctx.Channel.EnterTypingState();

            var stock = await _stocksService.GetStockDataAsync(query);

            if (stock is null)
            {
                var symbols = await _stocksService.SearchSymbolAsync(query);

                if (symbols.Count == 0)
                {
                    await Response().Error(strs.not_found).SendAsync();
                    return;
                }

                var symbol = symbols.First();
                var promptEmbed = _sender.CreateEmbed()
                                         .WithDescription(symbol.Description)
                                         .WithTitle(GetText(strs.did_you_mean(symbol.Symbol)));

                if (!await PromptUserConfirmAsync(promptEmbed))
                    return;

                query = symbol.Symbol;
                stock = await _stocksService.GetStockDataAsync(query);

                if (stock is null)
                {
                    await Response().Error(strs.not_found).SendAsync();
                    return;
                }
            }

            var candles = await _stocksService.GetCandleDataAsync(query);
            var stockImageTask = _stockDrawingService.GenerateCombinedChartAsync(candles);

            var localCulture = (CultureInfo)Culture.Clone();
            localCulture.NumberFormat.CurrencySymbol = "$";

            var sign = stock.Price >= stock.Close
                ? "\\🔼"
                : "\\🔻";

            var change = (stock.Price - stock.Close).ToString("N2", Culture);
            var changePercent = (1 - (stock.Close / stock.Price)).ToString("P1", Culture);

            var price = stock.Price.ToString("C2", localCulture);

            var eb = _sender.CreateEmbed()
                            .WithOkColor()
                            .WithAuthor(stock.Symbol)
                            .WithUrl($"https://www.tradingview.com/chart/?symbol={stock.Symbol}")
                            .WithTitle(stock.Name)
                            .AddField(GetText(strs.price), $"{sign} **{price}**", true)
                            .AddField(GetText(strs.market_cap), stock.MarketCap, true)
                            .AddField(GetText(strs.volume_24h), stock.DailyVolume.ToString("C0", localCulture), true)
                            .AddField("Change", $"{change} ({changePercent})", true)
                            // .AddField("Change 50d", $"{sign50}{change50}", true)
                            // .AddField("Change 200d", $"{sign200}{change200}", true)
                            .WithFooter(stock.Exchange);

            var message = await Response().Embed(eb).SendAsync();
            await using var imageData = await stockImageTask;
            if (imageData is null)
                return;

            var fileName = $"{query}-sparkline.{imageData.Extension}";
            using var attachment = new FileAttachment(
                imageData.FileData,
                fileName
            );
            await message.ModifyAsync(mp =>
            {
                mp.Attachments =
                    new(new[] { attachment });

                mp.Embed = eb.WithImageUrl($"attachment://{fileName}").Build();
            });
        }


        [Cmd]
        public async Task Crypto(string name)
        {
            name = name?.ToUpperInvariant();

            if (string.IsNullOrWhiteSpace(name))
                return;

            var (crypto, nearest) = await _service.GetCryptoData(name);

            if (nearest is not null)
            {
                var embed = _sender.CreateEmbed()
                                   .WithTitle(GetText(strs.crypto_not_found))
                                   .WithDescription(
                                       GetText(strs.did_you_mean(Format.Bold($"{nearest.Name} ({nearest.Symbol})"))));

                if (await PromptUserConfirmAsync(embed))
                    crypto = nearest;
            }

            if (crypto is null)
            {
                await Response().Error(strs.crypto_not_found).SendAsync();
                return;
            }

            var usd = crypto.Quote["USD"];

            var localCulture = (CultureInfo)Culture.Clone();
            localCulture.NumberFormat.CurrencySymbol = "$";

            var sevenDay = (usd.PercentChange7d / 100).ToString("P2", localCulture);
            var lastDay = (usd.PercentChange24h / 100).ToString("P2", localCulture);
            var price = usd.Price < 0.01
                ? usd.Price.ToString(localCulture)
                : usd.Price.ToString("C2", localCulture);

            var volume = usd.Volume24h.ToString("C0", localCulture);
            var marketCap = usd.MarketCap.ToString("C0", localCulture);
            var dominance = (usd.MarketCapDominance / 100).ToString("P2", localCulture);

            await using var sparkline = await _service.GetSparklineAsync(crypto.Id, usd.PercentChange7d >= 0);
            var fileName = $"{crypto.Slug}_7d.png";

            var toSend = _sender.CreateEmbed()
                                .WithOkColor()
                                .WithAuthor($"#{crypto.CmcRank}")
                                .WithTitle($"{crypto.Name} ({crypto.Symbol})")
                                .WithUrl($"https://coinmarketcap.com/currencies/{crypto.Slug}/")
                                .WithThumbnailUrl(
                                    $"https://s3.coinmarketcap.com/static/img/coins/128x128/{crypto.Id}.png")
                                .AddField(GetText(strs.market_cap), marketCap, true)
                                .AddField(GetText(strs.price), price, true)
                                .AddField(GetText(strs.volume_24h), volume, true)
                                .AddField(GetText(strs.change_7d_24h), $"{sevenDay} / {lastDay}", true)
                                .AddField(GetText(strs.market_cap_dominance), dominance, true)
                                .WithImageUrl($"attachment://{fileName}");

            if (crypto.CirculatingSupply is double cs)
            {
                var csStr = cs.ToString("N0", localCulture);

                if (crypto.MaxSupply is double ms)
                {
                    var perc = (cs / ms).ToString("P1", localCulture);

                    toSend.AddField(GetText(strs.circulating_supply), $"{csStr} ({perc})", true);
                }
                else
                {
                    toSend.AddField(GetText(strs.circulating_supply), csStr, true);
                }
            }


            await ctx.Channel.SendFileAsync(sparkline, fileName, embed: toSend.Build());
        }

        [Cmd]
        public async Task Coins(int page = 1)
        {
            if (--page < 0)
                return;

            if (page > 25)
                page = 25;

            await Response()
                  .Paginated()
                  .PageItems(async (page) =>
                  {
                      var coins = await _service.GetTopCoins(page);
                      return coins;
                  })
                  .PageSize(10)
                  .Page((items, _) =>
                  {
                      var embed = _sender.CreateEmbed()
                                         .WithOkColor();

                      if (items.Count > 0)
                      {
                          foreach (var coin in items)
                          {
                              embed.AddField($"#{coin.MarketCapRank} {coin.Symbol} - {coin.Name}",
                                  $"""
                                   `Price:` {GetArrowEmoji(coin.PercentChange24h)} {coin.CurrentPrice.ToShortString()}$ ({GetSign(coin.PercentChange24h)}{Math.Round(coin.PercentChange24h, 2)}%)
                                   `MarketCap:` {coin.MarketCap.ToShortString()}$
                                   `Supply:` {(coin.CirculatingSupply?.ToShortString() ?? "?")} / {(coin.TotalSupply?.ToShortString() ?? "?")}
                                   """,
                                  inline: false);
                          }
                      }

                      return embed;
                  })
                  .CurrentPage(page)
                  .AddFooter(false)
                  .SendAsync();
        }
        
        private static string GetArrowEmoji(decimal value)
            => value > 0 ? "▲" : "▼";

        private static string GetSign(decimal value)
            => value >= 0 ? "+" : "";
    }
}