
using FMSAdmin.Data;
using FMSAdmin.Helpers;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using FMSAdmin.Services;
using System.Linq;
using Microsoft.Extensions.WebEncoders;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Quartz.Impl;
using Quartz;
using Quartz.Logging;
using System;
using Microsoft.AspNetCore.StaticFiles;
using GrapeCity.ActiveReports.Aspnetcore.Viewer;
using System.IO;

namespace FMSAdmin {
    public class Startup {
        public Startup(IConfiguration configuration, IWebHostEnvironment env) {
            Environment = env;
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        public IWebHostEnvironment Environment { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services) {
            services.Configure<WebEncoderOptions>(options => {
                options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All); // 한글이 인코딩되는 문제 해결
            });

            services.Configure<ApiBehaviorOptions>(options => {
                options.SuppressModelStateInvalidFilter = true;
            });

            // CORS처리
            services.AddCors(options => {
                options.AddDefaultPolicy(
                    builder => builder
                        .AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                    );
            });

            // 서비스들 추가
            foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.Namespace == "FMSAdmin.Services")) {
                services.AddScoped(type);
            }
            foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.Namespace == "FMSApp.Services")) {
                services.AddScoped(type);
            }

            // 리파지토리 추가
            foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.Namespace == "FMSApp.Repositories")) {
                services.AddScoped(type);
            }

            // 기타 추가
            services.AddScoped<StorageHelper>();
            services.AddScoped<QrCodeHelper>();
            services.AddScoped<PushHelper>();

            services.AddControllers(options =>
                options.Filters.Add(new HttpResponseExceptionFilter())
            )/*.AddJsonOptions(options => { // JSON 카멜케이스 - DictionaryKeyPolicy (ModelState Error)는 반영안됨
                options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
                options.JsonSerializerOptions.PropertyNamingPolicy = null;
                options.JsonSerializerOptions.DictionaryKeyPolicy = null;
            });
            기본 JSON모듈대신 Newtonsoft 이용. (파라메터 바인딩시에 int 자동캐스팅이 별도작업없이가능)*/
            .AddNewtonsoftJson(options => {
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                //options.SerializerSettings.MaxDepth = 5;
            });

            if (Environment.IsDevelopment()) {
                var sever = Configuration.GetConnectionString("Server");
                if (sever != null && sever == "Sqlite") {
                    services.AddDbContext<FMSContext>(
                        options => options.UseLazyLoadingProxies().UseSqlite(Configuration.GetConnectionString("SqliteContext")));
                } else {
                    services.AddDbContext<FMSContext>(
                        options => options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("SqlServerContext")));
                }
            } else {
                // LazyLoading 추가
                services.AddDbContext<FMSContext>(
                    options => options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("SqlServerContext")));
            }

            // AppSettings 객체로 가져오기
            var appSettingsSection = Configuration.GetSection("AppSettings");
            services.Configure<AppSettings>(appSettingsSection);

            // JWT 시크릿키 설정
            var appSettings = appSettingsSection.Get<AppSettings>();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);

            // JWT 인증
            services.AddAuthentication(x => {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x => {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });

            // AR 세팅
            services.AddReporting();

            // Job Scheduler 추가
            bool runScheduler = false;
            if (appSettings.Scheduler != "off") { // 설정으로 실행 여부 관리
                runScheduler = true;
            }

            if (runScheduler) {
                LogProvider.SetCurrentLogProvider(new QuartzLogProvider());
                foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.Namespace == "FMSAdmin.QuartzSchedule.Jobs")) {
                    services.AddScoped(type);
                }
                foreach (var type in Assembly.GetExecutingAssembly().GetTypes().Where(x => x.Namespace == "FMSAdmin.QuartzSchedule.Schedulers")) {
                    services.AddScoped(type);
                }
                services.AddSingleton<QuartzJobRunner>();
                services.AddSingleton<QuartzJobFactory>();
                services.AddScoped<QuartzSchedule.Startup>();
                services.AddScoped<QuartzSchedulerListener>();
                var scheduler = StdSchedulerFactory.GetDefaultScheduler().GetAwaiter().GetResult();
                services.AddSingleton(scheduler);
                services.AddHostedService<QuartzHostedService>();
            }

            // iis
            services.Configure<IISServerOptions>(options => {
                options.AutomaticAuthentication = false;
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) {
            ApplicationLogging.ConfigureLogger(loggerFactory);

            // static 클래스 로거설정
            Util._logger = ApplicationLogging.CreateLogger("Util");
            EntityHelper._logger = ApplicationLogging.CreateLogger("EntityHelper");

            // 기본 익셉션 핸들러 설정
            if (env.IsDevelopment()) {
                app.UseExceptionHandler("/error-local-development");
            } else {
                app.UseExceptionHandler("/error");
            }

            // 추가 static files 설정
            var provider = new FileExtensionContentTypeProvider();
            provider.Mappings[".dwg"] = "application/acad";
            provider.Mappings[".plist"] = "text/xml";
            provider.Mappings[".ipa"] = "application/octet-stream";

            //app.UseStaticFiles();
            app.UseStaticFiles(new StaticFileOptions() {
                ContentTypeProvider = provider
            });

            app.UseRouting();

            app.UseCors();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints => {
                endpoints.MapControllers();
            });

            // AR 추가
            app.UseReporting(settings => {
                settings.UseCompression = true;
                settings.UseFileStore(new DirectoryInfo("Reports")); // Reports 디렉토리
            });
        }
    }
}