#nullable disable
using Ellie.Common.ModuleBehaviors;
using Ellie.Db;
using Ellie.Modules.Games.Common;
using Ellie.Services.Database.Models;

namespace Ellie.Modules.Games.Services;

public class PollService : IExecOnMessage
{
    public ConcurrentDictionary<ulong, PollRunner> ActivePolls { get; } = new();

    public int Priority
        => 5;

    private readonly DbService _db;
    private readonly IBotStrings _strs;
    private readonly IEmbedBuilderService _eb;

    public PollService(DbService db, IBotStrings strs, IEmbedBuilderService eb)
    {
        _db = db;
        _strs = strs;
        _eb = eb;

        using var uow = db.GetDbContext();
        ActivePolls = uow.Set<Poll>().GetAllPolls()
            .ToDictionary(x => x.GuildId,
                x =>
                {
                    var pr = new PollRunner(db, x);
                    pr.OnVoted += Pr_OnVoted;
                    return pr;
                })
            .ToConcurrent();
    }

    public Poll CreatePoll(ulong guildId, ulong channelId, string input)
    {
        if (string.IsNullOrWhiteSpace(input) || !input.Contains(";"))
            return null;
        var data = input.Split(';');
        if (data.Length < 3)
            return null;

        var col = new IndexedCollection<PollAnswer>(data.Skip(1)
            .Select(x => new PollAnswer
            {
                Text = x
            }));

        return new()
        {
            Answers = col,
            Question = data[0],
            ChannelId = channelId,
            GuildId = guildId,
            Votes = new()
        };
    }

    public bool StartPoll(Poll p)
    {
        var pr = new PollRunner(_db, p);
        if (ActivePolls.TryAdd(p.GuildId, pr))
        {
            using (var uow = _db.GetDbContext())
            {
                uow.Set<Poll>().Add(p);
                uow.SaveChanges();
            }

            pr.OnVoted += Pr_OnVoted;
            return true;
        }

        return false;
    }

    public Poll StopPoll(ulong guildId)
    {
        if (ActivePolls.TryRemove(guildId, out var pr))
        {
            pr.OnVoted -= Pr_OnVoted;

            using var uow = _db.GetDbContext();
            uow.RemovePoll(pr.Poll.Id);
            uow.SaveChanges();

            return pr.Poll;
        }

        return null;
    }

    private async Task Pr_OnVoted(IUserMessage msg, IGuildUser usr)
    {
        var toDelete = await msg.Channel.SendConfirmAsync(_eb,
            _strs.GetText(strs.poll_voted(Format.Bold(usr.ToString())), usr.GuildId));
        toDelete.DeleteAfter(5);
        try
        {
            await msg.DeleteAsync();
        }
        catch
        {
        }
    }

    public async Task<bool> ExecOnMessageAsync(IGuild guild, IUserMessage msg)
    {
        if (guild is null)
            return false;

        if (!ActivePolls.TryGetValue(guild.Id, out var poll))
            return false;

        try
        {
            var voted = await poll.TryVote(msg);

            if (voted)
            {
                Log.Information("User {UserName} [{UserId}] voted in a poll on {GuildName} [{GuildId}] server",
                    msg.Author.ToString(),
                    msg.Author.Id,
                    guild.Name,
                    guild.Id);
            }

            return voted;
        }
        catch (Exception ex)
        {
            Log.Warning(ex, "Error voting");
        }

        return false;
    }
}