#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;

namespace EllieBot.Modules.Administration.Services;

public class AutoPublishService : IExecNoCommand, IReadyExecutor, IEService
{
    private readonly DbService _db;
    private readonly DiscordSocketClient _client;
    private readonly IBotCredsProvider _creds;
    private ConcurrentDictionary<ulong, ulong> _enabled;

    public AutoPublishService(DbService db, DiscordSocketClient client, IBotCredsProvider creds)
    {
        _db = db;
        _client = client;
        _creds = creds;
    } 
    
    public async Task ExecOnNoCommandAsync(IGuild guild, IUserMessage msg)
    {
        if (guild is null)
            return;
        
        if (msg.Channel.GetChannelType() != ChannelType.News)
            return;

        if (!_enabled.TryGetValue(guild.Id, out var cid) || cid != msg.Channel.Id)
            return;
        
        await msg.CrosspostAsync(new RequestOptions()
        {
            RetryMode = RetryMode.AlwaysFail
        });
    }

    public async Task OnReadyAsync()
    {
        var creds = _creds.GetCreds();
        
        await using var ctx = _db.GetDbContext();
        var items = await ctx.GetTable<AutoPublishChannel>()
                             .Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, creds.TotalShards, _client.ShardId))
                             .ToListAsyncLinqToDB();

        _enabled = items
                   .ToDictionary(x => x.GuildId, x => x.ChannelId)
                   .ToConcurrent();

        _client.LeftGuild += ClientOnLeftGuild;
    }

    public async Task ClientOnLeftGuild(SocketGuild guild)
    {
        await using var ctx = _db.GetDbContext();
        _enabled.TryRemove(guild.Id, out _);

        await ctx.GetTable<AutoPublishChannel>()
                 .Where(x => x.GuildId == guild.Id)
                 .DeleteAsync();
    }

    public async Task<bool> ToggleAutoPublish(ulong guildId, ulong channelId)
    {
        await using var ctx = _db.GetDbContext();
        var deleted = await ctx.GetTable<AutoPublishChannel>()
                               .DeleteAsync(x => x.GuildId == guildId && x.ChannelId == channelId);

        if (deleted != 0)
        {
            _enabled.TryRemove(guildId, out _);
            return false;
        }

        await ctx.GetTable<AutoPublishChannel>()
                 .InsertOrUpdateAsync(() => new()
                     {
                         GuildId = guildId,
                         ChannelId = channelId,
                         DateAdded = DateTime.UtcNow,
                     },
                     old => new()
                     {
                         ChannelId = channelId,
                         DateAdded = DateTime.UtcNow,
                     },
                     () => new()
                     {
                         GuildId = guildId
                     });

        _enabled[guildId] = channelId;

        return true;
    }
}