// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.TestFramework;

namespace Microsoft.DotNet.Tools.Test.Utilities
{

    /// <summary>
    /// Base class for all unit test classes.
    /// </summary>
    public abstract class TestBase : IDisposable
    {
        protected const string DefaultFramework = "netstandardapp1.5";
        private TempRoot _temp;
        private static TestAssetsManager s_testsAssetsMgr;
        private static string s_repoRoot;

        protected static string RepoRoot
        {
            get
            {
                if (!string.IsNullOrEmpty(s_repoRoot))
                {
                    return s_repoRoot;
                }

                string directory = AppContext.BaseDirectory;

                while (!Directory.Exists(Path.Combine(directory, ".git")) && directory != null)
                {
                    directory = Directory.GetParent(directory).FullName;
                }

                if (directory == null)
                {
                    throw new Exception("Cannot find the git repository root");
                }

                s_repoRoot = directory;
                return s_repoRoot;
            }
        }

        protected static TestAssetsManager TestAssetsManager
        {
            get
            {
                if (s_testsAssetsMgr == null)
                {
                    string assetsRoot = Path.Combine(RepoRoot, "TestAssets", "TestProjects");
                    s_testsAssetsMgr = new TestAssetsManager(assetsRoot);
                }

                return s_testsAssetsMgr;
            }
        }

        protected TestBase()
        {
        }

        public static string GetUniqueName()
        {
            return Guid.NewGuid().ToString("D");
        }

        public TempRoot Temp
        {
            get
            {
                if (_temp == null)
                {
                    _temp = new TempRoot();
                }

                return _temp;
            }
        }

        public virtual void Dispose()
        {
            if (_temp != null && !PreserveTemp())
            {
                _temp.Dispose();
            }
        }

        // Quick-n-dirty way to allow the temp output to be preserved when running tests
        private bool PreserveTemp()
        {
            var val = Environment.GetEnvironmentVariable("DOTNET_TEST_PRESERVE_TEMP");
            return !string.IsNullOrEmpty(val) && (
                string.Equals("true", val, StringComparison.OrdinalIgnoreCase) ||
                string.Equals("1", val, StringComparison.OrdinalIgnoreCase) ||
                string.Equals("on", val, StringComparison.OrdinalIgnoreCase));
        }

        protected void TestExecutable(string outputDir,
            string executableName,
            string expectedOutput)
        {
            var executablePath = Path.Combine(outputDir, executableName);

            var executableCommand = new TestCommand(executablePath);

            var result = executableCommand.ExecuteWithCapturedOutput("");

            result.Should().HaveStdOut(expectedOutput);
            result.Should().NotHaveStdErr();
            result.Should().Pass();
        }

        protected void TestOutputExecutable(
            string outputDir,
            string executableName,
            string expectedOutput,
            bool native = false)
        {
            TestExecutable(GetCompilationOutputPath(outputDir, native), executableName, expectedOutput);
        }

        protected void TestNativeOutputExecutable(string outputDir, string executableName, string expectedOutput)
        {
            TestOutputExecutable(outputDir, executableName, expectedOutput, true);
        }

        protected string GetCompilationOutputPath(string outputDir, bool native)
        {
            var executablePath = outputDir;
            if (native)
            {
                executablePath = Path.Combine(executablePath, "native");
            }

            return executablePath;
        }
    }
}
