namespace GmRelay.DiscordBot.Features.Sessions;

using NetCord;
using NetCord.Rest;
using NetCord.Services.ApplicationCommands;

public class DiscordRescheduleCommand : ApplicationCommandModule<SlashCommandContext>
{
    private readonly DiscordRescheduleHandler _handler;
    private readonly ILogger<DiscordRescheduleCommand> _logger;

    public DiscordRescheduleCommand(DiscordRescheduleHandler handler, ILogger<DiscordRescheduleCommand> logger)
    {
        _handler = handler;
        _logger = logger;
    }

    [SlashCommand("reschedule", "Initiate reschedule voting for a session")]
    public async Task ExecuteAsync(
        [SlashCommandParameter(Name = "session", Description = "Session ID to reschedule")] string sessionIdText,
        [SlashCommandParameter(Name = "option1", Description = "First time option (YYYY-MM-DD HH:mm)")] string option1,
        [SlashCommandParameter(Name = "option2", Description = "Second time option (YYYY-MM-DD HH:mm)")] string option2,
        [SlashCommandParameter(Name = "option3", Description = "Third time option (optional)")] string? option3 = null,
        [SlashCommandParameter(Name = "deadline", Description = "Voting deadline (YYYY-MM-DD HH:mm)")] string deadline = "")
    {
        _logger.LogInformation(
            "reschedule called by user {UserId} ({UserType}) in guild {GuildId}",
            Context.User.Id,
            Context.User.GetType().Name,
            Context.Interaction.GuildId);

        var guildId = Context.Interaction.GuildId
            ?? throw new InvalidOperationException("This command can only be used in a guild.");

        var member = Context.User as GuildInteractionUser;
        if (member is null)
        {
            _logger.LogError("Context.User is not GuildInteractionUser. Actual type: {ActualType}", Context.User.GetType().Name);
            throw new InvalidOperationException("Guild member data not available in interaction.");
        }

        var resolvedPermissions = (ulong)member.Permissions;
        _logger.LogInformation("Resolved permissions for user {UserId}: {Permissions}", Context.User.Id, resolvedPermissions);

        ulong guildOwnerId = 0;
        try
        {
            var guild = await Context.Client.Rest.GetGuildAsync(guildId);
            guildOwnerId = guild.OwnerId;
            _logger.LogInformation("Guild owner id: {OwnerId}", guildOwnerId);
        }
        catch (RestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            _logger.LogWarning(
                ex,
                "Bot is not a REST member of guild {GuildId}; using resolved permissions from interaction payload",
                guildId);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error fetching guild {GuildId}", guildId);
        }

        if (!Guid.TryParse(sessionIdText, out var sessionId))
        {
            await Context.Interaction.SendResponseAsync(
                InteractionCallback.Message("❌ Некорректный ID сессии."));
            return;
        }

        var options = new List<string> { option1, option2 };
        if (!string.IsNullOrWhiteSpace(option3))
            options.Add(option3);

        var parsedOptions = new List<DateTimeOffset>();
        foreach (var opt in options)
        {
            var result = DiscordNewSessionHandler.ParseTimeInput(opt);
            if (!result.IsSuccess)
            {
                await Context.Interaction.SendResponseAsync(
                    InteractionCallback.Message($"❌ {opt}: {result.Error}"));
                return;
            }
            parsedOptions.Add(result.Value);
        }

        var deadlineResult = DiscordNewSessionHandler.ParseTimeInput(deadline);
        if (!deadlineResult.IsSuccess)
        {
            await Context.Interaction.SendResponseAsync(
                InteractionCallback.Message($"❌ Дедлайн: {deadlineResult.Error}"));
            return;
        }

        if (deadlineResult.Value >= parsedOptions.Min())
        {
            await Context.Interaction.SendResponseAsync(
                InteractionCallback.Message("❌ Дедлайн должен быть раньше первого варианта времени."));
            return;
        }

        // Defer the response to avoid Discord 3-second interaction timeout
        await Context.Interaction.SendResponseAsync(InteractionCallback.DeferredMessage());

        try
        {
            _logger.LogInformation("Initiating reschedule for session {SessionId} in guild {GuildId}", sessionId, guildId);

            var result = await _handler.HandleAsync(
                guildId: guildId.ToString(),
                channelId: Context.Channel!.Id.ToString(),
                userId: Context.User.Id,
                userDisplayName: Context.User.GlobalName ?? Context.User.Username,
                resolvedPermissions: resolvedPermissions,
                guildOwnerId: guildOwnerId,
                sessionId: sessionId,
                options: parsedOptions,
                deadline: deadlineResult.Value,
                CancellationToken.None);

            _logger.LogInformation("Reschedule voting started for session {SessionId}, proposal {ProposalId}", sessionId, result.ProposalId);

            await Context.Interaction.ModifyResponseAsync(message =>
            {
                message.Content = $"🗳 Голосование за перенос запущено! Дедлайн: {deadlineResult.Value:yyyy-MM-dd HH:mm} UTC.";
            });
        }
        catch (UnauthorizedAccessException ex)
        {
            _logger.LogWarning(ex, "Unauthorized reschedule attempt by user {UserId}", Context.User.Id);
            await Context.Interaction.ModifyResponseAsync(message =>
            {
                message.Content = $":no_entry: {ex.Message}";
            });
        }
        catch (InvalidOperationException ex)
        {
            _logger.LogWarning(ex, "Invalid reschedule request by user {UserId}", Context.User.Id);
            await Context.Interaction.ModifyResponseAsync(message =>
            {
                message.Content = $":warning: {ex.Message}";
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to initiate reschedule for session {SessionId}", sessionId);
            await Context.Interaction.ModifyResponseAsync(message =>
            {
                message.Content = ":boom: Ошибка при запуске голосования.";
            });
        }
    }
}
