using System;
using System.Collections.Generic;
using System.Linq;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.Extensions;

namespace BS.Shared.Services
{
// LWL: keinen RateFactor einrechnen
	[IDSpecificClass(Identifiers = new string[] { "LWL", "LWLMaxAmount", "LWLNEU", "LWLBeWo67" })]
    public class LWLCalculations : Calculations
    {
        public override decimal GetIntervalCountForDays(decimal totalDays, SupportConceptApprovalInterval interval)
        {
            return Math.Round(ConvertPerIntervall2PerDay(totalDays, interval), 2, MidpointRounding.AwayFromZero);
        }

        public override decimal ConvertPerIntervall2PerDay(decimal pPerInterval, SupportConceptApprovalInterval pInterval)
        {
            decimal val = 0;
            if (pInterval == SupportConceptApprovalInterval.Weekly)
                val = pPerInterval * .1427m;
            else 
                val = base.ConvertPerIntervall2PerDay(pPerInterval, pInterval);
            return val;
        }

        

        public override decimal? GetApprovedHoursPerDay(SupportConceptApprovalPeriodDC scap,
                                                       List<CostRatePeriodDC> costRatePeriods)
        {
            if (scap.EndDate.HasValue && scap.StartDate.HasValue && scap.ApprovedBEPerInterval.HasValue && !scap.ApprovedBETotal.HasValue 
                && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly)
            {
                var scapDurationInDays = (scap.EndDate.Value - scap.StartDate.Value).Days + 1;

                decimal weeks = Math.Round(scapDurationInDays * 0.1427m, 2, MidpointRounding.AwayFromZero);
                decimal total = weeks*scap.ApprovedBEPerInterval.Value;

                decimal ah = 0;
                if (scapDurationInDays != 0)
                    ah = total/scapDurationInDays;
                return ah;

            }
            return base.GetApprovedHoursPerDay(scap, costRatePeriods);


        }

        public override decimal? GetApprovedHoursForPeriod(SupportConceptApprovalPeriodDC scap,
                                                          List<CostRatePeriodDC> costRatePeriods,
                                                          DateTime? pStart,
                                                          DateTime? pEnd)
        {
            //if (!pStart.HasValue || !pEnd.HasValue || scap.ApprovedFixedAmount.HasValue)
            if (!pStart.HasValue || !pEnd.HasValue)
            {
                return null;
            }


            if (!scap.ApprovedBEPerInterval.HasValue)
                return base.GetApprovedHoursForPeriod(scap, costRatePeriods, pStart, pEnd);

            var hourlyRatesInSpanList = costRatePeriods.GetSpans(CostRatePeriodType.HourlyRate,
                                                                                      pStart.Value.Date, pEnd.Value.Date).ToList();
            if (hourlyRatesInSpanList.Count > 1)
            {
                DateTime start = pStart.Value;
                DateTime end = pEnd.Value;

                decimal totalHours = 0;

                for (int i = 0; i < hourlyRatesInSpanList.Count; i++)
                {
                    if (start <= pEnd.Value)
                    {
                        var crp = hourlyRatesInSpanList.ElementAt(i).Key;

                        if (crp.EndDate.HasValue && crp.EndDate.Value < end)
                            end = crp.EndDate.Value.Date;

                        int periodInDays = (end - start).Days + 1;
                        decimal weeks = Math.Round(periodInDays * 0.1427m, 2, MidpointRounding.AwayFromZero);
						decimal total = Math.Round(weeks * scap.ApprovedBEPerInterval.Value, 2, MidpointRounding.AwayFromZero);
                        totalHours += total;
                        start = end.AddDays(1);
                        end = pEnd.Value;
                    }
                }

                return totalHours;
            }
            else
            {
                var periodInDays = (pEnd.Value.Date - pStart.Value.Date).Days + 1;

                return this.GetApprovedHoursPerDay(scap, costRatePeriods) * periodInDays;
            }
            
        }

