<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Umbraco.Core.Scoping;

namespace Umbraco.Core.IO
{
    internal class ShadowWrapper : IFileSystem2
    {
        private readonly IScopeProviderInternal _scopeProvider;
        private readonly IFileSystem _innerFileSystem;
        private readonly string _shadowPath;
        private ShadowFileSystem _shadowFileSystem;
        private string _shadowDir;

        public ShadowWrapper(IFileSystem innerFileSystem, string shadowPath, IScopeProviderInternal scopeProvider)
        {
            _innerFileSystem = innerFileSystem;
            _shadowPath = shadowPath;
            _scopeProvider = scopeProvider;
        }

        internal void Shadow(Guid id)
        {
            // note: no thread-safety here, because ShadowFs is thread-safe due to the check
            // on ShadowFileSystemsScope.None - and if None is false then we should be running
            // in a single thread anyways

            var virt = "~/App_Data/TEMP/ShadowFs/" + id + "/" + _shadowPath;
            _shadowDir = IOHelper.MapPath(virt);
            Directory.CreateDirectory(_shadowDir);
            var tempfs = new PhysicalFileSystem(virt);
            _shadowFileSystem = new ShadowFileSystem(_innerFileSystem, tempfs);
        }

        internal void UnShadow(bool complete)
        {
            var shadowFileSystem = _shadowFileSystem;
            var dir = _shadowDir;
            _shadowFileSystem = null;
            _shadowDir = null;

            try
            {
                // this may throw an AggregateException if some of the changes could not be applied
                if (complete) shadowFileSystem.Complete();
            }
            finally
            {
                // in any case, cleanup
                try
                {
                    Directory.Delete(dir, true);

                    // shadowPath make be path/to/dir, remove each
                    var min = IOHelper.MapPath("~/App_Data/TEMP/ShadowFs").Length;
                    var pos = dir.LastIndexOf("/", StringComparison.OrdinalIgnoreCase);
                    while (pos > min)
                    {
                        dir = dir.Substring(0, pos);
                        if (Directory.EnumerateFileSystemEntries(dir).Any() == false)
                            Directory.Delete(dir, true);
                        else
                            break;
                        pos = dir.LastIndexOf("/", StringComparison.OrdinalIgnoreCase);
                    }
                }
                catch
                {
                    // ugly, isn't it? but if we cannot cleanup, bah, just leave it there
                }
            }
        }

        private IFileSystem FileSystem
        {
            get
            {
                var isScoped = _scopeProvider != null && _scopeProvider.AmbientScope != null && _scopeProvider.AmbientScope.ScopedFileSystems;

                // if the filesystem is created *after* shadowing starts, it won't be shadowing
                // better not ignore that situation and raised a meaningful (?) exception<commit_msg>Fix shadow wrapper cleanup<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Umbraco.Core.Scoping;

namespace Umbraco.Core.IO
{
    internal class ShadowWrapper : IFileSystem2
    {
        private readonly IScopeProviderInternal _scopeProvider;
        private readonly IFileSystem _innerFileSystem;
        private readonly string _shadowPath;
        private ShadowFileSystem _shadowFileSystem;
        private string _shadowDir;

        public ShadowWrapper(IFileSystem innerFileSystem, string shadowPath, IScopeProviderInternal scopeProvider)
        {
            _innerFileSystem = innerFileSystem;
            _shadowPath = shadowPath;
            _scopeProvider = scopeProvider;
        }

        internal void Shadow(Guid id)
        {
            // note: no thread-safety here, because ShadowFs is thread-safe due to the check
            // on ShadowFileSystemsScope.None - and if None is false then we should be running
            // in a single thread anyways

            var virt = "~/App_Data/TEMP/ShadowFs/" + id + "/" + _shadowPath;
            _shadowDir = IOHelper.MapPath(virt);
            Directory.CreateDirectory(_shadowDir);
            var tempfs = new PhysicalFileSystem(virt);
            _shadowFileSystem = new ShadowFileSystem(_innerFileSystem, tempfs);
        }

        internal void UnShadow(bool complete)
        {
            var shadowFileSystem = _shadowFileSystem;
            var dir = _shadowDir;
            _shadowFileSystem = null;
            _shadowDir = null;

            try
            {
                // this may throw an AggregateException if some of the changes could not be applied
                if (complete) shadowFileSystem.Complete();
            }
            finally
            {
                // in any case, cleanup
                try
                {
                    Directory.Delete(dir, true);

                    // shadowPath make be path/to/dir, remove each
                    dir = dir.Replace("/", "\\");
                    var min = IOHelper.MapPath("~/App_Data/TEMP/ShadowFs").Length;
                    var pos = dir.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase);
                    while (pos > min)
                    {
                        dir = dir.Substring(0, pos);
                        if (Directory.EnumerateFileSystemEntries(dir).Any() == false)
                            Directory.Delete(dir, true);
                        else
                            break;
                        pos = dir.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase);
                    }
                }
                catch
                {
                    // ugly, isn't it? but if we cannot cleanup, bah, just leave it there
                }
            }
        }

        private IFileSystem FileSystem
        {
            get
            {
                var isScoped = _scopeProvider != null && _scopeProvider.AmbientScope != null && _scopeProvider.AmbientScope.ScopedFileSystems;

                // if the filesystem is created *after* shadowing starts, it won't be shadowing
                // better not ignore that situation and raised a meaningful (?) exception
{'fraction_non_alphanumeric': 0.07030886233121217, 'fraction_numerical': 0.0006208288064566196, 'mean_word_length': 1.537007874015748, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1769796', 'n_tokens_mistral': 1561, 'n_tokens_neox': 1486, 'n_words': 430}