using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Knot.Modules.Admin;
using Knot.Modules.Admin.Presentation.Endpoints;
using Knot.Modules.Auth;
using Knot.Modules.Auth.Infrastructure.Persistence;
using Knot.Modules.Auth.Presentation.Endpoints;
using Knot.Modules.Conversations;
using Knot.Modules.Conversations.Infrastructure.Persistence;
using Knot.Modules.Conversations.Infrastructure.SignalR;
using Knot.Modules.Conversations.Presentation.Endpoints;
using Knot.Modules.Federation;
using Knot.Modules.Federation.Presentation.Endpoints;
using Knot.Modules.Klipy;
using Knot.Modules.Klipy.Presentation.Endpoints;
using Knot.Modules.Messaging;
using Knot.Modules.Profiles;
using Knot.Modules.Profiles.Presentation.Endpoints;
using Knot.Modules.Relations;
using Knot.Modules.Relations.Presentation.Endpoints;
using Knot.Modules.Settings;
using Knot.Modules.Settings.Presentation.Endpoints;
using Knot.Modules.Storage;
using Knot.Modules.Storage.Presentation.Endpoints;
using Knot.Modules.Stories;
using Knot.Modules.Stories.Presentation.Endpoints;
using Knot.Modules.TelegramImport;
using Knot.Modules.TelegramImport.Presentation.Endpoints;
using Knot.Modules.WebRtc;
using Knot.Modules.WebRtc.Presentation.Endpoints;
using Knot.Shared.Infrastructure;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using MediatR;



JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

var builder = WebApplication.CreateBuilder(args);

// Регистрация модулей
// Маппинг стандартных переменных окружения в иерархию .NET
var envMappings = new Dictionary<string, string?>
{
    ["ConnectionStrings:DefaultConnection"] = builder.Configuration["DATABASE_URL"] ?? builder.Configuration.GetConnectionString("DefaultConnection") ?? "Host=localhost;Database=knot;Username=postgres;Password=postgres",
    ["ConnectionStrings:MongoConnection"] = builder.Configuration["MONGO_CONNECTION"],
    ["Jwt:Secret"] = builder.Configuration["JWT_SECRET"],
    ["Jwt:Issuer"] = builder.Configuration["JWT_ISSUER"],
    ["Jwt:Audience"] = builder.Configuration["JWT_AUDIENCE"],
    ["WebRtc:TurnUrl"] = builder.Configuration["TURN_URL"],
    ["WebRtc:TurnUsername"] = builder.Configuration["TURN_USERNAME"],
    ["WebRtc:TurnPassword"] = builder.Configuration["TURN_PASSWORD"],
    ["KNOT_MASTER_ENCRYPTION_KEY"] = builder.Configuration["KNOT_MASTER_ENCRYPTION_KEY"],
    ["S3_ENDPOINT"] = builder.Configuration["S3_ENDPOINT"],
    ["S3_ACCESS_KEY"] = builder.Configuration["S3_ACCESS_KEY"],
    ["S3_SECRET_KEY"] = builder.Configuration["S3_SECRET_KEY"],
    ["S3_BUCKET"] = builder.Configuration["S3_BUCKET"],
    ["KNOT_ADMIN_USER"] = builder.Configuration["KNOT_ADMIN_USER"],
    ["KNOT_ADMIN_PASSWORD"] = builder.Configuration["KNOT_ADMIN_PASSWORD"]
};

// Добавляем только те, что реально заданы в ENV
builder.Configuration.AddInMemoryCollection(
    envMappings.Where(kv => !string.IsNullOrEmpty(kv.Value))
               .ToDictionary(kv => kv.Key, kv => kv.Value));

builder.Services.AddAuthModule(builder.Configuration);
builder.Services.AddSettingsModule(builder.Configuration);
builder.Services.AddMessagingModule(builder.Configuration);
builder.Services.AddConversationsModule(builder.Configuration);
builder.Services.AddProfilesModule(builder.Configuration);
builder.Services.AddRelationsModule(builder.Configuration);
builder.Services.AddStorageModule(builder.Configuration);
builder.Services.AddStoriesModule(builder.Configuration);
builder.Services.AddKlipyModule();
builder.Services.AddAdminModule();
builder.Services.AddWebRtcModule();
builder.Services.AddTelegramImportModule();
builder.Services.AddSharedInfrastructure(builder.Configuration);

// CQRS / MediatR для команд в Host (например, AdminController)
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(
    typeof(Program).Assembly,
    typeof(Knot.Modules.Settings.DependencyInjection).Assembly,
    typeof(Knot.Modules.Admin.DependencyInjection).Assembly,
    typeof(Knot.Modules.Messaging.DependencyInjection).Assembly,
    typeof(Knot.Modules.Conversations.DependencyInjection).Assembly,
    typeof(Knot.Modules.Stories.DependencyInjection).Assembly,
    typeof(Knot.Modules.Klipy.DependencyInjection).Assembly,
    typeof(Knot.Modules.Relations.DependencyInjection).Assembly,
    typeof(Knot.Modules.WebRtc.DependencyInjection).Assembly,
    typeof(Knot.Modules.TelegramImport.DependencyInjection).Assembly,
    typeof(Knot.Modules.Auth.Infrastructure.Persistence.AuthDbContext).Assembly,
    typeof(Knot.Modules.Profiles.DependencyInjection).Assembly
));

