// 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 Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.TestFramework;
using Microsoft.DotNet.Tools.Test.Utilities;
using System;
using System.IO;
using System.Runtime.InteropServices;
using Xunit;
using Xunit.Abstractions;
using FluentAssertions;
using Microsoft.DotNet.PlatformAbstractions;

namespace Microsoft.DotNet.Tests
{
    public class GivenThatIWantToManageMulticoreJIT : TestBase
    {
        ITestOutputHelper _output;
        private const string OptimizationProfileFileName = "dotnet";
        
        public GivenThatIWantToManageMulticoreJIT(ITestOutputHelper output)
        {
            _output = output;
        }

        [Fact]
        public void WhenInvokedThenDotnetWritesOptimizationDataToTheProfileRoot()
        {
            var testDirectory = TestAssets.CreateTestDirectory();
            var testStartTime = GetTruncatedDateTime();
                        
            new TestCommand("dotnet")
                .WithUserProfileRoot(testDirectory.FullName)
                .ExecuteWithCapturedOutput("--help");

            var optimizationProfileFilePath = GetOptimizationProfileFilePath(testDirectory.FullName);

            new FileInfo(optimizationProfileFilePath)
                .Should().Exist("Because dotnet CLI creates it after each run")
                     .And.HaveLastWriteTimeUtc()
                         .Which.Should().BeOnOrAfter(testStartTime, "Because dotnet CLI was executed after that time");
        }

        [Fact]
        public void WhenInvokedWithMulticoreJitDisabledThenDotnetDoesNotWriteOptimizationDataToTheProfileRoot()
        {
            var testDirectory = TestAssets.CreateTestDirectory();
            var testStartTime = GetTruncatedDateTime();
                        
            new TestCommand("dotnet")
                .WithUserProfileRoot(testDirectory.FullName)
                .WithEnvironmentVariable("DOTNET_DISABLE_MULTICOREJIT", "1")
                .ExecuteWithCapturedOutput("--help");

            var optimizationProfileFilePath = GetOptimizationProfileFilePath(testDirectory.FullName);

            File.Exists(optimizationProfileFilePath)
                .Should().BeFalse("Because multicore JIT is disabled");
        }

        [Fact]
        public void WhenTheProfileRootIsUndefinedThenDotnetDoesNotCrash()
        {
            var testDirectory = TestAssets.CreateTestDirectory();
            var testStartTime = GetTruncatedDateTime();
            
            var optimizationProfileFilePath = GetOptimizationProfileFilePath(testDirectory.FullName);

            new TestCommand("dotnet")
                .WithUserProfileRoot("")
                .ExecuteWithCapturedOutput("--help")
                .Should().Pass();
        }

        [Fact]
        public void WhenCliRepoBuildsThenDotnetWritesOptimizationDataToTheDefaultProfileRoot()
        {
            var optimizationProfileFilePath = GetOptimizationProfileFilePath();

            File.Exists(optimizationProfileFilePath)
                .Should().BeTrue("Because the dotnet building dotnet writes to the default root");
        }

        private static string GetOptimizationProfileFilePath(string userHomePath = null)
        {
            return Path.Combine(
                GetUserProfileRoot(userHomePath), 
                GetOptimizationRootPath(GetDotnetVersion()),
                OptimizationProfileFileName);
        }
        
        private static string GetUserProfileRoot(string overrideUserProfileRoot = null)
        {
            if (overrideUserProfileRoot != null)
            {
                return overrideUserProfileRoot;
            }
            
            return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                ? Environment.GetEnvironmentVariable("LocalAppData")
                : Environment.GetEnvironmentVariable("HOME");
        }

        private static string GetOptimizationRootPath(string version)
        {
            var rid = PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier();
            
            return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                ? $@"Microsoft\dotnet\optimizationdata\{version}\{rid}" 
                : $@".dotnet/optimizationdata/{version}/{rid}";
        }

        private static string GetDotnetVersion()
        {
            return new TestCommand("dotnet")
                .ExecuteWithCapturedOutput("--version" )
                .StdOut
                .Trim();
        }
        
        private static DateTime GetTruncatedDateTime()
        {
            var dt = DateTime.UtcNow;
            
            return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, 0, dt.Kind);
        }
    }
}
