using GmRelay.Shared.Domain;

namespace GmRelay.Bot.Features.Sessions.CreateSession;

internal sealed record NewSessionParseResult(
    string? Title,
    string? Link,
    string? ImageUrl,
    int? MaxPlayers,
    IReadOnlyList<DateTimeOffset> ScheduledTimes,
    IReadOnlyList<string> PastTimeInputs,
    IReadOnlyList<string> InvalidTimeInputs,
    IReadOnlyList<string> InvalidSeatLimitInputs,
    IReadOnlyList<string> InvalidRecurringInputs)
{
    public bool IsValid =>
        !string.IsNullOrWhiteSpace(Title) &&
        !string.IsNullOrWhiteSpace(Link) &&
        ScheduledTimes.Count > 0 &&
        InvalidSeatLimitInputs.Count == 0 &&
        InvalidRecurringInputs.Count == 0;
}

internal static class NewSessionCommandParser
{
    private const int MaxRecurringSessionCount = 52;
    private const int MaxRecurringIntervalDays = 365;
    private const string TitlePrefix = "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435:";
    private const string TimePrefix = "\u0412\u0440\u0435\u043c\u044f:";
    private const string LinkPrefix = "\u0421\u0441\u044b\u043b\u043a\u0430:";
    private static readonly string[] ImagePrefixes =
    [
        "\u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0430:",
        "\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435:",
        "\u041e\u0431\u043b\u043e\u0436\u043a\u0430:"
    ];
    private static readonly string[] SeatLimitPrefixes =
    [
        "\u041c\u0435\u0441\u0442:",
        "\u041b\u0438\u043c\u0438\u0442:",
        "\u041c\u0430\u043a\u0441\u0438\u043c\u0443\u043c:"
    ];
    private static readonly string[] RecurringCountPrefixes =
    [
        "\u0418\u0433\u0440:",
        "\u0421\u0435\u0441\u0441\u0438\u0439:",
        "\u041f\u043e\u0432\u0442\u043e\u0440\u043e\u0432:"
    ];
    private static readonly string[] RecurringIntervalPrefixes =
    [
        "\u0428\u0430\u0433:",
        "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b:"
    ];

    public static NewSessionParseResult Parse(string? text, DateTimeOffset nowUtc)
    {
        string? title = null;
        string? link = null;
        string? imageUrl = null;
        int? maxPlayers = null;
        int? recurringCount = null;
        var recurringIntervalDays = 7;
        var scheduledTimes = new List<DateTimeOffset>();
        var pastTimeInputs = new List<string>();
        var invalidTimeInputs = new List<string>();
        var invalidSeatLimitInputs = new List<string>();
        var invalidRecurringInputs = new List<string>();

        foreach (var line in (text ?? string.Empty).Split('\n', StringSplitOptions.TrimEntries))
        {
            if (line.StartsWith(TitlePrefix, StringComparison.OrdinalIgnoreCase))
            {
                title = line[TitlePrefix.Length..].Trim();
                continue;
            }

            if (line.StartsWith(LinkPrefix, StringComparison.OrdinalIgnoreCase))
            {
                link = line[LinkPrefix.Length..].Trim();
                continue;
            }

            var imagePrefix = ImagePrefixes.FirstOrDefault(prefix =>
                line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
            if (imagePrefix is not null)
            {
                imageUrl = line[imagePrefix.Length..].Trim();
                continue;
            }

            var seatLimitPrefix = SeatLimitPrefixes.FirstOrDefault(prefix =>
                line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
            if (seatLimitPrefix is not null)
            {
                var seatLimitInput = line[seatLimitPrefix.Length..].Trim();
                if (int.TryParse(seatLimitInput, out var parsedMaxPlayers) && parsedMaxPlayers > 0)
                {
                    maxPlayers = parsedMaxPlayers;
                }
                else
                {
                    invalidSeatLimitInputs.Add(seatLimitInput);
                }

                continue;
            }

            var recurringCountPrefix = RecurringCountPrefixes.FirstOrDefault(prefix =>
                line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
            if (recurringCountPrefix is not null)
            {
                var recurringInput = line[recurringCountPrefix.Length..].Trim();
                if (int.TryParse(recurringInput, out var parsedCount) &&
                    parsedCount is >= 1 and <= MaxRecurringSessionCount)
                {
                    recurringCount = parsedCount;
                }
                else
                {
                    invalidRecurringInputs.Add(recurringInput);
                }

                continue;
            }

            var recurringIntervalPrefix = RecurringIntervalPrefixes.FirstOrDefault(prefix =>
                line.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));
            if (recurringIntervalPrefix is not null)
            {
                var recurringInput = line[recurringIntervalPrefix.Length..].Trim();
                if (int.TryParse(recurringInput, out var parsedInterval) &&
                    parsedInterval is >= 1 and <= MaxRecurringIntervalDays)
                {
                    recurringIntervalDays = parsedInterval;
                }
                else
                {
                    invalidRecurringInputs.Add(recurringInput);
                }

                continue;
            }

            if (!line.StartsWith(TimePrefix, StringComparison.OrdinalIgnoreCase))
            {
                continue;
            }

            var timeInput = line[TimePrefix.Length..].Trim();
            if (!MoscowTime.TryParseMoscow(timeInput, out var scheduledAt))
            {
                invalidTimeInputs.Add(timeInput);
                continue;
            }

            if (scheduledAt <= nowUtc)
            {
                pastTimeInputs.Add(timeInput);
                continue;
            }

            scheduledTimes.Add(scheduledAt);
        }

        if (recurringCount.HasValue && scheduledTimes.Count == 1)
        {
            var firstScheduledTime = scheduledTimes[0];
            scheduledTimes = Enumerable.Range(0, recurringCount.Value)
                .Select(index => firstScheduledTime.AddDays(recurringIntervalDays * index))
                .ToList();
        }

        return new NewSessionParseResult(
            title,
            link,
            imageUrl,
            maxPlayers,
            scheduledTimes,
            pastTimeInputs,
            invalidTimeInputs,
            invalidSeatLimitInputs,
            invalidRecurringInputs);
    }
}
