using System;
using System.Collections.Generic;
using System.Linq;

using BS.Shared.Core;
using BS.Shared.DataContracts;

namespace BS.Shared.Extensions
{
    public static class IListCostRatePeriodDCExtensions
    {
        public static CostRatePeriodDC GetCostRatePeriodForDate(this IList<CostRatePeriodDC> ths, CostRatePeriodType type, DateTime date)
        {
            CostRatePeriodDC result = ths.GetOldRates(type).OrderBy(i => i.EndDate).FirstOrDefault(i => i.EndDate.Value.AddDays(1).Date > date);
            if (result == null)
                result = ths.GetLatestRate(type);
            if (result == null && type == CostRatePeriodType.MinutesPerServiceUnit)
            {
                result = new CostRatePeriodDC();
                result.CostRateType = CostRatePeriodType.MinutesPerServiceUnit;
                result.UnitName = "FLS";
                result.CostRateValue = 60;
            }
            return result;
        }

        public static CostRatePeriodDC GetCostRatePeriodForDate(
            this IList<CostRatePeriodDC> ths, 
            CostRatePeriodType type, 
            DateTime date, 
            long valueListEntry)
        {
			var result = ths.GetOldRates(type, valueListEntry).OrderBy(i => i.EndDate).FirstOrDefault(i => i.EndDate.Value.AddDays(1).Date > date);

            return result ?? ths.GetLatestRate(type, valueListEntry);
        }

        public static Dictionary<long, CostRatePeriodDC> GetCostRatePeriodForDateGroupedByValueListEntryOid
            (this IList<CostRatePeriodDC> ths, CostRatePeriodType type, DateTime date)
        {
            return ths.Where(i => i.ValueListEntry != null).GroupBy(i => i.ValueListEntry.ValueListEntryOid.Value)
                .ToDictionary(i => i.Key, i => i.ToList().GetCostRatePeriodForDate(type, date, i.Key));
        }

        public static CostRatePeriodDC GetCurrentlyValidRate(this IList<CostRatePeriodDC> ths, CostRatePeriodType type)
        {
            return ths.GetCostRatePeriodForDate(type, DateTime.Now);
        }

        public static CostRatePeriodDC GetCurrentlyValidRate(
            this IList<CostRatePeriodDC> ths, 
            CostRatePeriodType type, 
            long? valueListEntry)
        {
            if (valueListEntry == null)
            {
                return ths.GetCurrentlyValidRate(type);
            }

            var result = ths.GetOldRates(type, valueListEntry).OrderBy(i => i.EndDate).FirstOrDefault(i => i.EndDate >= DateTime.Now.Date);

            return result ?? ths.GetLatestRate(type, valueListEntry);
        }

        public static decimal? GetCurrentlyValidRateValue(this IList<CostRatePeriodDC> ths, CostRatePeriodType type)
        {
            return ths.GetValidRateValueForDate(type, DateTime.Now);
        }

        public static decimal? GetValidRateValueForDate(this IList<CostRatePeriodDC> ths, CostRatePeriodType type, DateTime date)
        {
            var dc = ths.GetCostRatePeriodForDate(type, date);
            if (dc == null)
            {
                return null;
            }

            return dc.CostRateValue;
        }

        public static CostRatePeriodDC GetLatestRate(this IList<CostRatePeriodDC> ths, CostRatePeriodType type)
        {
            
            foreach (var i in ths)
            {
                if (i.CostRateType == type && i.ValueListEntry == null && !i.EndDate.HasValue)
                    return i;
            }
            return null;
        }

        public static CostRatePeriodDC GetLatestRate(
            this IList<CostRatePeriodDC> ths, 
            CostRatePeriodType type, 
            long? valueListEntry)
        {
            if (valueListEntry == null)
            {
                return ths.GetLatestRate(type);
            }

            return ths.SingleOrDefault(i => i.CostRateType == type && i.ValueListEntry != null
                                            && i.ValueListEntry.ValueListEntryOid.Equals(valueListEntry) && !i.EndDate.HasValue);
        }

        public static List<CostRatePeriodDC> GetOldRates(this IList<CostRatePeriodDC> ths, CostRatePeriodType type)
        {
            return new List<CostRatePeriodDC>(ths.Where(i => i.CostRateType == type && i.EndDate.HasValue && i.ValueListEntry == null).ToList());
        }

        public static List<CostRatePeriodDC> GetOldRates(
            this IList<CostRatePeriodDC> ths, 
            CostRatePeriodType type, 
            long? valueListEntry)
        {
            if (valueListEntry == null)
            {
                return ths.GetOldRates(type);
            }

            return new List<CostRatePeriodDC>(ths.Where(i =>
                                                        i.CostRateType == type && i.EndDate.HasValue && i.ValueListEntry != null &&
                                                        i.ValueListEntry.ValueListEntryOid.Equals(valueListEntry)).ToList());
        }

        public static List<CostRatePeriodDC> GetRatesInSpan(this List<CostRatePeriodDC> ths, CostRatePeriodType type, DateTime start, DateTime end)
        {
            return GetSpans(ths, type, start, end).Keys.ToList();
        }

        public static Dictionary<CostRatePeriodDC, DateTimeSpan> GetSpans(this List<CostRatePeriodDC> ths, CostRatePeriodType type, DateTime spanStart, DateTime spanEnd)
        {
            var result = new Dictionary<CostRatePeriodDC, DateTimeSpan>();

            foreach (var i in ths.OrderByEndDateAscNullEndDatesLast().Where(i => i.CostRateType == type && (i.EndDate >= spanStart || !i.EndDate.HasValue)))
            {
                bool stop = false;
                var end = i.EndDate;
                if (!i.EndDate.HasValue || i.EndDate > spanEnd)
                {
                    end = spanEnd;
                    stop = true;
                }

                var span = new DateTimeSpan
                           {
                               StartDateTime = spanStart, 
                               EndDateTime = end.Value
                           };
				if (spanStart <= end)
					result.Add(i, span);
                if (stop)
                {
                    break;
                }

                spanStart = span.EndDateTime.AddDays(1);
            }

            return result;
        }

        public static List<CostRatePeriodDC> OrderByEndDateAscNullEndDatesLast(this List<CostRatePeriodDC> ths)
        {
            var result = ths
                .Where(i => i.EndDate.HasValue).OrderBy(i => i.EndDate)
                .ToList();
            result.AddRange(ths.Where(i => !i.EndDate.HasValue));
            return result;
        }

    }
}