        public override decimal? GetApprovedAmountDefaultHourlyRateForPeriod(SupportConceptApprovalPeriodDC scap, 
                                                                             List<CostRatePeriodDC> costRatePeriods, 
                                                                             DateTime? start, 
                                                                             DateTime? end)
        {
            if (!start.HasValue || !end.HasValue)
                return null;
            var approvedHoursPerDay = this.GetApprovedHoursPerDay(scap, costRatePeriods);
            if (!approvedHoursPerDay.HasValue)
            {
                return null;
            }

            decimal? result = 0m;

            var day = start.Value.Date;
            do
            {
                var hourlyRate = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, day);
                var amountThisDay = approvedHoursPerDay * hourlyRate.CostRateValue;
                result += amountThisDay;
            }
            while ((day = day.AddDays(1)) <= end.Value.Date);

            return result;
        }

        public override decimal? GetBillableAmount(ServiceRecordDC record)
        {
            decimal rdMinutes = record.RoundedDuration;
            if (record.ServiceDescription.Category.Percentage != 100)
            {
                rdMinutes = (rdMinutes * record.ServiceDescription.Category.Percentage) / 100;
            }

            var hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record);

            return (rdMinutes / 60m) * hourlyRate.CostRateValue.Value;
        }

        public override decimal GetDurationInMinutesForBillableAmount(ServiceRecordDC record, decimal amount)
        {
            var hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record);
            if (hourlyRate != null && hourlyRate.CostRateValue.HasValue)
                return (amount / hourlyRate.CostRateValue.Value) * 60m;
            return 0;
        }

        public override decimal? GetApprovedAmount(AccountingPeriod ap, SupportConceptApprovalPeriodDC scap)
        {
			decimal t1;
			decimal t2;
            if (scap.ApprovedFixedAmount.HasValue)
                return this.GetApprovedFixedAmountForPeriod(scap, ap.PeriodStart, ap.PeriodEnd, out t1, out t2);

            
            var approvedBETotal = this.GetApprovedBETotalInPeriod(scap, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd);
            if (!approvedBETotal.HasValue)
            {
                return null;
            }

            decimal? result = null;

            var hourlyRate = ap.CostRatesInPeriod.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, ap.PeriodStart);
            if (hourlyRate != null && hourlyRate.CostRateValue.HasValue)
            {
                result = Math.Round(approvedBETotal.Value, 2, MidpointRounding.AwayFromZero) * hourlyRate.CostRateValue;
            }
            return result;
        }

        public override decimal? GetApprovedBETotalInPeriod(SupportConceptApprovalPeriodDC scap,
                                                   List<CostRatePeriodDC> costRatePeriods,
                                                   DateTime start, DateTime end)
        {

            return costRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit)
                .GetHoursInBE(this.GetApprovedHoursForPeriodLWL(scap, costRatePeriods, start, end));
        }

        private decimal? GetApprovedHoursForPeriodLWL(SupportConceptApprovalPeriodDC scap,
                                                      List<CostRatePeriodDC> costRatePeriods,
                                                      DateTime start, DateTime end)
        {
            int periodInDays = (end - start).Days + 1;
            decimal? approvedBEPerWeek = null;
            if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly)
                approvedBEPerWeek = scap.ApprovedBEPerInterval.Value;
            else
                approvedBEPerWeek = base.GetApprovedHoursPerWeek(scap, costRatePeriods);
            
            if (approvedBEPerWeek.HasValue)
            {
                approvedBEPerWeek = Math.Round(approvedBEPerWeek.Value, 2, MidpointRounding.AwayFromZero);

                decimal weeks = Math.Round(periodInDays * 0.1427m, 2, MidpointRounding.AwayFromZero);
                decimal total = weeks * approvedBEPerWeek.Value;
                return total;
            }
            return this.GetApprovedHoursPerDay(scap, costRatePeriods) * periodInDays;


        }

		public override int GetRoundedDuration(int minuteInterval, decimal durationInMinutes)
		{
			decimal lRoundedDuration = durationInMinutes;

			if (minuteInterval > 0 && durationInMinutes > 5)
			{
				lRoundedDuration = durationInMinutes % minuteInterval != 0
										? durationInMinutes + (minuteInterval - (durationInMinutes % minuteInterval))
										: durationInMinutes;
			}

			return (int)Math.Round(lRoundedDuration, 0, MidpointRounding.AwayFromZero);
		}
    }
}