// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
using Microsoft.AspNetCore.SpaServices.StaticFiles;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DiagnosticAdapter;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace Microsoft.AspNetCore.SpaServices.Extensions.Tests
{
    public class SpaServicesExtensionsTests
    {
        [Fact]
        public void UseSpa_ThrowsInvalidOperationException_IfRootpathNotSet()
        {
            // Arrange
            var applicationbuilder = GetApplicationBuilder(GetServiceProvider());

            // Act & Assert
            var exception = Assert.Throws<InvalidOperationException>(
                () => applicationbuilder.UseSpa(rb => { }));

            Assert.Equal("No RootPath was set on the SpaStaticFilesOptions.", exception.Message);
        }

        private IApplicationBuilder GetApplicationBuilder(IServiceProvider serviceProvider = null)
        {
            if (serviceProvider == null)
            {
                serviceProvider = new Mock<IServiceProvider>(MockBehavior.Strict).Object;
            }

            var applicationbuilderMock = new Mock<IApplicationBuilder>();
            applicationbuilderMock
                .Setup(s => s.ApplicationServices)
                .Returns(serviceProvider);

            return applicationbuilderMock.Object;
        }

        private IServiceProvider GetServiceProvider(Action<SpaStaticFilesOptions> configuration = null)
        {
            var services = new ServiceCollection();
            services.AddLogging();
            services.AddSpaStaticFiles(configuration);
            services.AddSingleton<ILoggerFactory>(ListLoggerFactory);
            services.AddSingleton(typeof(IHostApplicationLifetime), new TestHostApplicationLifetime());
            services.AddSingleton(typeof(IWebHostEnvironment), new TestWebHostEnvironment());

            var listener = new DiagnosticListener("Microsoft.AspNetCore");
            services.AddSingleton(listener);
            services.AddSingleton<DiagnosticSource>(listener);

            return services.BuildServiceProvider();
        }

        private ListLoggerFactory ListLoggerFactory { get; } = new ListLoggerFactory(c => c == "Microsoft.AspNetCore.SpaServices");

        private class TestHostApplicationLifetime : IHostApplicationLifetime
        {
            CancellationTokenSource _applicationStoppingSource;
            CancellationTokenSource _applicationStoppedSource;

            public TestHostApplicationLifetime()
            {
                _applicationStoppingSource = new CancellationTokenSource();
                ApplicationStopping = _applicationStoppingSource.Token;

                _applicationStoppedSource = new CancellationTokenSource();
                ApplicationStopped = _applicationStoppedSource.Token;
            }

            public CancellationToken ApplicationStarted => CancellationToken.None;

            public CancellationToken ApplicationStopping { get; }

            public CancellationToken ApplicationStopped { get; }

            public void StopApplication()
            {
                _applicationStoppingSource.Cancel();
                _applicationStoppedSource.Cancel();
            }
        }

        private class TestWebHostEnvironment : IWebHostEnvironment
        {
            public string EnvironmentName { get; set; }
            public string ApplicationName { get; set; }
            public string ContentRootPath { get; set; } = Directory.GetCurrentDirectory();
            public string WebRootPath { get; set; } = Directory.GetCurrentDirectory();
            public IFileProvider ContentRootFileProvider { get; set; }
            public IFileProvider WebRootFileProvider { get; set; }
        }
    }
}
