using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Options;
using StellaOps.Feedser.Source.CertCc.Configuration;
using StellaOps.Feedser.Source.Common.Cursors;

namespace StellaOps.Feedser.Source.CertCc.Internal;

/// <summary>
/// Computes which CERT/CC summary endpoints should be fetched for the next export window.
/// </summary>
public sealed class CertCcSummaryPlanner
{
    private readonly CertCcOptions _options;
    private readonly TimeProvider _timeProvider;

    public CertCcSummaryPlanner(
        IOptions<CertCcOptions> options,
        TimeProvider? timeProvider = null)
    {
        _options = (options ?? throw new ArgumentNullException(nameof(options))).Value ?? throw new ArgumentNullException(nameof(options));
        _options.Validate();
        _timeProvider = timeProvider ?? TimeProvider.System;
    }

    public CertCcSummaryPlan CreatePlan(TimeWindowCursorState? state)
    {
        var now = _timeProvider.GetUtcNow();
        var window = TimeWindowCursorPlanner.GetNextWindow(now, state, _options.SummaryWindow);
        var nextState = (state ?? TimeWindowCursorState.Empty).WithWindow(window);

        var months = EnumerateYearMonths(window.Start, window.End)
            .Take(_options.MaxMonthlySummaries)
            .ToArray();

        if (months.Length == 0)
        {
            return new CertCcSummaryPlan(window, Array.Empty<CertCcSummaryRequest>(), nextState);
        }

        var requests = new List<CertCcSummaryRequest>(months.Length * 2);
        foreach (var month in months)
        {
            requests.Add(new CertCcSummaryRequest(
                BuildMonthlyUri(month.Year, month.Month),
                CertCcSummaryScope.Monthly,
                month.Year,
                month.Month));
        }

        foreach (var year in months.Select(static value => value.Year).Distinct().OrderBy(static year => year))
        {
            requests.Add(new CertCcSummaryRequest(
                BuildYearlyUri(year),
                CertCcSummaryScope.Yearly,
                year,
                Month: null));
        }

        return new CertCcSummaryPlan(window, requests, nextState);
    }

    private Uri BuildMonthlyUri(int year, int month)
    {
        var path = $"{year:D4}/{month:D2}/summary/";
        return new Uri(_options.BaseApiUri, path);
    }

    private Uri BuildYearlyUri(int year)
    {
        var path = $"{year:D4}/summary/";
        return new Uri(_options.BaseApiUri, path);
    }

    private static IEnumerable<(int Year, int Month)> EnumerateYearMonths(DateTimeOffset start, DateTimeOffset end)
    {
        if (end <= start)
        {
            yield break;
        }

        var cursor = new DateTime(start.Year, start.Month, 1, 0, 0, 0, DateTimeKind.Utc);
        var limit = new DateTime(end.Year, end.Month, 1, 0, 0, 0, DateTimeKind.Utc);
        if (end.Day != 1 || end.TimeOfDay != TimeSpan.Zero)
        {
            limit = limit.AddMonths(1);
        }

        while (cursor < limit)
        {
            yield return (cursor.Year, cursor.Month);
            cursor = cursor.AddMonths(1);
        }
    }
}
