using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TeleWave.Application.Common;
using TeleWave.Application.Common.Interfaces;

namespace TeleWave.Infrastructure.Support;

/// <summary>
/// Складывает копии отладочных дампов в настроенный каталог и подрезает его до последних N архивов.
///
/// Ошибки записи только логируются: дамп к этому моменту уже собран и уходит в браузер, и валить
/// запрос из-за того, что папка недоступна, значит терять данные ровно тогда, когда они нужны.
/// </summary>
public sealed class DebugExportStore(
    IOptions<DebugExportOptions> options,
    ILogger<DebugExportStore> logger
) : IDebugExportStore
{
    private const string ArchivePattern = "*.zip";

    private readonly DebugExportOptions _options = options.Value;

    public async Task<string?> SaveAsync(
        string fileName,
        byte[] content,
        CancellationToken cancellationToken
    )
    {
        if (string.IsNullOrWhiteSpace(_options.ExportDirectory))
            return null;

        try
        {
            var directory = Path.GetFullPath(_options.ExportDirectory);
            Directory.CreateDirectory(directory);

            var path = Path.Combine(directory, Path.GetFileName(fileName));
            await File.WriteAllBytesAsync(path, content, cancellationToken);

            Prune(directory);
            return path;
        }
        catch (Exception exception) when (exception is IOException or UnauthorizedAccessException)
        {
            logger.LogWarning(
                exception,
                "Не удалось сохранить отладочный дамп в {Directory}",
                _options.ExportDirectory
            );
            return null;
        }
    }

    /// <summary>Оставляет последние <see cref="DebugExportOptions.KeepLast"/> архивов по времени записи.</summary>
    private void Prune(string directory)
    {
        var keep = Math.Max(1, _options.KeepLast);
        var stale = new DirectoryInfo(directory)
            .GetFiles(ArchivePattern)
            .OrderByDescending(file => file.LastWriteTimeUtc)
            .Skip(keep)
            .ToList();

        foreach (var file in stale)
        {
            try
            {
                file.Delete();
            }
            catch (Exception exception)
                when (exception is IOException or UnauthorizedAccessException)
            {
                // Файл держит открытым архиватор или антивирус — не повод срывать экспорт.
                logger.LogDebug(exception, "Не удалось удалить старый дамп {File}", file.FullName);
            }
        }
    }
}
