namespace PlotLine.Services;

public interface IHardDeleteFileCleanupService
{
    HardDeleteFileCleanupResult DeleteFiles(string operationName, int ownerId, IReadOnlyList<string> storagePaths);
}

public sealed record HardDeleteFileCleanupResult(int Deleted, int Missing, int Failed);

public sealed class HardDeleteFileCleanupService(
    IUploadStorageService uploadStorage,
    ILogger<HardDeleteFileCleanupService> logger) : IHardDeleteFileCleanupService
{
    public HardDeleteFileCleanupResult DeleteFiles(string operationName, int ownerId, IReadOnlyList<string> storagePaths)
    {
        var deleted = 0;
        var missing = 0;
        var failed = 0;
        var deletedDirectories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        foreach (var storagePath in storagePaths)
        {
            var absolutePath = uploadStorage.TryResolvePublicPath(storagePath);
            if (absolutePath is null)
            {
                logger.LogWarning("Hard delete skipped a non-local upload path during {OperationName} for owner {OwnerID}.", operationName, ownerId);
                continue;
            }

            if (!File.Exists(absolutePath))
            {
                missing++;
                logger.LogInformation("Hard delete file cleanup found a missing file during {OperationName} for owner {OwnerID}.", operationName, ownerId);
                continue;
            }

            try
            {
                File.Delete(absolutePath);
                deleted++;
                logger.LogInformation("Hard delete removed an uploaded file during {OperationName} for owner {OwnerID}.", operationName, ownerId);

                var directory = Path.GetDirectoryName(absolutePath);
                if (!string.IsNullOrWhiteSpace(directory))
                {
                    deletedDirectories.Add(directory);
                }
            }
            catch (Exception ex)
            {
                failed++;
                logger.LogWarning(ex, "Hard delete could not remove an uploaded file during {OperationName} for owner {OwnerID}.", operationName, ownerId);
            }
        }

        foreach (var directory in deletedDirectories.OrderByDescending(x => x.Length))
        {
            TryDeleteEmptyUploadDirectory(operationName, ownerId, directory);
        }

        return new HardDeleteFileCleanupResult(deleted, missing, failed);
    }

    private void TryDeleteEmptyUploadDirectory(string operationName, int ownerId, string directory)
    {
        var uploadsRoot = Path.EndsInDirectorySeparator(uploadStorage.UploadsRootPath)
            ? uploadStorage.UploadsRootPath
            : uploadStorage.UploadsRootPath + Path.DirectorySeparatorChar;
        var comparison = OperatingSystem.IsWindows()
            ? StringComparison.OrdinalIgnoreCase
            : StringComparison.Ordinal;
        var fullDirectory = Path.GetFullPath(directory);
        if (!fullDirectory.StartsWith(uploadsRoot, comparison)
            || string.Equals(fullDirectory, uploadStorage.UploadsRootPath, comparison))
        {
            return;
        }

        try
        {
            if (Directory.Exists(fullDirectory) && !Directory.EnumerateFileSystemEntries(fullDirectory).Any())
            {
                Directory.Delete(fullDirectory);
                logger.LogInformation("Hard delete removed an empty upload directory during {OperationName} for owner {OwnerID}.", operationName, ownerId);
            }
        }
        catch (Exception ex)
        {
            logger.LogWarning(ex, "Hard delete could not remove an empty upload directory during {OperationName} for owner {OwnerID}.", operationName, ownerId);
        }
    }
}
