using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Database.Implementations;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Server.Implementations.ScheduledTasks
{
    /// <summary>
    /// Class PostgresPeriodicTuner.
    /// </summary>
    public class PostgresPeriodicTuner : IScheduledTask, IConfigurableScheduledTask
    {
        private readonly ILogger<PostgresPeriodicTuner> _logger;
        private readonly IDbContextFactory<JellyfinDbContext> _dbContextFactory;

        /// <summary>
        /// Initializes a new instance of the <see cref="PostgresPeriodicTuner"/> class.
        /// </summary>
        /// <param name="logger">The logger.</param>
        /// <param name="dbContextFactory">The database context factory.</param>
        public PostgresPeriodicTuner(ILogger<PostgresPeriodicTuner> logger, IDbContextFactory<JellyfinDbContext> dbContextFactory)
        {
            _logger = logger;
            _dbContextFactory = dbContextFactory;
        }

        /// <inheritdoc />
        public string Name => "PostgreSQL Periodic Tuner";

        /// <inheritdoc />
        public string Key => "PostgresPeriodicTuner";

        /// <inheritdoc />
        public string Description => "Periodically checks for dead tuples and runs VACUUM ANALYZE on tables that need it.";

        /// <inheritdoc />
        public string Category => "Database";

        /// <inheritdoc />
        public bool IsHidden => false;

        /// <inheritdoc />
        public bool IsEnabled => true;

        /// <inheritdoc />
        public bool IsLogged => true;

        /// <summary>
        /// Executes the task.
        /// </summary>
        /// <param name="progress">The progress.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
        {
            _logger.LogInformation("PostgreSQL Periodic Tuner task started.");

            try
            {
                await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);

                const string DeadTupleQuery = @"
                    SELECT
                        relname AS TableName,
                        n_dead_tup AS DeadTuples
                    FROM
                        pg_stat_user_tables
                    WHERE
                        n_dead_tup > 1000 -- Example threshold: more than 1000 dead tuples
                    ORDER BY
                        n_dead_tup DESC;";

                var tablesToVacuum = (await dbContext.Database.SqlQueryRaw<(string TableName, long DeadTuples)>(DeadTupleQuery).ToListAsync(cancellationToken).ConfigureAwait(false)).ToList();

                if (tablesToVacuum.Count == 0)
                {
                    _logger.LogInformation("No tables require vacuuming at this time.");
                    return;
                }

                progress.Report(10);

                double progressStep = 90.0 / tablesToVacuum.Count;
                double currentProgress = 10.0;

                foreach (var table in tablesToVacuum)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    _logger.LogInformation("Found {DeadTuples} dead tuples in table {TableName}. Running VACUUM ANALYZE.", table.DeadTuples, table.TableName);

                    try
                    {
                        await dbContext.Database.ExecuteSqlInterpolatedAsync($"VACUUM ANALYZE \"{table.TableName}\"", cancellationToken).ConfigureAwait(false);
                        _logger.LogInformation("Successfully vacuumed and analyzed table {TableName}.", table.TableName);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error running VACUUM ANALYZE on table {TableName}.", table.TableName);
                    }

                    currentProgress += progressStep;
                    progress.Report(currentProgress);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during PostgreSQL Periodic Tuner task.");
            }
            finally
            {
                _logger.LogInformation("PostgreSQL Periodic Tuner task finished.");
                progress.Report(100);
            }
        }

        /// <summary>
        /// Gets the default triggers.
        /// </summary>
        /// <returns>IEnumerable&lt;TaskTriggerInfo&gt;.</returns>
        public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
        {
            return new[]
            {
                new TaskTriggerInfo
                {
                    Type = TaskTriggerInfoType.IntervalTrigger,
                    IntervalTicks = TimeSpan.FromHours(24).Ticks
                }
            };
        }
    }
}
