using System.Text;
using Dapper;
using GmRelay.Shared.Domain;
using GmRelay.Shared.Platform;
using Microsoft.Extensions.Configuration;
using Npgsql;

namespace GmRelay.Shared.Features.Sessions.ExportCalendar;

internal sealed record CalendarSessionDto(Guid Id, string Title, DateTime ScheduledAt);

public sealed class ExportCalendarHandler(
    NpgsqlDataSource dataSource,
    IPlatformMessenger messenger,
    IConfiguration configuration)
{
    public async Task HandleAsync(ExportCalendarCommand command, CancellationToken cancellationToken)
    {
        await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);

        var sessions = await connection.QueryAsync<CalendarSessionDto>(
            @"SELECT s.id as Id, s.title as Title, s.scheduled_at as ScheduledAt"
            + " FROM sessions s"
            + " JOIN game_groups g ON s.group_id = g.id"
            + " WHERE g.platform = @Platform"
            + " AND g.external_group_id = @ExternalGroupId"
            + " AND s.status = @Planned"
            + " AND s.scheduled_at > NOW()"
            + " ORDER BY s.scheduled_at ASC",
            new { Platform = command.Group.Platform.ToString(), ExternalGroupId = command.Group.ExternalGroupId, Planned = SessionStatus.Planned });

        var sessionsList = sessions.ToList();

        if (sessionsList.Count == 0)
        {
            await messenger.SendGroupMessageAsync(
                command.Group,
                "📭 У этой группы нет запланированных сессий для экспорта.",
                cancellationToken);
            return;
        }

        var sb = new StringBuilder();
        sb.AppendLine("BEGIN:VCALENDAR");
        sb.AppendLine("VERSION:2.0");
        sb.AppendLine("PRODID:-//GM-Relay//TTRPG Schedule//EN");

        foreach (var s in sessionsList)
        {
            var dtStart = s.ScheduledAt.ToString("yyyyMMddTHHmmssZ");
            var dtEnd = s.ScheduledAt.AddHours(4).ToString("yyyyMMddTHHmmssZ");

            sb.AppendLine("BEGIN:VEVENT");
            sb.AppendLine($"UID:{s.Id}@gmrelay");
            sb.AppendLine($"DTSTAMP:{DateTime.UtcNow:yyyyMMddTHHmmssZ}");
            sb.AppendLine($"DTSTART:{dtStart}");
            sb.AppendLine($"DTEND:{dtEnd}");
            sb.AppendLine($"SUMMARY:{s.Title}");
            sb.AppendLine("END:VEVENT");
        }

        sb.AppendLine("END:VCALENDAR");

        var bytes = Encoding.UTF8.GetBytes(sb.ToString());

        // Create calendar subscription
        string? subscriptionUrl = null;
        var baseUrl = configuration["Web:BaseUrl"];
        var senderId = command.User.ExternalUserId;
        if (!string.IsNullOrWhiteSpace(baseUrl) && !string.IsNullOrWhiteSpace(senderId))
        {
            try
            {
                var token = Guid.NewGuid().ToString("N");
                var groupId = await connection.QueryFirstOrDefaultAsync<Guid?>(
                    @"SELECT id FROM game_groups WHERE platform = @Platform AND external_group_id = @ExternalGroupId",
                    new { Platform = command.Group.Platform.ToString(), ExternalGroupId = command.Group.ExternalGroupId });

                await connection.ExecuteAsync(
                    @"INSERT INTO calendar_subscriptions (id, token, user_platform, user_external_id, group_id, filter_type, created_at, expires_at)
                      VALUES (gen_random_uuid(), @token, @userPlatform, @userExternalId, @groupId, @filterType, now(), NULL)",
                    new { token, userPlatform = command.Group.Platform.ToString(), userExternalId = senderId, groupId, filterType = (int)CalendarSubscriptionFilter.SpecificGroup });

                subscriptionUrl = $"{baseUrl.TrimEnd('/')}/calendar/{token}.ics";
            }
            catch
            {
                // Non-critical: if subscription creation fails, still send the file
            }
        }

        var actions = subscriptionUrl is not null
            ? new[]
            {
                new PlatformMessageAction(
                    "calendar-subscription",
                    "🔗 Подписаться на календарь",
                    subscriptionUrl)
            }
            : Array.Empty<PlatformMessageAction>();

        await messenger.SendCalendarFileAsync(
            new PlatformCalendarFile(
                command.Group,
                "schedule.ics",
                bytes,
                "📅 <b>Ваш календарь игр!</b>\nОткройте файл на устройстве, чтобы добавить события в свой календарь.",
                actions),
            cancellationToken);
    }
}
