using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Hangfire;
using Hangfire.PostgreSql;
using MEU.API.Config;
using MEU.API.MiddleWare;
//using MEU.API.Models;
//using MEU.API.Services.CronJobs;
//using MEU.API.Services.NotificationHub;
using MEU.API.Utils;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; 
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using NLog;
using Sieve.Models;
using Sieve.Services;

namespace MEU.API
{
    public class Startup
    {
        private IWebHostEnvironment CurrentEnvironment { get; set; }
        public Startup(Microsoft.AspNetCore.Hosting.IWebHostEnvironment env)
        {   
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

            Configuration = builder.Build();

            CurrentEnvironment = env;
        }
        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            if (CurrentEnvironment.IsDevelopment())
            {
                ConfigEnvironment.load("development");
            }
            else if (CurrentEnvironment.IsStaging())
            {
                ConfigEnvironment.load("staging");
            }
            else
            {
                ConfigEnvironment.load("production");
            }
           

            services.AddHangfire(x => x.UsePostgreSqlStorage(ConfigEnvironment.CONECTION_STRING));
            services.AddHangfireServer();
            
            //services.AddCronJob<CronJobSyncInOutLog>(c =>
            //{
            //    c.TimeZoneInfo = TimeZoneInfo.Local;
            //    c.CronExpression = @"1/1 * * * *";
            //});