// Настройка CORS
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        var originsFromConfig = builder.Configuration["Cors:Origins"];
        var domain = builder.Configuration["DOMAIN"];

        var origins = !string.IsNullOrEmpty(originsFromConfig)
            ? originsFromConfig.Split(',')
            : (!string.IsNullOrEmpty(domain) ? new[] { $"https://{domain}" } : new[] { "*" });

        var corsBuilder = policy.WithOrigins(origins)
                            .AllowAnyHeader()
                            .AllowAnyMethod();

        if (origins.Contains("*"))
        {
            // The CORS protocol does not allow specifying a wildcard (any) origin and credentials at the same time.
        }
        else
        {
            corsBuilder.AllowCredentials();
        }
    });
});

// Настройка Swagger/OpenAPI
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Настройка маршрутизации
builder.Services.AddRouting(options =>
{
    options.LowercaseUrls = true;
    options.LowercaseQueryStrings = true;
});

builder.Services.AddMemoryCache();
builder.Services.AddHttpClient();

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});

builder.Services.AddSignalR()
    .AddJsonProtocol(options =>
    {
        options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter());
    });

builder.Services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();

// Настройка JWT Аутентификации
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"]!))
        };

        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;
            }
        };
    });

builder.Services.AddAuthorization();

var app = builder.Build();

// Применяем миграции при старте
using (var scope = app.Services.CreateScope())
{
    var identityDb = scope.ServiceProvider.GetRequiredService<AuthDbContext>();
    await identityDb.Database.MigrateAsync();

    var chatsDb = scope.ServiceProvider.GetRequiredService<Knot.Modules.Conversations.Infrastructure.Persistence.ChatsDbContext>();
    await chatsDb.Database.MigrateAsync();

    var systemDb = scope.ServiceProvider.GetRequiredService<Knot.Shared.Infrastructure.Persistence.SystemDbContext>();
    await systemDb.Database.MigrateAsync();

    var storiesDb = scope.ServiceProvider.GetRequiredService<Knot.Modules.Stories.Infrastructure.Database.StoriesDbContext>();
    await storiesDb.Database.MigrateAsync();

    var relationsDb = scope.ServiceProvider.GetRequiredService<Knot.Modules.Relations.Infrastructure.Persistence.RelationsDbContext>();
    await relationsDb.Database.MigrateAsync();

    // Set Encryption Service for MongoDB serializers
    var encryptionService = scope.ServiceProvider.GetRequiredService<Knot.Shared.Kernel.Security.IEncryptionService>();
    Knot.Modules.Messaging.Infrastructure.Persistence.Mongo.EncryptedStringSerializer.EncryptionService = encryptionService;

    // Initialize Global Settings Cache
    var settingsService = scope.ServiceProvider.GetRequiredService<Knot.Contracts.Settings.Application.Abstractions.ISettingsService>();
    if (settingsService is Knot.Modules.Settings.Infrastructure.Configuration.SettingsService concreteSettings)
    {
        concreteSettings.Initialize();
    }

    // Sync user replicas for Relations module on startup
    var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
    await mediator.Send(new Knot.Modules.Relations.Application.Contacts.SyncReplicasCommand());
}

// Настройка конвейера запросов
app.UseCors();

app.UseMiddleware<Knot.Shared.Infrastructure.Middleware.ExceptionHandlingMiddleware>();
app.UseMiddleware<Knot.Shared.Infrastructure.Middleware.AdminAuthMiddleware>();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// app.UseHttpsRedirection();

// Не раздаем статические файлы, так как теперь используем MinIO

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

// Регистрация эндпоинтов
app.MapAuthEndpoints();
app.MapStoriesEndpoints();
app.MapContactsEndpoints();
app.MapSettingsEndpoints();
app.MapProfilesEndpoints();
app.MapFederationEndpoints();
app.MapKlipyEndpoints();
app.MapChatsEndpoints();
app.MapMessagesEndpoints();
app.MapAdminEndpoints();
app.MapFilesEndpoints();
app.MapWebRtcEndpoints();
app.MapTelegramImportEndpoints();

// Добавляем SignalR хабы
app.MapHub<ChatHub>("/hubs/chat");

app.Run();

public class CustomUserIdProvider : IUserIdProvider
{
    public string? GetUserId(HubConnectionContext connection)
    {
        return connection.User?.FindFirstValue("sub") ?? connection.User?.FindFirstValue(ClaimTypes.NameIdentifier);
    }
}

