using System.Text;
using System.Text.Json;
using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW.LegacyApplication.Tests;

public sealed class LegacyOperatorSettingsTests
{
    private static readonly Encoding Cp949 = CreateCp949Encoding();

    [Fact]
    public void DefaultPath_IsTheBoundedLocalApplicationConfigurationPath()
    {
        var expected = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "MBN_STOCK_WEBVIEW",
            "Config",
            "runtime-folders.local.json");

        Assert.Equal(expected, LegacyOperatorSettingsStore.DefaultPath);
        Assert.True(Path.IsPathFullyQualified(LegacyOperatorSettingsStore.DefaultPath));
    }

    [Fact]
    public void SceneValidation_AcceptsAnEmptyCutsDirectoryAsADirectRuntimePath()
    {
        using var directory = new TemporaryDirectory();
        var sceneDirectory = directory.CreateDirectory("Cuts");

        var result = LegacyOperatorFolderValidator.Validate(
            LegacyOperatorSettingsFolderKind.Scene,
            sceneDirectory + Path.DirectorySeparatorChar);

        Assert.True(result.IsValid);
        Assert.Equal(Path.GetFullPath(sceneDirectory), result.Folder!.CanonicalPath);
        Assert.Contains("Cuts", result.Folder.AbbreviatedPath);
        Assert.InRange(
            result.Folder.AbbreviatedPath.Length,
            1,
            LegacyOperatorFolderValidator.DefaultDisplayCharacters);
        Assert.False(result.Folder.DatabaseIniDetected);
        Assert.Equal(0, result.Folder.MissingOptionalExternalAssetCount);
        Assert.Null(result.WarningMessage);
    }

    [Fact]
    public void SceneValidation_DoesNotInspectPartialScenesOrVideoFiles()
    {
        using var directory = new TemporaryDirectory();
        var sceneDirectory = directory.CreateDirectory("partial-Cuts");
        File.WriteAllText(Path.Combine(sceneDirectory, "5001.t2s"), "baseline");
        var videoDirectory = Directory.CreateDirectory(
            Path.Combine(sceneDirectory, "Video")).FullName;
        File.WriteAllBytes(Path.Combine(videoDirectory, "partial.vrv"), []);

        var result = LegacyOperatorFolderValidator.Validate(
            LegacyOperatorSettingsFolderKind.Scene,
            sceneDirectory);

        Assert.True(result.IsValid);
        Assert.Equal(Path.GetFullPath(sceneDirectory), result.Folder!.CanonicalPath);
        Assert.Null(result.WarningMessage);
    }

    [Fact]
    public void SceneValidationContract_MatchesTheReachableCutRequirementManifest()
    {
        var manifestPath = Path.Combine(
            FindRepositoryRoot(),
            "scripts",
            "LegacyCutRequirements.json");
        using var document = JsonDocument.Parse(File.ReadAllBytes(manifestPath));
        var root = document.RootElement;
        var manifestScenes = root.GetProperty("activeAliases")
            .EnumerateArray()
            .Select(value => string.Concat(value.GetString(), ".t2s"))
            .ToArray();
        var manifestAssets = root.GetProperty("assets")
            .EnumerateArray()
            .Select(value => new
            {
                Kind = value.GetProperty("kind").GetString(),
                Path = value.GetProperty("path").GetString()!
            })
            .ToArray();

        Assert.Equal(
            manifestScenes.OrderBy(value => value, StringComparer.Ordinal),
            LegacyOperatorFolderValidator.RequiredSceneFileNames
                .OrderBy(value => value, StringComparer.Ordinal));
        Assert.Equal(
            manifestAssets.Where(value => value.Kind != "Video")
                .Select(value => value.Path)
                .OrderBy(value => value, StringComparer.Ordinal),
            LegacyOperatorFolderValidator.RequiredBuiltInAssetRelativePaths
                .OrderBy(value => value, StringComparer.Ordinal));
        Assert.Equal(
            manifestAssets.Where(value => value.Kind == "Video")
                .Select(value => value.Path)
                .OrderBy(value => value, StringComparer.Ordinal),
            LegacyOperatorFolderValidator.OptionalExternalAssetRelativePaths
                .OrderBy(value => value, StringComparer.Ordinal));
    }

    [Fact]
    public void SceneValidation_MissingDirectoryFailsWithoutLeakingThePath()
    {
        using var directory = new TemporaryDirectory();
        var sceneDirectory = Path.Combine(
            directory.Path,
            "sensitive-design-folder");

        var result = LegacyOperatorFolderValidator.Validate(
            LegacyOperatorSettingsFolderKind.Scene,
            sceneDirectory);

        Assert.False(result.IsValid);
        Assert.Equal(
            LegacyOperatorFolderValidationFailure.DirectoryNotFound,
            result.Failure);
        Assert.Null(result.Folder);
        AssertPathFree(result.WarningMessage, sceneDirectory);
    }

    [Fact]
    public void BackgroundValidation_AcceptsAnEmptyFixedDriveDirectory()
    {
        using var directory = new TemporaryDirectory();
        var backgroundDirectory = directory.CreateDirectory("Background");

        Assert.True(LegacyOperatorFolderValidator.TryValidate(
            LegacyOperatorSettingsFolderKind.Background,
            backgroundDirectory,
            out var folder,
            out var warning));
        Assert.Equal(Path.GetFullPath(backgroundDirectory), folder!.CanonicalPath);
        Assert.Null(warning);
    }

    [Fact]
    public void ResourceValidation_AcceptsAnEmptyResDirectoryAsADirectRuntimePath()
    {
        using var directory = new TemporaryDirectory();
        var resourceDirectory = directory.CreateDirectory("Res");

        var result = LegacyOperatorFolderValidator.Validate(
            LegacyOperatorSettingsFolderKind.Resource,
            resourceDirectory);

        Assert.True(result.IsValid, $"{result.Failure}: {result.WarningMessage}");
        Assert.Equal(Path.GetFullPath(resourceDirectory), result.Folder!.CanonicalPath);
        Assert.False(result.Folder.DatabaseIniDetected);
        Assert.Null(result.WarningMessage);
    }

    [Fact]
    public void ResourceValidation_DoesNotParseIniContents()
    {
        using var directory = new TemporaryDirectory();
        var resourceDirectory = directory.CreateDirectory("private-runtime-resources");
        WriteCp949(
            Path.Combine(resourceDirectory, "종목.ini"),
            ["[종목]", "등록되지_않은_컷"]);

        var result = LegacyOperatorFolderValidator.Validate(
            LegacyOperatorSettingsFolderKind.Resource,
            resourceDirectory);

        Assert.True(result.IsValid);
        Assert.Equal(Path.GetFullPath(resourceDirectory), result.Folder!.CanonicalPath);
        Assert.Null(result.WarningMessage);
    }

    [Fact]
    public void ResourceValidation_DoesNotInspectDatabaseIni()
    {
        using var directory = new TemporaryDirectory();
        var resourceDirectory = directory.CreateDirectory("Res");
        Directory.CreateDirectory(Path.Combine(resourceDirectory, "MmoneyCoder.ini"));

        var result = LegacyOperatorFolderValidator.Validate(
            LegacyOperatorSettingsFolderKind.Resource,
            resourceDirectory);

        Assert.True(result.IsValid);
        Assert.Equal(Path.GetFullPath(resourceDirectory), result.Folder!.CanonicalPath);
        Assert.False(result.Folder.DatabaseIniDetected);
        Assert.Null(result.WarningMessage);
    }

    [Fact]
    public void Validation_RejectsRelativeMissingRootAndControlContainingPaths()
    {
        using var directory = new TemporaryDirectory();
        var missing = Path.Combine(directory.Path, "missing-private-folder");
        string?[] candidates =
        [
            null,
            string.Empty,
            "relative-folder",
            missing,
            Path.GetPathRoot(directory.Path),
            directory.Path + "\nprivate"
        ];

        foreach (var candidate in candidates)
        {
            var result = LegacyOperatorFolderValidator.Validate(
                LegacyOperatorSettingsFolderKind.Background,
                candidate);

            Assert.False(result.IsValid);
            Assert.Null(result.Folder);
            AssertPathFree(result.WarningMessage, missing);
        }
    }

    [Fact]
    public void Validation_RejectsAReparsePointInTheDirectoryAncestry_WhenSupported()
    {
        using var directory = new TemporaryDirectory();
        var target = directory.CreateDirectory("target");
        var link = Path.Combine(directory.Path, "link");
        try
        {
            Directory.CreateSymbolicLink(link, target);
        }
        catch (Exception exception) when (
            exception is IOException or UnauthorizedAccessException or
            PlatformNotSupportedException or NotSupportedException)
        {
            return;
        }

        try
        {
            var result = LegacyOperatorFolderValidator.Validate(
                LegacyOperatorSettingsFolderKind.Background,
                link);

            Assert.False(result.IsValid);
            Assert.Equal(
                LegacyOperatorFolderValidationFailure.ReparsePoint,
                result.Failure);
        }
        finally
        {
            Directory.Delete(link);
        }
    }

    [Fact]
    public void Load_MissingFileUsesDefaultsWithoutWarning()
    {
        using var directory = new TemporaryDirectory();
        var store = new LegacyOperatorSettingsStore(
            Path.Combine(directory.Path, "Config", "runtime-folders.local.json"));

        var result = store.Load();

        Assert.Equal(LegacyOperatorSettings.Default, result.Settings);
        Assert.False(result.LoadedFromDisk);
        Assert.Equal(LegacyOperatorSettingsStoreFailure.None, result.Failure);
        Assert.Null(result.WarningMessage);
    }

    [Fact]
    public void SaveAndLoad_RoundTripsOnlyTheVersionedClosedSchemaAtomically()
    {
        using var directory = new TemporaryDirectory();
        var sceneDirectory = directory.CreateDirectory("Cuts");
        WriteValidSceneFiles(sceneDirectory);
        var resourceDirectory = directory.CreateDirectory("Res");
        WriteValidResourceFiles(resourceDirectory);
        var backgroundDirectory = directory.CreateDirectory("Background");
        var configurationPath = Path.Combine(
            directory.Path,
            "Config",
            "runtime-folders.local.json");
        var store = new LegacyOperatorSettingsStore(configurationPath);
        var settings = new LegacyOperatorSettings(
            sceneDirectory,
            resourceDirectory,
            backgroundDirectory,
            NavigationExpanded: false,
            ColorTheme: LegacyOperatorColorTheme.Dark,
            ViewMode: LegacyOperatorViewMode.Cards,
            StartWorkspace: LegacyOperatorStartWorkspace.LastWorkspace);

        var firstSave = store.Save(settings);
        var secondSave = store.Save(settings with { NavigationExpanded = true });
        var loaded = store.Load();

        Assert.True(firstSave.Succeeded);
        Assert.True(secondSave.Succeeded);
        Assert.NotNull(secondSave.SavedSettings);
        Assert.Null(secondSave.WarningMessage);
        Assert.True(loaded.LoadedFromDisk);
        Assert.True(loaded.Settings.NavigationExpanded);
        Assert.Equal(LegacyOperatorColorTheme.Dark, loaded.Settings.ColorTheme);
        Assert.Equal(LegacyOperatorViewMode.Cards, loaded.Settings.ViewMode);
        Assert.Equal(
            LegacyOperatorStartWorkspace.LastWorkspace,
            loaded.Settings.StartWorkspace);
        Assert.Equal(Path.GetFullPath(sceneDirectory), loaded.Settings.SceneDirectory);
        Assert.Equal(Path.GetFullPath(resourceDirectory), loaded.Settings.ResourceDirectory);
        Assert.Equal(
            Path.GetFullPath(backgroundDirectory),
            loaded.Settings.BackgroundDirectory);
        Assert.Empty(Directory.EnumerateFiles(
            Path.GetDirectoryName(configurationPath)!,
            "*.tmp"));

        using var document = JsonDocument.Parse(File.ReadAllBytes(configurationPath));
        var names = document.RootElement.EnumerateObject()
            .Select(property => property.Name)
            .ToArray();
        Assert.Equal(
            [
                "schemaVersion",
                "sceneDirectory",
                "resourceDirectory",
                "backgroundDirectory",
                "navigationExpanded",
                "colorTheme",
                "viewMode",
                "startWorkspace"
            ],
            names);
        Assert.Equal(
            LegacyOperatorSettings.CurrentSchemaVersion,
            document.RootElement.GetProperty("schemaVersion").GetInt32());
        Assert.Equal("dark", document.RootElement.GetProperty("colorTheme").GetString());
        Assert.Equal("cards", document.RootElement.GetProperty("viewMode").GetString());
        Assert.Equal(
            "lastWorkspace",
            document.RootElement.GetProperty("startWorkspace").GetString());
        Assert.DoesNotContain(
            document.RootElement.EnumerateObject(),
            property => property.Name.Contains("password", StringComparison.OrdinalIgnoreCase) ||
                property.Name.Contains("target", StringComparison.OrdinalIgnoreCase) ||
                property.Name.Contains("live", StringComparison.OrdinalIgnoreCase));
    }

    [Fact]
    public void Load_LegacySchemaUsesClosedAppearanceDefaults()
    {
        using var directory = new TemporaryDirectory();
        var configurationPath = Path.Combine(directory.Path, "settings.json");
        File.WriteAllText(
            configurationPath,
            "{\"schemaVersion\":1,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":false}",
            new UTF8Encoding(false));

        var result = new LegacyOperatorSettingsStore(configurationPath).Load();

        Assert.True(result.LoadedFromDisk);
        Assert.Equal(LegacyOperatorSettingsStoreFailure.None, result.Failure);
        Assert.False(result.Settings.NavigationExpanded);
        Assert.Equal(LegacyOperatorColorTheme.System, result.Settings.ColorTheme);
        Assert.Equal(LegacyOperatorViewMode.Automatic, result.Settings.ViewMode);
        Assert.Equal(
            LegacyOperatorStartWorkspace.StockCut,
            result.Settings.StartWorkspace);
    }

    [Theory]
    [InlineData("{}", LegacyOperatorSettingsStoreFailure.InvalidJson)]
    [InlineData("{\"schemaVersion\":3,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true}", LegacyOperatorSettingsStoreFailure.UnsupportedSchema)]
    [InlineData("{\"schemaVersion\":1,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true,\"unexpected\":1}", LegacyOperatorSettingsStoreFailure.InvalidJson)]
    [InlineData("{\"schemaVersion\":1,\"schemaVersion\":1,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true}", LegacyOperatorSettingsStoreFailure.InvalidJson)]
    [InlineData("{\"SchemaVersion\":1,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true}", LegacyOperatorSettingsStoreFailure.InvalidJson)]
    [InlineData("{\"schemaVersion\":1,\"sceneDirectory\":\"relative\",\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true}", LegacyOperatorSettingsStoreFailure.InvalidSettings)]
    [InlineData("{\"schemaVersion\":2,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true,\"colorTheme\":\"mbn\",\"viewMode\":\"automatic\",\"startWorkspace\":\"stockCut\"}", LegacyOperatorSettingsStoreFailure.InvalidJson)]
    [InlineData("{\"schemaVersion\":2,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true,\"colorTheme\":\"system\",\"viewMode\":\"grid\",\"startWorkspace\":\"stockCut\"}", LegacyOperatorSettingsStoreFailure.InvalidJson)]
    [InlineData("{\"schemaVersion\":2,\"sceneDirectory\":null,\"resourceDirectory\":null,\"backgroundDirectory\":null,\"navigationExpanded\":true,\"colorTheme\":\"system\",\"viewMode\":\"automatic\",\"startWorkspace\":\"other\"}", LegacyOperatorSettingsStoreFailure.InvalidJson)]
    public void Load_StrictMalformedOrUnsafeJsonFailsClosed(
        string json,
        LegacyOperatorSettingsStoreFailure expectedFailure)
    {
        using var directory = new TemporaryDirectory();
        var configurationPath = Path.Combine(directory.Path, "settings.json");
        File.WriteAllText(configurationPath, json, new UTF8Encoding(false));
        var store = new LegacyOperatorSettingsStore(configurationPath);

        var result = store.Load();

        Assert.Equal(LegacyOperatorSettings.Default, result.Settings);
        Assert.False(result.LoadedFromDisk);
        Assert.Equal(expectedFailure, result.Failure);
        AssertPathFree(result.WarningMessage, directory.Path);
    }

    [Fact]
    public void Load_OversizedJsonFailsBeforeParsing()
    {
        using var directory = new TemporaryDirectory();
        var configurationPath = Path.Combine(directory.Path, "settings.json");
        using (var stream = new FileStream(
                   configurationPath,
                   FileMode.CreateNew,
                   FileAccess.Write))
        {
            stream.SetLength(LegacyOperatorSettingsStore.MaximumFileBytes + 1L);
        }

        var result = new LegacyOperatorSettingsStore(configurationPath).Load();

        Assert.Equal(LegacyOperatorSettingsStoreFailure.FileTooLarge, result.Failure);
        Assert.Equal(LegacyOperatorSettings.Default, result.Settings);
        AssertPathFree(result.WarningMessage, configurationPath);
    }

    [Fact]
    public void Save_RejectsUnvalidatedPathsWithoutCreatingAConfigurationFile()
    {
        using var directory = new TemporaryDirectory();
        var configurationPath = Path.Combine(directory.Path, "settings.json");
        var store = new LegacyOperatorSettingsStore(configurationPath);
        var settings = LegacyOperatorSettings.Default with
        {
            SceneDirectory = Path.Combine(directory.Path, "not-present-private-cuts")
        };

        var result = store.Save(settings);

        Assert.False(result.Succeeded);
        Assert.Equal(LegacyOperatorSettingsStoreFailure.InvalidSettings, result.Failure);
        Assert.Null(result.SavedSettings);
        Assert.False(File.Exists(configurationPath));
        AssertPathFree(result.WarningMessage, settings.SceneDirectory!);
    }

    [Theory]
    [InlineData(99, 0, 0)]
    [InlineData(0, 99, 0)]
    [InlineData(0, 0, 99)]
    public void Save_RejectsUndefinedAppearanceValuesWithoutCreatingAConfigurationFile(
        int colorTheme,
        int viewMode,
        int startWorkspace)
    {
        using var directory = new TemporaryDirectory();
        var configurationPath = Path.Combine(directory.Path, "settings.json");
        var settings = LegacyOperatorSettings.Default with
        {
            ColorTheme = (LegacyOperatorColorTheme)colorTheme,
            ViewMode = (LegacyOperatorViewMode)viewMode,
            StartWorkspace = (LegacyOperatorStartWorkspace)startWorkspace
        };

        var result = new LegacyOperatorSettingsStore(configurationPath).Save(settings);

        Assert.False(result.Succeeded);
        Assert.Equal(LegacyOperatorSettingsStoreFailure.InvalidSettings, result.Failure);
        Assert.False(File.Exists(configurationPath));
    }

    [Fact]
    public void AbbreviateForDisplay_IsBoundedAndNeverReturnsInvalidInput()
    {
        using var directory = new TemporaryDirectory();
        var longLeaf = new string('a', 80);
        var path = directory.CreateDirectory(longLeaf);

        var abbreviated = LegacyOperatorFolderValidator.AbbreviateForDisplay(path, 20);

        Assert.InRange(abbreviated.Length, 1, 20);
        Assert.Contains('…', abbreviated);
        Assert.Equal(
            LegacyOperatorFolderValidator.UnconfiguredDisplay,
            LegacyOperatorFolderValidator.AbbreviateForDisplay("relative\\path"));
    }

    private static void WriteValidResourceFiles(string resourceDirectory)
    {
        var stockLabel = LegacyStockCutCatalog.Rows
            .First(row => !row.IsSeparator)
            .RawLabel;
        WriteCp949(
            Path.Combine(resourceDirectory, "종목.ini"),
            ["[종목]", stockLabel]);

        WriteFixedFile(
            resourceDirectory,
            "해외.ini",
            LegacyFixedMarket.Overseas);
        WriteFixedFile(
            resourceDirectory,
            "환율.ini",
            LegacyFixedMarket.Exchange);
        WriteFixedFile(
            resourceDirectory,
            "지수.ini",
            LegacyFixedMarket.Index);
        WriteIndustryFile(
            resourceDirectory,
            "업종_코스피.ini",
            IndustryMarket.Kospi);
        WriteIndustryFile(
            resourceDirectory,
            "업종_코스닥.ini",
            IndustryMarket.Kosdaq);
        WriteComparisonFile(resourceDirectory);
    }

    private static void WriteValidSceneFiles(string sceneDirectory)
    {
        foreach (var fileName in LegacyOperatorFolderValidator.RequiredSceneFileNames)
        {
            File.WriteAllText(Path.Combine(sceneDirectory, fileName), "fixture-scene");
        }

        foreach (var relativePath in
                 LegacyOperatorFolderValidator.RequiredBuiltInAssetRelativePaths)
        {
            var path = Path.Combine(sceneDirectory, relativePath);
            Directory.CreateDirectory(Path.GetDirectoryName(path)!);
            File.WriteAllText(path, "fixture-asset");
        }
    }

    private static void WriteFixedFile(
        string resourceDirectory,
        string fileName,
        LegacyFixedMarket market)
    {
        var lines = new List<string>();
        foreach (var section in LegacyFixedActionCatalog.Default
                     .GetActions(market)
                     .GroupBy(action => action.Section, StringComparer.Ordinal))
        {
            lines.Add($"[{section.Key}]");
            lines.AddRange(section.Select(action => action.Label));
        }

        WriteCp949(Path.Combine(resourceDirectory, fileName), lines);
    }

    private static void WriteIndustryFile(
        string resourceDirectory,
        string fileName,
        IndustryMarket market)
    {
        var actions = LegacyIndustryActionCatalog.GetActions(market);
        var section = market == IndustryMarket.Kospi ? "코스피" : "코스닥";
        WriteCp949(
            Path.Combine(resourceDirectory, fileName),
            [$"[{section}]", .. actions.Select(action => action.Label)]);
    }

    private static void WriteComparisonFile(string resourceDirectory)
    {
        var actions = LegacyComparisonWorkflowCatalog.Actions;
        var directActions = actions
            .Where(action => action.Kind != LegacyComparisonActionKind.Return)
            .ToArray();
        var returnActions = actions
            .Where(action => action.Kind == LegacyComparisonActionKind.Return)
            .ToArray();
        var lines = new List<string> { "[지수]" };
        lines.AddRange(LegacyComparisonWorkflowCatalog.FixedTargets
            .Select(target => target.DisplayName));
        lines.AddRange(directActions.Select(action => $"[{action.Label}]"));
        lines.Add($"[{returnActions[0].Section}]");
        lines.AddRange(returnActions.Select(action => action.Label));
        WriteCp949(Path.Combine(resourceDirectory, "종목비교.ini"), lines);
    }

    private static void WriteCp949(string path, IEnumerable<string> lines) =>
        File.WriteAllText(path, string.Join("\r\n", lines) + "\r\n", Cp949);

    private static void AssertPathFree(string? warningMessage, string sensitivePath)
    {
        var warning = Assert.IsType<string>(warningMessage);
        Assert.InRange(warning.Length, 1, 100);
        Assert.DoesNotContain(sensitivePath, warning, StringComparison.OrdinalIgnoreCase);
        Assert.DoesNotContain('\\', warning);
        Assert.DoesNotContain('/', warning);
    }

    private static Encoding CreateCp949Encoding()
    {
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
        return Encoding.GetEncoding(
            949,
            EncoderFallback.ExceptionFallback,
            DecoderFallback.ExceptionFallback);
    }

    private static string FindRepositoryRoot()
    {
        for (var current = new DirectoryInfo(AppContext.BaseDirectory);
             current is not null;
             current = current.Parent)
        {
            if (File.Exists(Path.Combine(current.FullName, "MBN_STOCK_WEBVIEW.sln")))
            {
                return current.FullName;
            }
        }

        throw new DirectoryNotFoundException("Repository root was not found.");
    }

    private sealed class TemporaryDirectory : IDisposable
    {
        public TemporaryDirectory()
        {
            Path = System.IO.Path.Combine(
                System.IO.Path.GetTempPath(),
                $"MBN_STOCK_WEBVIEW_SETTINGS_{Guid.NewGuid():N}");
            Directory.CreateDirectory(Path);
        }

        public string Path { get; }

        public string CreateDirectory(string name)
        {
            var path = System.IO.Path.Combine(Path, name);
            Directory.CreateDirectory(path);
            return path;
        }

        public void Dispose()
        {
            if (Directory.Exists(Path))
            {
                Directory.Delete(Path, recursive: true);
            }
        }
    }
}
