using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.MediaEncoding;
/// 
/// Transcoding segment cleaner.
/// 
public class TranscodingSegmentCleaner : IDisposable
{
    private readonly TranscodingJob _job;
    private readonly ILogger _logger;
    private readonly IConfigurationManager _config;
    private readonly IFileSystem _fileSystem;
    private readonly IMediaEncoder _mediaEncoder;
    private Timer? _timer;
    private int _segmentLength;
    /// 
    /// Initializes a new instance of the  class.
    /// 
    /// Transcoding job dto.
    /// Instance of the  interface.
    /// Instance of the  interface.
    /// Instance of the  interface.
    /// Instance of the  interface.
    /// The segment length of this transcoding job.
    public TranscodingSegmentCleaner(TranscodingJob job, ILogger logger, IConfigurationManager config, IFileSystem fileSystem, IMediaEncoder mediaEncoder, int segmentLength)
    {
        _job = job;
        _logger = logger;
        _config = config;
        _fileSystem = fileSystem;
        _mediaEncoder = mediaEncoder;
        _segmentLength = segmentLength;
    }
    /// 
    /// Start timer.
    /// 
    public void Start()
    {
        _timer = new Timer(TimerCallback, null, 20000, 20000);
    }
    /// 
    /// Stop cleaner.
    /// 
    public void Stop()
    {
        DisposeTimer();
    }
    /// 
    /// Dispose cleaner.
    /// 
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    /// 
    /// Dispose cleaner.
    /// 
    /// Disposing.
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            DisposeTimer();
        }
    }
    private EncodingOptions GetOptions()
    {
        return _config.GetEncodingOptions();
    }
    private async void TimerCallback(object? state)
    {
        if (_job.HasExited)
        {
            DisposeTimer();
            return;
        }
        var options = GetOptions();
        var enableSegmentDeletion = options.EnableSegmentDeletion;
        var segmentKeepSeconds = Math.Max(options.SegmentKeepSeconds, 20);
        if (enableSegmentDeletion)
        {
            var downloadPositionTicks = _job.DownloadPositionTicks ?? 0;
            var downloadPositionSeconds = Convert.ToInt64(TimeSpan.FromTicks(downloadPositionTicks).TotalSeconds);
            if (downloadPositionSeconds > 0 && segmentKeepSeconds > 0 && downloadPositionSeconds > segmentKeepSeconds)
            {
                var idxMaxToDelete = (downloadPositionSeconds - segmentKeepSeconds) / _segmentLength;
                if (idxMaxToDelete > 0)
                {
                    await DeleteSegmentFiles(_job, 0, idxMaxToDelete, 1500).ConfigureAwait(false);
                }
            }
        }
    }
    private async Task DeleteSegmentFiles(TranscodingJob job, long idxMin, long idxMax, int delayMs)
    {
        var path = job.Path ?? throw new ArgumentException("Path can't be null.");
        _logger.LogDebug("Deleting segment file(s) index {Min} to {Max} from {Path}", idxMin, idxMax, path);
        await Task.Delay(delayMs).ConfigureAwait(false);
        try
        {
            if (job.Type == TranscodingJobType.Hls)
            {
                DeleteHlsSegmentFiles(path, idxMin, idxMax);
            }
        }
        catch (Exception ex)
        {
            _logger.LogDebug(ex, "Error deleting segment file(s) {Path}", path);
        }
    }
    private void DeleteHlsSegmentFiles(string outputFilePath, long idxMin, long idxMax)
    {
        var directory = Path.GetDirectoryName(outputFilePath)
                        ?? throw new ArgumentException("Path can't be a root directory.", nameof(outputFilePath));
        var name = Path.GetFileNameWithoutExtension(outputFilePath);
        var filesToDelete = _fileSystem.GetFilePaths(directory)
            .Where(f => long.TryParse(Path.GetFileNameWithoutExtension(f).Replace(name, string.Empty, StringComparison.Ordinal), out var idx)
                        && (idx >= idxMin && idx <= idxMax));
        List? exs = null;
        foreach (var file in filesToDelete)
        {
            try
            {
                _logger.LogDebug("Deleting HLS segment file {0}", file);
                _fileSystem.DeleteFile(file);
            }
            catch (IOException ex)
            {
                (exs ??= new List()).Add(ex);
                _logger.LogDebug(ex, "Error deleting HLS segment file {Path}", file);
            }
        }
        if (exs is not null)
        {
            throw new AggregateException("Error deleting HLS segment files", exs);
        }
    }
    private void DisposeTimer()
    {
        if (_timer is not null)
        {
            _timer.Dispose();
            _timer = null;
        }
    }
}