// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if NET472 // AppDomains

using System;
using System.Collections.Immutable;
using System.Reflection;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Test.Utilities.Desktop;
using Xunit;
using Basic.Reference.Assemblies;
using System.Numerics;

namespace Microsoft.CodeAnalysis.UnitTests
{
    public class RemoteAnalyzerFileReferenceTest : MarshalByRefObject
    {
        public override object InitializeLifetimeService()
        {
            return null;
        }

        public Exception LoadAnalyzer(string shadowPath, string analyzerPath)
        {
            var loader = AnalyzerAssemblyLoader.CreateNonLockingLoader(shadowPath, []);
            Exception analyzerLoadException = null;
            var analyzerRef = new AnalyzerFileReference(analyzerPath, loader);
            analyzerRef.AnalyzerLoadFailed += (s, e) => analyzerLoadException = e.Exception;
            var builder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>();
            analyzerRef.AddAnalyzers(builder, LanguageNames.CSharp);
            return analyzerLoadException;
        }
    }

    public class AnalyzerFileReferenceAppDomainTests : TestBase
    {
        [Fact]
        public void TestAnalyzerLoading_AppDomain()
        {
            var dir = Temp.CreateDirectory();
            dir.CopyFile(typeof(AppDomainUtils).Assembly.Location);
            dir.CopyFile(typeof(RemoteAnalyzerFileReferenceTest).Assembly.Location);
            dir.CopyFile(typeof(Vector).Assembly.Location);
            var analyzerFile = DesktopTestHelpers.CreateCSharpAnalyzerAssemblyWithTestAnalyzer(dir, "MyAnalyzer");
            var loadDomain = AppDomainUtils.Create("AnalyzerTestDomain", basePath: dir.Path);
            try
            {
                // Test analyzer load success.
                var remoteTest = (RemoteAnalyzerFileReferenceTest)loadDomain.CreateInstanceAndUnwrap(typeof(RemoteAnalyzerFileReferenceTest).Assembly.FullName, typeof(RemoteAnalyzerFileReferenceTest).FullName);
                var exception = remoteTest.LoadAnalyzer(dir.CreateDirectory("shadow").Path, analyzerFile.Path);
                Assert.Null(exception);
            }
            finally
            {
                AppDomain.Unload(loadDomain);
            }
        }

        [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/mono/mono/issues/10960")]
        public void TestAnalyzerLoading_Error()
        {
            var analyzerSource = @"
using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Runtime.InteropServices;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
[StructLayout(LayoutKind.Sequential, Size = 10000000)]
public class TestAnalyzer : DiagnosticAnalyzer
{
    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { throw new NotImplementedException(); } }
    public override void Initialize(AnalysisContext context) { throw new NotImplementedException(); }
}";

            var dir = Temp.CreateDirectory();

            dir.CopyFile(typeof(System.Reflection.Metadata.MetadataReader).Assembly.Location);
            dir.CopyFile(typeof(AppDomainUtils).Assembly.Location);
            dir.CopyFile(typeof(Memory<>).Assembly.Location);
            dir.CopyFile(typeof(System.Runtime.CompilerServices.Unsafe).Assembly.Location);
            var immutable = dir.CopyFile(typeof(ImmutableArray).Assembly.Location);
            var analyzer = dir.CopyFile(typeof(DiagnosticAnalyzer).Assembly.Location);
            dir.CopyFile(typeof(RemoteAnalyzerFileReferenceTest).Assembly.Location);
            dir.CopyFile(typeof(Vector).Assembly.Location);

            var analyzerCompilation = CSharp.CSharpCompilation.Create(
                "MyAnalyzer",
                new SyntaxTree[] { CSharp.SyntaxFactory.ParseSyntaxTree(analyzerSource) },
                new MetadataReference[]
                {
                    NetStandard20.References.mscorlib,
                    NetStandard20.References.netstandard,
                    NetStandard20.References.SystemRuntime,
                    MetadataReference.CreateFromFile(immutable.Path),
                    MetadataReference.CreateFromFile(analyzer.Path)
                },
                new CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, warningLevel: CodeAnalysis.Diagnostic.MaxWarningLevel));

            var analyzerFile = dir.CreateFile("MyAnalyzer.dll").WriteAllBytes(analyzerCompilation.EmitToArray());

            var loadDomain = AppDomainUtils.Create("AnalyzerTestDomain", basePath: dir.Path);
            try
            {
                // Test analyzer load failure.
                var remoteTest = (RemoteAnalyzerFileReferenceTest)loadDomain.CreateInstanceAndUnwrap(typeof(RemoteAnalyzerFileReferenceTest).Assembly.FullName, typeof(RemoteAnalyzerFileReferenceTest).FullName);
                var exception = remoteTest.LoadAnalyzer(dir.CreateDirectory("shadow").Path, analyzerFile.Path);
                Assert.NotNull(exception as TypeLoadException);
            }
            finally
            {
                AppDomain.Unload(loadDomain);
            }
        }

        private static Assembly OnResolve(object sender, ResolveEventArgs e)
        {
            Console.WriteLine($"Resolve in {AppDomain.CurrentDomain.Id} for {e.Name}");
            return null;
        }
    }
}
#endif