            services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
            {
                builder
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .WithOrigins("http://localhost:4200",
                    "https://localhost:4200",
                    "http://localhost:4000",
                    "https://localhost:4000",
                    "http://*.meu-solutions.com",
                    "https://*.meu-solutions.com",
                    "http://localhost:8000", 
                    "https://localhost:8000",
                    "http://localhost:8080",
                    "https://localhost:8080",
                    "http://27.74.255.96:90",                    
                    "http://27.74.255.96:8090", 
                    "https://27.74.255.96:8090", 
                    "http://192.168.0.188:8090", 
                    "https://192.168.0.188:8090")
                            .AllowAnyHeader()
                            .AllowAnyMethod()
                            .AllowCredentials();
            }));
            services.Configure<IISServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

            // If using Kestrel:
            services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
                options.Limits.MaxRequestBodySize = null;
            });

            services.Configure<FormOptions>(options =>
            {
                options.ValueLengthLimit = int.MaxValue;
                options.MultipartBodyLengthLimit = int.MaxValue; // if don't set default value is: 128 MB
                options.MultipartHeadersLengthLimit = int.MaxValue;
            });

            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;                
            }).AddJwtBearer(x =>
            {
                x.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"];

                        // If the request is for our hub...
                        var path = context.HttpContext.Request.Path;
                        if (!string.IsNullOrEmpty(accessToken) &&
                            (path.StartsWithSegments("/api/notificationhub")))
                        {
                            // Read the token out of the query string
                            context.Token = accessToken;
                        }
                        return Task.CompletedTask;
                    }
                };
                #region verify user is existed


                //x.Events = new JwtBearerEvents()
                //{
                //    OnAuthenticationFailed = context =>
                //    {
                //        if (context.Exception is SecurityTokenExpiredException)
                //        {
                //            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                //            context.Response.ContentType = "application/json; charset=utf-8";
                //            var result = JsonConvert.SerializeObject(new { status = "Unauthorized", message = "Token expired." });
                //            context.Response.WriteAsync(result).Wait();
                //            return Task.CompletedTask;
                //        }
                //        else
                //        {
                //            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                //            context.Response.ContentType = "application/json; charset=utf-8";
                //            var result = JsonConvert.SerializeObject(new { status = "Unauthorized", message = "An error occurred processing your authentication." });
                //            context.Response.WriteAsync(result).Wait();
                //            return Task.CompletedTask;
                //        }

                //    }
                //};

                //x.Events = new JwtBearerEvents
                //{
                //    OnTokenValidated = context =>
                //    {
                //        var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
                //        var userId = int.Parse(context.Principal.Identity.Name);
                //        var user = userService.GetById(userId);
                //        if (user == null)
                //        {
                //            // return unauthorized if user no longer exists
                //            context.Fail("Unauthorized");
                //        }
                //        return Task.CompletedTask;
                //    }
                //};
                #endregion
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(ConfigEnvironment.TOKEN_ENCRYPTED_KEY)),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
                
            });

            services.AddSignalR(o =>
            {
                o.EnableDetailedErrors = true;
                o.MaximumReceiveMessageSize = 102400000;
            });
            //services.AddSingleton<NotificationHub>();

            services.AddControllers()
            .AddMvcOptions(options =>
            {
                options.ModelBinderProviders.Insert(0, new DateTimeModelBinderProvider());
            });

            services.Configure<FormOptions>(x =>
            {
                x.ValueLengthLimit = int.MaxValue;
                x.MultipartBodyLengthLimit = int.MaxValue; // In case of multipart
            });
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddScoped<SieveProcessor>();
            services.Configure<SieveOptions>(Configuration.GetSection("Sieve"));
            services.AddControllers().AddNewtonsoftJson(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });

            //services.AddDbContext<meuerpContext>(opt => opt.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));

            // Disable swagger on Production env
            if (!CurrentEnvironment.IsProduction())
            {
                // Register the Swagger generator, defining 1 or more Swagger documents
                services.AddSwaggerGen(c =>
                {
                    c.SchemaFilter<ApplyCustomSchemaFilters>();
                    c.SwaggerDoc("v1", new OpenApiInfo { Title = "MEU ERP API", Version = "v1" });
                    // Set the comments path for the Swagger JSON and UI.
                    try
                    {
                        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                        var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                        c.IncludeXmlComments(xmlPath);
                    }
                    catch (Exception)
                    {


                    }
                    //c.DocumentFilter<SwaggerDocumentFilter>();
                    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                    {
                        In = ParameterLocation.Header,
                        Description = "Usage: Bearer $token",
                        Name = "Authorization",
                        Type = SecuritySchemeType.ApiKey
                    });

                    c.AddSecurityRequirement(new OpenApiSecurityRequirement {
                   {
                     new OpenApiSecurityScheme
                     {
                       Reference = new OpenApiReference
                       {
                         Type = ReferenceType.SecurityScheme,
                         Id = "Bearer"
                       }
                      },
                      new string[] { }
                    }
                });
                });
                //services.AddSwaggerGenNewtonsoftSupport();
            }
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            
            AppHttpContext.Services = app.ApplicationServices;

            IdentityModelEventSource.ShowPII = true;

            app.UseHangfireDashboard();

            //Delare static physical File provider
            app.UseStaticFiles(new StaticFileOptions
            {
                FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")),
                RequestPath = "/StaticFiles",
                OnPrepareResponse = context =>
                {
                    context.Context.Response.Headers["Access-Control-Allow-Origin"] = "*";
                }
            });

            //Create public folder if not exist
            //if (!Directory.Exists(MEU.FILE.Config.rootPath + ConfigEnvironment.STORAGE_PUBLIC))
            //{
            //    Directory.CreateDirectory(MEU.FILE.Config.rootPath + ConfigEnvironment.STORAGE_PUBLIC);
            //}

            //Declare statis file path
            app.UseStaticFiles(new StaticFileOptions
            {
                //FileProvider = new PhysicalFileProvider(MEU.FILE.Config.rootPath + ConfigEnvironment.STORAGE_PUBLIC),
                RequestPath = "/public",
                ServeUnknownFileTypes = true,
                OnPrepareResponse = context =>
                {
                    context.Context.Response.Headers["Access-Control-Allow-Origin"] = "*";
                }
            });

            // Disable swagger on Production env
            if (!env.IsProduction())
            {
                // Enable middleware to serve generated Swagger as a JSON endpoint.
                app.UseSwagger();

                // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
                // specifying the Swagger JSON endpoint.
                app.UseSwaggerUI(c =>
                {
                    c.InjectJavascript("/StaticFiles/js/jquery-3.4.1.min.js");
                    c.InjectJavascript("/StaticFiles/js/swagger-auth-custom.js");
                    c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "MEU ERP API V1");
                });
            }
          
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //Localization
            var supportedCultures = new[] { new CultureInfo("vi-VN") };
            app.UseRequestLocalization(new RequestLocalizationOptions
            {
                DefaultRequestCulture = new RequestCulture("vi-VN"),
                SupportedCultures = supportedCultures,
                SupportedUICultures = supportedCultures
            });

            //app.UseHttpsRedirection();
            app.UseRouting();
            app.UseCors("MyPolicy");

            app.UseAuthentication();
            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add("X-Xss-Protection", "1");
                await next();
            });

            app.Use(async (context, next) => {
                // Check if the request endpoint is an upload API? If yes, increase the bufferLimit to 500Mb to avoid exception
                if (context.Request.Path.Value.ToString().ToLower().Contains("upload"))
                {
                    context.Request.EnableBuffering(bufferThreshold: 1024 * 45, bufferLimit: 1024 * 500000);
                }
                else
                {
                    context.Request.EnableBuffering(bufferThreshold: 1024 * 45, bufferLimit: 1024 * 100);
                }
                await next();
            });

            app.UseAuthorization();

            app.UseMiddleware(typeof(SignatureMiddleware));

            app.UseMiddleware(typeof(AddErrorLogToHeaderMiddelware));

            //Return exception content when 500
            //app.UseMiddleware(typeof(ErrorHandlingMiddleware));

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                //endpoints.MapHub<NotificationHub>("/api/notificationhub"); 
            });

        }
    }
}