
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using KfChatDotNetBot.Extensions;
using KfChatDotNetBot.Models;
using KfChatDotNetBot.Models.DbModels;
using KfChatDotNetBot.Services;
using KfChatDotNetBot.Settings;
using KfChatDotNetWsClient.Models.Events;
using RandN;
using RandN.Compat;

namespace KfChatDotNetBot.Commands.Kasino;

[KasinoCommand]
[WagerCommand]
public class CoinflipCommand : ICommand
{
    public List<Regex> Patterns => [
        new Regex("^coinflip$", RegexOptions.IgnoreCase),
        new Regex(@"^coinflip (?<amount>\d+) (?<choice>heads|tails)$", RegexOptions.IgnoreCase),
        new Regex(@"^coinflip (?<amount>\d+\.\d+) (?<choice>heads|tails)$", RegexOptions.IgnoreCase),
    ];

    public string? HelpText => "!coinflip <amount> <heads|tails>, flip a coin";
    public UserRight RequiredRight => UserRight.Loser;
    public TimeSpan Timeout => TimeSpan.FromSeconds(5);
    public RateLimitOptionsModel? RateLimitOptions => new()
    {
        MaxInvocations = 3,
        Window = TimeSpan.FromSeconds(15)
    };

    public bool WhisperCanInvoke => false;
    private static double _houseEdge = 0.015; // house edge hack?

    public async Task RunCommand(ChatBot botInstance, BotCommandMessageModel message, UserDbModel user, GroupCollection arguments,
        CancellationToken ctx)
    {
        var settings = await SettingsProvider.GetMultipleValuesAsync([
            BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay, BuiltIn.Keys.KasinoCoinflipCleanupDelay,
            BuiltIn.Keys.KasinoCoinflipEnabled
        ]);

        var coinflipEnabled = settings[BuiltIn.Keys.KasinoCoinflipEnabled].ToBoolean();
        if (!coinflipEnabled)
        {
            var gameDisabledCleanupDelay = TimeSpan.FromMilliseconds(settings[BuiltIn.Keys.KasinoGameDisabledMessageCleanupDelay].ToType<int>());
            await botInstance.SendChatMessageAsync(
                $"{user.FormatUsername()}, coinflip is currently disabled.",
                true, autoDeleteAfter: gameDisabledCleanupDelay);
            return;
        }
        
        if (message is { IsWhisper: false, MessageUuid: not null })
        {
            await botInstance.KfClient.DeleteMessageAsync(message.MessageUuid);
        }

        var cleanupDelay = TimeSpan.FromMilliseconds(settings[BuiltIn.Keys.KasinoCoinflipCleanupDelay].ToType<int>());

        if (!arguments.TryGetValue("amount", out var amount))
        {
            await botInstance.SendChatMessageAsync(
                $"{user.FormatUsername()}, not enough arguments. !coinflip <wager> <heads|tails>",
                true, autoDeleteAfter: cleanupDelay);
            RateLimitService.RemoveMostRecentEntry(user, this);
            return;
        }

        if (!arguments.TryGetValue("choice", out var choice))
        {
            await botInstance.SendChatMessageAsync(
                $"{user.FormatUsername()}, not enough arguments. !coinflip <wager> <heads|tails>",
                true, autoDeleteAfter: cleanupDelay);
            RateLimitService.RemoveMostRecentEntry(user, this);
            return;
        }

        var choiceStr = choice.Value.ToLowerInvariant();
        var wager = Convert.ToDecimal(amount.Value);
        if (wager <= 0)
        {
            await botInstance.SendChatMessageAsync(
                $"{user.FormatUsername()}, your wager must be greater than zero.",
                true, autoDeleteAfter: cleanupDelay);
            RateLimitService.RemoveMostRecentEntry(user, this);
            return;
        }

        var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);

        if (gambler == null)
            throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}");
        if (gambler.Balance < wager)
        {
            await botInstance.SendChatMessageAsync(
                $"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} isn't enough for this wager.",
                true, autoDeleteAfter: cleanupDelay);
            RateLimitService.RemoveMostRecentEntry(user, this);
            return;
        }
        var rolled = Money.GetRandomDouble(gambler);
        var colors =
            await SettingsProvider.GetMultipleValuesAsync([
                BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor
            ]);


        decimal newBalance;
        if (rolled > 0.5 + _houseEdge)
        {
            // won
            var coinflipAnimation = GetCoinFlipAnimationUrl(choiceStr);

            await botInstance.SendChatMessageAsync($"[IMG]{coinflipAnimation}[/IMG]", true, autoDeleteAfter: cleanupDelay);
            await Task.Delay(1500, ctx);

            var effect = wager;
            newBalance = await Money.NewWagerAsync(gambler.Id, wager, effect, WagerGame.CoinFlip, ct: ctx);
            await botInstance.SendChatMessageAsync(
                $"{user.FormatUsername()}, you [B][COLOR={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]WON![/COLOR][/B] " +
                $"You won {await effect.FormatKasinoCurrencyAsync()} and your balance is now {await newBalance.FormatKasinoCurrencyAsync()}",
                true, autoDeleteAfter: cleanupDelay);
            return;
        }

        // lost
        bool isJacky = rolled > 0.5; // would've won without house edge
        var coinflipAnimationURL = GetCoinFlipAnimationUrl("heads" == choiceStr ? "tails" : "heads", isJacky);

        await botInstance.SendChatMessageAsync($"[IMG]{coinflipAnimationURL}[/IMG]", true, autoDeleteAfter: cleanupDelay);
        await Task.Delay(1500, ctx);

        newBalance = await Money.NewWagerAsync(gambler.Id, wager, -wager, WagerGame.CoinFlip, ct: ctx);
        await botInstance.SendChatMessageAsync(
            $"{user.FormatUsername()}, you [B][COLOR={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]LOST![/COLOR][/B] " +
            $"Your balance is now {await newBalance.FormatKasinoCurrencyAsync()}",
            true, autoDeleteAfter: cleanupDelay);
    }

    private static string GetCoinFlipAnimationUrl(string choiceStr, bool isJacky = false)
    {
        var baseUrl = "https://i.ddos.lgbt/u";
        var unixTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
        if (isJacky)
        {
            return $"{baseUrl}/bossmancoin-{choiceStr}-jacky.webp?{unixTime}";
        }

        return $"{baseUrl}/bossmancoin-{choiceStr}.webp?{unixTime}";
    }
}
