using Knoks.Api.Authentication;
using Knoks.Api.Constraints;
using Knoks.Api.Data.Dao;
using Knoks.Api.Data.Interfaces;
using Knoks.Api.Filters;
using Knoks.Api.Logic.Interfaces;
using Knoks.Api.Logic.Managers;
using Knoks.Api.Specification;
using Knoks.Core.Data.Dao;
//using Knoks.Core.Data.Dao.Interfaces;
using Knoks.Core.Data.Interfaces;
using Knoks.Core.Logic.Interfaces;
using Knoks.Core.Logic.Managers;
using Knoks.Framework.Cryptography;
using Knoks.Framework.DataAccess;
using Knoks.Framework.Document;
using Knoks.Framework.Security;
using Knoks.Framework.Services;
using Knoks.Framework.Content;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Swashbuckle.AspNetCore.Swagger;
using System.Collections.Generic;
using System.IO;
using System.Text;
using static Knoks.Api.Authentication.IdentityConsts;
using Microsoft.Extensions.Hosting;

namespace Knoks.Api
{
    public class Startup
    {
        public static void Main(string[] args)
        {
            new WebHostBuilder()
                .UseKestrel(o => o.AddServerHeader = false)
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build()
                .Run();
        }

        private const string MySecret = "MySecret";
        private readonly SymmetricSecurityKey SecurityKey;

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

            if (env.IsEnvironment("Development"))
            {
                // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
                builder.AddApplicationInsightsSettings(developerMode: true);
                builder.AddUserSecrets<Startup>();
            }

