using GmRelay.DiscordBot;
using GmRelay.DiscordBot.Features.Sessions;
using GmRelay.DiscordBot.Infrastructure;
using GmRelay.DiscordBot.Infrastructure.Discord;
using GmRelay.DiscordBot.Infrastructure.Health;
using GmRelay.DiscordBot.Infrastructure.Logging;
using GmRelay.Shared.Features.Confirmation.HandleRsvp;
using GmRelay.Shared.Features.Confirmation.SendConfirmation;
using GmRelay.Shared.Features.Notifications;
using GmRelay.Shared.Features.Reminders.SendJoinLink;
using GmRelay.Shared.Features.Reminders.SendOneHourReminder;
using GmRelay.Shared.Features.Sessions.CreateSession;
using GmRelay.Shared.Features.Sessions.RescheduleSession;
using GmRelay.Shared.Infrastructure.Scheduling;
using GmRelay.Shared.Platform;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using NetCord;
using NetCord.Gateway;
using NetCord.Hosting.Gateway;
using NetCord.Hosting.Services;
using NetCord.Hosting.Services.ApplicationCommands;
using NetCord.Hosting.Services.ComponentInteractions;
using NetCord.Services.ApplicationCommands;
using NetCord.Services.ComponentInteractions;
using Npgsql;

var builder = Host.CreateApplicationBuilder(args);

builder.AddServiceDefaults();

var discordOptions = builder.Configuration
    .GetRequiredSection("Discord")
    .Get<DiscordOptions>() ?? new DiscordOptions();
discordOptions.Validate();

builder.Services.AddSingleton(discordOptions);

builder.Logging.AddConsole();

builder.Services.AddSingleton<NpgsqlDataSource>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
    var connectionString = config.GetConnectionString("gmrelaydb")
        ?? throw new InvalidOperationException(
            "ConnectionStrings:gmrelaydb is required. Set via environment variable ConnectionStrings__gmrelaydb.");

    var logger = loggerFactory.CreateLogger("GmRelay.DiscordBot.Startup");
    logger.LogInformation(
        "Configured PostgreSQL data source with connection string {ConnectionString}",
        SecretRedactor.RedactConnectionString(connectionString));

    return NpgsqlDataSource.Create(connectionString);
});

builder.Services.AddSingleton<DiscordPermissionChecker>();
builder.Services.AddSingleton<DiscordListSessionsHandler>();
builder.Services.AddSingleton<DiscordDeleteSessionHandler>();
builder.Services.AddSingleton<DiscordNewSessionHandler>();
builder.Services.AddSingleton<DiscordRescheduleHandler>();
builder.Services.AddSingleton<GmRelay.Shared.Features.Sessions.RescheduleSession.HandleRescheduleVoteHandler>();
builder.Services.AddSingleton<DiscordRescheduleVoteHandler>();
builder.Services.AddSingleton<IScheduleMessageUpdateLock, ScheduleMessageUpdateLock>();
builder.Services.AddSingleton<JoinSessionHandler>();
builder.Services.AddSingleton<LeaveSessionHandler>();
builder.Services.AddSingleton<DiscordInteractionReplyCache>();
builder.Services.AddSingleton<IPlatformMessenger, DiscordPlatformMessenger>();
builder.Services.AddSingleton<ISystemClock, SystemClock>();
builder.Services.AddSingleton(new PlatformSchedulerOptions(PlatformKind.Discord));
builder.Services.AddSingleton<ISessionTriggerStore, DbSessionTriggerStore>();
builder.Services.AddSingleton<PlatformDirectNotificationSender>();
builder.Services.AddSingleton<SendConfirmationHandler>();
builder.Services.AddSingleton<ISendConfirmationHandler>(sp => sp.GetRequiredService<SendConfirmationHandler>());
builder.Services.AddSingleton<SendJoinLinkHandler>();
builder.Services.AddSingleton<ISendJoinLinkHandler>(sp => sp.GetRequiredService<SendJoinLinkHandler>());
builder.Services.AddSingleton<SendOneHourReminderHandler>();
builder.Services.AddSingleton<ISendOneHourReminderHandler>(sp => sp.GetRequiredService<SendOneHourReminderHandler>());
builder.Services.AddSingleton<HandleRsvpHandler>();
builder.Services.AddSingleton<RescheduleVotingFinalizer>();
builder.Services.AddHostedService<SessionSchedulerService>();
builder.Services.AddHostedService<DiscordRescheduleVotingDeadlineService>();
builder.Services.AddHostedService<DiscordHealthCheckHostedService>();

builder.Services
    .AddDiscordGateway(options =>
    {
        options.Token = discordOptions.Token;
        options.Intents = GatewayIntents.Guilds;
    })
    .AddApplicationCommands<SlashCommandInteraction, SlashCommandContext>()
    .AddComponentInteractions<ButtonInteraction, ButtonInteractionContext>()
    .AddGatewayHandlers(typeof(Program).Assembly);

var host = builder.Build();

host.AddSlashCommand("ping", "Checks whether GM-Relay Discord is online.", () => "Pong!");
host.AddModules(typeof(Program).Assembly);

await host.RunAsync();
