// Copyright (C) 2021-present sam (starshines.gay)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.Diagnostics;
using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
using Dapper;
using Humanizer;
using Prometheus;

namespace Catalogger.Backend.Services;

public class MetricsCollectionService(
    ILogger logger,
    GuildCache guildCache,
    ChannelCache channelCache,
    RoleCache roleCache,
    UserCache userCache,
    EmojiCache emojiCache,
    IServiceProvider services
)
{
    private readonly ILogger _logger = logger.ForContext<MetricsCollectionService>();

    public async Task CollectMetricsAsync(CancellationToken ct = default)
    {
        var timer = CataloggerMetrics.MetricsCollectionTime.NewTimer();

        await using var scope = services.CreateAsyncScope();
        await using var conn = scope.ServiceProvider.GetRequiredService<DatabaseConnection>();

        var messageCount = await conn.ExecuteScalarAsync<int>("select count(id) from messages");

        CataloggerMetrics.GuildsCached.Set(guildCache.Size);
        CataloggerMetrics.ChannelsCached.Set(channelCache.Size);
        CataloggerMetrics.RolesCached.Set(roleCache.Size);
        CataloggerMetrics.UsersCached.Set(userCache.Size);
        CataloggerMetrics.EmojiCached.Set(emojiCache.Size);
        CataloggerMetrics.MessagesStored.Set(messageCount);
        CataloggerMetrics.MessageRateMinute = messageCount - CataloggerMetrics.MessageRateMinute;

        var process = Process.GetCurrentProcess();
        CataloggerMetrics.ProcessPhysicalMemory.Set(process.WorkingSet64);
        CataloggerMetrics.ProcessVirtualMemory.Set(process.VirtualMemorySize64);
        CataloggerMetrics.ProcessPrivateMemory.Set(process.PrivateMemorySize64);
        CataloggerMetrics.ProcessThreads.Set(process.Threads.Count);
        CataloggerMetrics.ProcessHandles.Set(process.HandleCount);

        _logger.Information("Collected metrics in {Duration}", timer.ObserveDuration());
    }
}

public class BackgroundMetricsCollectionService(
    ILogger logger,
    MetricsCollectionService innerService
) : BackgroundService
{
    private readonly ILogger _logger = logger.ForContext<BackgroundMetricsCollectionService>();

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        _logger.Information("Metrics are disabled, periodically collecting metrics manually");

        using var timer = new PeriodicTimer(1.Minutes());
        while (await timer.WaitForNextTickAsync(ct))
        {
            _logger.Debug("Collecting metrics");
            await innerService.CollectMetricsAsync(ct);
        }
    }
}
