using AsbCloudApp.Data.GTR;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using AsbCloudApp.IntegrationEvents;
using AsbCloudApp.IntegrationEvents.Interfaces;
using AsbCloudApp.Services.Notifications;
using AsbCloudInfrastructure.Services.Email;
using AsbCloudWebApi.SignalR;
using AsbCloudWebApi.SignalR.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Any;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace AsbCloudWebApi
{
    public static class DependencyInjection
    {
        public static void AddSwagger(this IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                c.MapType<TimeSpan>(() => new OpenApiSchema { Type = "string", Example = new OpenApiString("0.00:00:00") });
                c.MapType<DateOnly>(() => new OpenApiSchema { Type = "string", Format = "date" });
                c.MapType<JsonValue>(() => new OpenApiSchema
                {
                    AnyOf = new OpenApiSchema[]
                    {
                        new OpenApiSchema {Type = "string", Format = "string" },
                        new OpenApiSchema {Type = "number", Format = "int32" },
                        new OpenApiSchema {Type = "number", Format = "float" },
                    }
                });

                c.CustomOperationIds(e =>
                {
                    return $"{e.ActionDescriptor.RouteValues["action"]}";
                });

                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ASB cloud web api", Version = "v1" });
                c.SwaggerDoc("signalr", new OpenApiInfo { Title = "SignalR client methods", Version = "signalr" });
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = @"JWT Authorization header using the Bearer scheme. Enter 'Bearer' [space] and then your token in the text input below. Example: 'Bearer 12345abcdef'",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                    Scheme = "Bearer",
                });

                c.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            },
                            Scheme = "oauth2",
                            Name = "Bearer",
                            In = ParameterLocation.Header,
                        },
                        new List<string>()
                    }
                });

                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                var includeControllerXmlComment = true;
                c.IncludeXmlComments(xmlPath, includeControllerXmlComment);
                c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "AsbCloudApp.xml"), includeControllerXmlComment);

                c.AddSignalRSwaggerGen(options => {
                    options.DisplayInDocument("signalr");
                    options.UseHubXmlCommentsSummaryAsTagDescription = true;
                    options.UseHubXmlCommentsSummaryAsTag = true;
                    options.UseXmlComments(xmlPath);
                });
            });
        }

        public static void AddJWTAuthentication(this IServiceCollection services)
        {
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.RequireHttpsMetadata = false;
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidIssuer = AuthService.issuer,
                        ValidateAudience = true,
                        ValidAudience = AuthService.audience,
                        ValidateLifetime = true,
                        IssuerSigningKey = AuthService.securityKey,
                        ValidateIssuerSigningKey = true,
                    };

                    options.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = context =>
                        {
                            var accessToken = context.Request.Query["access_token"];

                            var path = context.HttpContext.Request.Path;
                            if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
                            {
                                context.Token = accessToken;
                            }

                            return Task.CompletedTask;
                        },
                        OnTokenValidated = context =>
                        {
                            var idUser = context.Principal?.GetUserId();
                            if (idUser is null)
                            {
                                context.Fail("idUser is null");
                                return Task.CompletedTask;
                            }

                            var userService = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>();
                            var user = userService.GetOrDefault(idUser.Value);

                            if (user is null)
                            {
                                context.Fail("user is null");
                            }
                            else if (user.IdState != User.ActiveStateId)
                            {
                                context.Fail("user is not active");
                            }

                            return Task.CompletedTask;
                        }
                    };
                });
        }

        public static void AddNotificationTransportServices(this IServiceCollection services)
        {
            services.AddTransient<INotificationTransportService, SignalRNotificationTransportService>();
            services.AddTransient<INotificationTransportService, EmailNotificationTransportService>();

            services.AddTransient<NotificationPublisher>();
        }

        public static void AddIntegrationEvents(this IServiceCollection services) => services
            .AddTransient<IIntegrationEventHandler<UpdateWellInfoEvent>, WellInfoHub>();
    }
}