// Ignore Spelling: Linq

using System;

namespace AsbCloudInfrastructure
{
    public static class DateTimeExtensions
    {
        /// <summary>
        /// Приветсти к UTC из времени куста
        /// </summary>
        /// <param name="date"></param>
        /// <param name="remoteTimezoneOffsetHours"></param>
        /// <returns></returns>
        public static DateTimeOffset ToUtcDateTimeOffset(this DateTime date, double remoteTimezoneOffsetHours)
        {
            if (date == default)
                return new DateTimeOffset();

            var dateUtc = date.Kind switch
            {
                DateTimeKind.Local => date.ToUniversalTime(),
                DateTimeKind.Unspecified => DateTime.SpecifyKind(date.AddHours(-remoteTimezoneOffsetHours), DateTimeKind.Utc),
                _ => date,
            };
            return new DateTimeOffset(dateUtc);
        }

        /// <summary>
        /// Привести ко времени куста из utc
        /// </summary>
        /// <param name="date"></param>
        /// <param name="remoteTimezoneOffsetHours"></param>
        /// <returns></returns>
        public static DateTime ToRemoteDateTime(this DateTimeOffset date, double remoteTimezoneOffsetHours)
        {
            if (date == default)
                return new DateTime(0, DateTimeKind.Unspecified);
            var dateTz = date.ToOffset(TimeSpan.FromHours(remoteTimezoneOffsetHours));
            return dateTz.DateTime;
        }

        /// <summary>
        /// Поиск индекса близкого к value значения в span.
        /// Элементы в span должны быть отсортированы по возрастанию по полю propertyGetter.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TProp"></typeparam>
        /// <param name="span"></param>
        /// <param name="propertyGetter">метод получения свойства из объекта класса T</param>
        /// <param name="value">искомое значение</param>
        /// <param name="maxIterations">максимальное кол-во итераций. Прим.: 
        /// в span из 4000 записей достаточно 9-ти итераций
        /// 8_000 => 10 итераций;
        /// 16_000 => 11;
        /// ...
        /// 256_000 => 15;
        /// При недостаточном кол-ве итераций индекс может оказаться близко, но это будет не ближайшее значение
        /// </param>
        /// <returns></returns>
        public static int BinarySearch<T, TProp>(this Span<T> span, Func<T, TProp> propertyGetter, TProp value, int maxIterations = 16)
            where TProp : IComparable
        {
            if (span.Length < 2 || --maxIterations < 0)
                return 0;

            var indexOfMiddle = span.Length >> 1;

            var comparison = value.CompareTo(propertyGetter(span[indexOfMiddle]));

            if (comparison > 0)
            {
                var index = indexOfMiddle + 1 + BinarySearch(span[(indexOfMiddle + 1)..], propertyGetter, value, maxIterations);
                return index < span.Length ? index : span.Length - 1;
            }
            else if (comparison < 0)
                return BinarySearch(span[..(indexOfMiddle)], propertyGetter, value, maxIterations);
            else //if (comparison == 0)
                return indexOfMiddle;
        }
    }
}