using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BeWo.Data.Entities;
using BeWo.Service.DCEntityMapper;
using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.Extensions;
using BS.Shared.Services;

namespace BeWo.Service.Invoicing
{
	[IDSpecificClass(Identifier = "LVRIBP")]
	class IBPLVRSettlementInvoiceCreation : SettlementInvoiceCreation
	{

		public override void CreateInvoiceDetailsTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s)
		{
			String id = c2s.CostBearer.ID;

			var calc = Calculations.GetInstance(id);
			var relDc = c2s.MapToNewDC();

			DateTime periodStart = si.AccountingPeriodStart.Value.Date;
			DateTime periodEnd = si.AccountingPeriodEnd.Value.Date;

			//Prüfe ob vorher abgemeldet
			if (c2s.SupportConcept.Customer.TerminationDate.HasValue &&
				c2s.SupportConcept.Customer.TerminationDate.Value < periodEnd)
			{
				periodEnd = c2s.SupportConcept.Customer.TerminationDate.Value;

			}

			var hourlyRatesInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate,
																				  periodStart, periodEnd).ToList();

			si.DifferentHourlyRateCount = hourlyRatesInSpanList.Count;

			var rateFactorsInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor,
																				  periodStart, periodEnd).ToList();
			if (rateFactorsInSpanList.Count > 0)
			{
				var cdc = rateFactorsInSpanList.Last().Key;
				if (cdc.CostRateValue.HasValue)
					si.RateFactor = cdc.CostRateValue.Value;
			}
			decimal rf = GetRateFactor(si);

			var apList = CreateApprovalPeriods(si, c2s);
			si.ApprovedFLS = CalculateApprovedTotalFLS(apList);
			decimal approvedMinutesOhneFactor = ((si.ApprovedFLS ?? 0) * 60) / rf;
			
			var records = c2s.ServiceRecords;
			records = records.Where(i => i.Start.HasValue && i.Start.Value.InBetween(periodStart, periodEnd, true)).ToList();
			
			var recordDCS = records.MapToNewDCs();

			si.RecordedBillableFLSNotBillableCustomerAbsent
				= calc.GetHoursNotBillableDueToAbsenceTimes(relDc, recordDCS,
				c2s.SupportConcept.Customer.GetAbsencesTimesNotBillableWholeWeeks(), false, false);

			si.RecordedBillableFLSNotBillableOutOfApprovedSpan = calc.GetHoursNotBillableDueToOutOfSpan(relDc, recordDCS);

			Dictionary<long, bool> groupBookingDict = new Dictionary<long, bool>();


			decimal flmCleared = 0;
			

			records = records.OrderBy<ServiceRecord, DateTime>(sr => sr.Start.HasValue ? sr.Start.Value : DateTime.MinValue).ToList<ServiceRecord>();

			foreach (var iRecord in records)
			{
				if (iRecord.ServiceDescription.ServiceCategory.IsBillable)
				{
					if (iRecord.GroupOid == null || !groupBookingDict.ContainsKey(iRecord.GroupOid.Value))
					{
						decimal rd = iRecord.RoundedDuration;
						if (iRecord.ServiceDescription.ServiceCategory.Percentage != 100)
						{
							rd = (rd * iRecord.ServiceDescription.ServiceCategory.Percentage) / 100;
						}

						var ap = apList.FirstOrDefault(iap => iap.PeriodStartDate <= iRecord.Start && (iap.PeriodEndDate == DateTime.MaxValue || iRecord.Start < iap.PeriodEndDate.AddDays(1)));


						if (ap != null)
						{
							decimal durationToAdd = rd;

							if (flmCleared + rd > approvedMinutesOhneFactor)
							{
								durationToAdd = approvedMinutesOhneFactor - flmCleared;
								if (durationToAdd < 0)
									durationToAdd = 0;
							}
							flmCleared += rd;



							ap.RecordedMinutes += durationToAdd;
						}




						if (iRecord.GroupOid != null && !groupBookingDict.ContainsKey(iRecord.GroupOid.Value))
						{
							groupBookingDict.Add(iRecord.GroupOid.Value, true);
						}

					}
				}
			}


			si.InvoiceItems = CreateInvoiceItems(si, apList);

			decimal totalAmount = 0;
			foreach (var ii in si.InvoiceItems)
			{
				totalAmount += ii.GrossAmountTotal ?? 0;
			}

			si.RecordedBillableFLSNetto = (flmCleared / 60) * rf; //eigentlich falsch dass netto = brutto ist.
			si.RecordedBillableFLSBrutto = si.RecordedBillableFLSNetto;

			si.AmountBillableTotal = totalAmount;

			//AdvancedPayments
			CalculateAdvancedPayments(si, c2s, null, null, hourlyRatesInSpanList);
			//EquityContribution (Eigenanteil)
			CalculateEquityContribution(si, c2s.SupportConcept, null, null);

			si.Claim = si.AmountBillableTotal - (si.AmountAdvancedPayments ?? 0) - si.EquityContribution;


		}

		private decimal GetRateFactor(Settlement2DC si)
		{
			decimal rf = 1;
			if (si.RateFactor.HasValue && si.RateFactor.Value != 0)
			{
				rf = si.RateFactor.Value;
				rf = (100 + rf) / 100;
			}
			return rf;
		}

		private List<InvoiceItemDC> CreateInvoiceItems(Settlement2DC si, IList<ApprovalPeriod> apList)
		{
			List<InvoiceItemDC> iiList = new List<InvoiceItemDC>();

			decimal rf = GetRateFactor(si);

			var apsByHourlyRate = new Dictionary<decimal, IList<ApprovalPeriod>>();

			foreach (var ap in apList)
			{
				IList<ApprovalPeriod> list = null;
				if (apsByHourlyRate.ContainsKey(ap.AmountPerUnit))
				{
					list = apsByHourlyRate[ap.AmountPerUnit];
				}
				else
				{
					list = new List<ApprovalPeriod>();
					apsByHourlyRate[ap.AmountPerUnit] = list;
				}

				list.Add(ap);
			}
			foreach (var hr in apsByHourlyRate.Keys)
			{
				IList<ApprovalPeriod> list = apsByHourlyRate[hr];
				var ii = new InvoiceItemDC();

				decimal minutesTotal = 0;
				DateTime start = DateTime.MaxValue;
				DateTime end = DateTime.MinValue;

				foreach (var approvalPeriod in list)
				{
					minutesTotal += approvalPeriod.RecordedMinutes;
					if (approvalPeriod.PeriodStartDate < start)
						start = approvalPeriod.PeriodStartDate;
					if (approvalPeriod.PeriodEndDate > end)
						end = approvalPeriod.PeriodEndDate;
				}
				ii.ItemPeriodStart = start;
				ii.ItemPeriodEnd = end;
				ii.RateFactor = si.RateFactor;
				ii.AmountPerUnit = hr;
				decimal hours = minutesTotal / 60m;
				hours *= rf;
				hours = Math.Round(hours, 2, MidpointRounding.AwayFromZero);
				ii.UnitCount = hours;
				ii.AmountTotal = Math.Round(hr * hours, 2, MidpointRounding.AwayFromZero);
				ii.GrossAmountTotal = ii.AmountTotal;

				iiList.Add(ii);
			}

			return iiList.OrderBy(ii => ii.ItemPeriodStart).ToList();
		}

		public override IList<ApprovalPeriod> CreateApprovalPeriods(Settlement2DC si, CostBearer2SupportConcept c2s)
		{
			decimal rf = GetRateFactor(si);

			var periods = base.CreateApprovalPeriods(si, c2s);
			foreach (var ap in periods)
			{
				ap.ApprovedTotal *= rf;
			}

			return periods;
		}

		private decimal? CalculateApprovedTotalFLS(IList<ApprovalPeriod> apList)
		{
			decimal flsTotal = 0;
			foreach (var ap in apList)
			{
				flsTotal += ap.ApprovedTotal;
			}
			return flsTotal;
		}

		//public override void InitApprovalPeriod(Settlement2DC si, ApprovalPeriod approvalPeriod)
		//{
		//	base.InitApprovalPeriod(si, approvalPeriod);
		//	approvalPeriod.Weeks = Math.Round(approvalPeriod.Days * 0.1427m, 2);
		//	approvalPeriod.ApprovedTotal = Math.Round(approvalPeriod.Weeks * approvalPeriod.ApprovedPerUnit, 2);
		//	approvalPeriod.ApprovedAmountTotal = Math.Round(approvalPeriod.ApprovedTotal * approvalPeriod.AmountPerUnit, 2);
		//}
	}

}