            builder.AddEnvironmentVariables();
            Configuration = builder.Build();
            //On exception case go to project folder and run the command: ".....\Knoks.Api>dotnet user-secrets set MySecret MySuperLongAndSecuritySecret"
            //For EC2 dev machine example: dotnet user-secrets set MySecret 3f2977b8-1184-4f81-95f8-466f503d6086" 
            SecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration[MySecret] ?? $"{MySecret} - 3f2977b8-1184-4f81-95f8-466f503d6086"));
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services
                .AddApplicationInsightsTelemetry(Configuration)
                .AddCors()
                .AddJwtAuthentication(Configuration, SecurityKey)
                .AddMvc()
                .AddMvcOptions(o =>
                {
                    o.Filters.Add(typeof(GlobalExceptionFilter));
                    o.Filters.Add(typeof(ApiResponseResultFilter));
                });

            //.AddJsonOptions(o => o.SerializerSettings.ContractResolver = new JsonPropertiesResolver());

            services.AddAuthorization(options =>
            {
                options.AddPolicy(ApiConsumerPolicy, policy => policy.RequireClaim(ApiIdentifierClaim));
                options.AddPolicy(ApiUserPolicy, policy => policy.RequireClaim(ApiIdentifierClaim).RequireClaim(UserIdClaim));
                options.AddPolicy(ApiOperatorPolicy, policy => policy.RequireClaim(ApiIdentifierClaim).RequireClaim(OperatorIdClaim));
            });

            //-- Options configs ------------------------------------------------------------------
            services.AddSingleton(provider => Configuration.Load(new JwtIssuerOptions { SigningCredentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256) }));
            services.AddSingleton<IPasswordOptions>(provider => Configuration.Load<PasswordOptions>());
            services.AddSingleton<IDocumentOptions>(provider => Configuration.Load<DocumentOptions>());
            services.AddSingleton<IAccountTransactionsSettings>(provider => Configuration.Load<AccountTransactionsSettings>());

            //-- ProcExecutor config --------------------------------------------------------------
            services.AddSingleton<IProcExecutor>(provider => new ProcExecutor(Configuration.Load<Dictionary<string, string>>("DbConnections"), provider.GetRequiredService<ILogger<ProcExecutor>>()));

            //-- Framework configs ----------------------------------------------------------------
            services.AddSingleton<IPasswordStrength, PasswordStrength>();
            services.AddSingleton<IPasswordProcess, PasswordProcess>();
            services.AddSingleton<IDocumentProcess, DocumentProcess>();
            services.AddSingleton<IContentGenerator, ContentGenerator>();

            //-- Data configs ---------------------------------------------------------------------
            services.AddSingleton<IGeneralDao, GeneralDao>();
            services.AddSingleton<IUserDao, UserDao>();
            services.AddSingleton<IAccountDao, AccountDao>();
            services.AddSingleton<IOperatorDao, OperatorDao>();
            services.AddSingleton<IApiConsumerDao, ApiConsumerDao>();
            services.AddSingleton<IBackofficeDao, BackofficeDao>();
            services.AddSingleton<ISystemConfigurationDao, SystemConfigurationDao>();
            services.AddSingleton<IKnokDao, KnokDao>();
            services.AddSingleton<IKnokPriceDao, KnokPriceDao>();
            services.AddSingleton<ITagDao, TagDao>();
            services.AddSingleton<IExchangeDao, ExchangeDao>();
            services.AddSingleton<IAccountTransactionDao, AccountTransactionDao>();
            services.AddSingleton<AccountTransactionsSettings>();

            //-- Logic and Managers configs -------------------------------------------------------
            services.AddSingleton<IGeneralManager, GeneralManager>();
            services.AddSingleton<IKnokPriceLogic, KnokPriceLogic>();
            services.AddSingleton<IKnokPricelManager, KnokPriceManager>();
            services.AddSingleton<IUserManager>(provider => provider.Init<UserManager>());
            services.AddSingleton<IOperatorManager, OperatorManager>();
            services.AddSingleton<IApiConsumerManager>(provider => provider.Init<ApiConsumerManager>());
            services.AddSingleton<IEventManager>(provider => provider.Init<EventManager>());
            services.AddSingleton<IBackofficeManager, BackofficeManager>();
            services.AddSingleton<IPhoneManager, PhoneManager>();
            services.AddSingleton<ISystemConfigurationManager, SystemConfigurationManager>();
            services.AddSingleton<IKnokSettings>(provider => Configuration.Load<KnokSettings>());
            services.AddSingleton<IKnokManager>(provider => new KnokManager(Configuration.Load<CommonSettings>(),
                provider.GetRequiredService<IKnokSettings>(), provider.GetRequiredService<IKnokDao>(),
                provider.GetRequiredService < ITagDao>(), provider.GetRequiredService < IImageManager>(),
                provider.GetRequiredService<IKnokPricelManager>(),
                provider.GetRequiredService<IGeneralManager>()));
            services.AddSingleton<IUidManager, UidManager>();
            services.AddSingleton<IImageManager, ImageManager>();
            services.AddSingleton<IUserImageManager, ImageManager>();
            services.AddSingleton<IDictionary<int, IExchange>>(provider => new Dictionary<int, IExchange>() {
                { 1, new Core.Exchanges.BitfinexExchange(Configuration.Load<Core.Exchanges.ExchangeSettings>("Bitfinex")) },
                { 2, new Core.Exchanges.BittrexExchange(Configuration.Load<Core.Exchanges.ExchangeSettings>("Bittrex")) },
                { 3, new Core.Exchanges.BinanceExchange(Configuration.Load<Core.Exchanges.ExchangeSettings>("Binance")) },
                { 4, new Core.Exchanges.CexExchange(Configuration.Load<Core.Exchanges.ExchangeSettings>("Cex")) },
                { 5, new Core.Exchanges.HUOBIExchange(Configuration.Load<Core.Exchanges.ExchangeSettings>("HUOBI")) },
                { 6, new Core.Exchanges.OKExExchange(Configuration.Load<Core.Exchanges.ExchangeSettings>("OKEx")) }
            });
            services.AddSingleton<IExchangeManager, ExchangeManager>();
            services.AddSingleton<IAccountTransactionManager, AccountTransactionManager>();

            //-- Services configs -----------------------------------------------------------------
            services.AddSingleton<ISmsService>(provider => new PlivoSmsService(provider.GetRequiredService<ILogger<PlivoSmsService>>(), Configuration.Load<PlivoSmsSettings>()));
            services.AddSingleton<IPhoneService>(provider => new TwilioPhoneService(provider.GetRequiredService<ILogger<TwilioPhoneService>>(), Configuration.Load<TwilioPhoneSettings>()));
            services.AddSingleton<IEmailService>(provider => new AwsEmailService(provider.GetRequiredService<ILogger<AwsEmailService>>(), Configuration.Load<AwsEmailSettings>()));
            services.AddSingleton<ISignatureService>(provider => new DocuSigneSignatureService(provider.GetRequiredService<ILogger<DocuSigneSignatureService>>(), Configuration.Load<DocuSigneSignatureSettings>()));
            services.AddSingleton<IHostedService>(provider => new RefreshExchangesHostedService(Configuration.Load<CommonSettings>(), provider.GetRequiredService<ILogger<RefreshExchangesHostedService>>(), provider.GetRequiredService<IExchangeManager>()));

            //-- Swagger API documentation config -------------------------------------------------
            services.AddSwaggerGen(o =>
            {
                //o.SchemaFilter<ApiSchemaFilter>();
                o.OperationFilter<ApiSwaggerOperationFilter>();
                o.SwaggerDoc(SwaggerDocumentName, Configuration.Load<Info>("SwaggerInfo"));
            });

            //TODO: services.AddCustomHeaders(); 
            //https://github.com/andrewlock/NetEscapades.AspNetCore.SecurityHeaders
            //https://jeremylindsayni.wordpress.com/author/jeremylindsayni/

        }

        private const string SwaggerDocumentName = "v1";

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
        public void Configure(ILoggerFactory loggerFactory, IApplicationBuilder app)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
            //app.UseApiJwt(Configuration, SecurityKey);
            app.UseAuthentication();
            app.UseMvc();
            app.UseSwagger(o => o.RouteTemplate = "spec/{documentName}/swagger.json");
            app.UseSwaggerUI(o =>
            {
                o.RoutePrefix = "spec/swagger";
                o.SwaggerEndpoint($"/spec/{SwaggerDocumentName}/swagger.json", "Knoks API");
            });
        }
    }
}