using System;
using System.Collections.Generic;
using BeWo.Report;
using BeWo.Data.Entities;
using BeWo.Data.Access;
using BeWo.Service.DCEntityMapper;
using BeWo.Service.Plugins;
using BeWo.Service.ServiceImplementations;
using BS.Shared.Core;
using BS.Shared;
using BS.Shared.Services;


namespace BeWo.Report.ReportObjects
{
	public class AuslastungBerechnung
	{
	    private Dictionary<long, List<MitarbeiterauslastungRO.SupportConceptDetail>> supportConceptListByScapCustomerBetreuungDict;
        private Dictionary<long, bool> scapBetreuungExistDict;
        public AuslastungBerechnung()
		{
            supportConceptListByScapCustomerBetreuungDict = new Dictionary<long, List<MitarbeiterauslastungRO.SupportConceptDetail>>();
            scapBetreuungExistDict = new Dictionary<long, bool>();
    }


		public virtual void CreateReport(MitarbeiterauslastungRO ro)
		{
			CreateEmployeeDetails(ro);

			CreateEmployeeTotals(ro);

			CreateTeamDetails(ro);

			CreateTotals(ro);
			
			
		}

		private void CreateEmployeeTotals(MitarbeiterauslastungRO ro)
		{
			foreach (var empDetail in ro.EmployeeDetailList)
			{
				foreach (var scd in empDetail.SupportConceptDetailList)
				{
					scd.DirectIst /= 60;
					scd.MittelbarIst /= 60;
					empDetail.DirectIst += scd.DirectIst;
					empDetail.MittelbarIst += scd.MittelbarIst;
					empDetail.ApprovedHoursPerWeek += scd.ApprovedHoursPerWeek;
					empDetail.ApprovedHoursPerWeekWithRateFactor += scd.ApprovedHoursPerWeekWithRateFactor;
				}
				
				
			}
		}

		public virtual void CreateTotals(MitarbeiterauslastungRO ro)
		{
			foreach (var empDetail in ro.EmployeeDetailList)
			{
				ro.DirectIst += empDetail.DirectIst;
				ro.MittelbarIst += empDetail.MittelbarIst;
				ro.ApprovedHoursPerWeek += empDetail.ApprovedHoursPerWeek;
				ro.ApprovedHoursPerWeekWithRateFactor += empDetail.ApprovedHoursPerWeekWithRateFactor;

			}

			if (ro.ApprovedHoursPerWeek != 0)
				ro.RelationIstSoll = (ro.DirectIst / ro.ApprovedHoursPerWeek) * 100m;

			if (ro.DirectIst + ro.MittelbarIst != 0)
				ro.RelationDirectToMittelbar = (ro.DirectIst / (ro.DirectIst + ro.MittelbarIst)) * 100;
		}

		public virtual void CreateTeamDetails(MitarbeiterauslastungRO ro)
		{
			foreach (var teamDetail in ro.TeamDetailList)
			{
				foreach (var empDetail in teamDetail.EmployeeDetailList)
				{
					teamDetail.DirectIst += empDetail.DirectIst;
					teamDetail.MittelbarIst += empDetail.MittelbarIst;
					teamDetail.ApprovedHoursPerWeek += empDetail.ApprovedHoursPerWeek;
					teamDetail.ApprovedHoursPerWeekWithRateFactor += empDetail.ApprovedHoursPerWeekWithRateFactor;

					foreach (var scd in empDetail.SupportConceptDetailList)
					{
						teamDetail.SupportConceptDetailList.Add(scd);
					}
					
				}
				if (teamDetail.ApprovedHoursPerWeek != 0)
					teamDetail.RelationIstSoll = (teamDetail.DirectIst / teamDetail.ApprovedHoursPerWeek) * 100m;

				if (teamDetail.DirectIst + teamDetail.MittelbarIst != 0)
					teamDetail.RelationDirectToMittelbar = (teamDetail.DirectIst / (teamDetail.DirectIst + teamDetail.MittelbarIst)) * 100;

				teamDetail.SupportConceptDetailList.Sort((a, b) => (a.Customer.Person.LastName + ", " + a.Customer.Person.FirstName).CompareTo(b.Customer.Person.LastName + ", " + b.Customer.Person.FirstName));
			}

			
		}

		
		public virtual void CreateEmployeeDetails(MitarbeiterauslastungRO ro)
		{
			foreach (var empDetail in ro.EmployeeDetailList)
			{
				var emp = empDetail.Employee;

				
				Contract contract = emp.LastContract;
				if (contract != null && contract.WeeklyFLS.HasValue)
				{
					empDetail.WeeklyTotalHoursByContract = contract.WeeklyTotalHours ?? 0;
					empDetail.FLSTotalHoursByContract = contract.WeeklyFLS ?? 0;
				}

				var span = new DateTimeSpan();
				span.StartDateTime = ro.ReportStartDate;
				span.EndDateTime = ro.ReportEndDate;

                //supportconcepts by SupportConceptApprovalPeriodOid
                Dictionary<long, MitarbeiterauslastungRO.SupportConceptDetail> supportConceptByScapDict = new Dictionary<long, MitarbeiterauslastungRO.SupportConceptDetail>();

			    var anteile = CreateAuslastungAnteilig(emp);

                foreach (var anteil in anteile)
                {
                    var scap = anteil.SupportConceptApprovalPeriod;
                    var cb2sc = anteil.CostBearer2SupportConcept;
                    var sc = cb2sc.SupportConcept;
                    bool inTimeRange = true;
                    var realEnd = scap.EndDate;

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

                    }
                    
                    if (scap.StartDate.HasValue && realEnd.HasValue)
                    {
                        if (scap.StartDate >= ro.ReportEndDate || realEnd < ro.ReportStartDate)
                        {
                            inTimeRange = false;
                        }
                    }

                    if (sc.IsActive != ActivationTypeId.Deleted && inTimeRange && !supportConceptByScapDict.ContainsKey(scap.Oid.Value))
                    {
                        if (supportConceptListByScapCustomerBetreuungDict.ContainsKey(scap.Oid.Value))
                        {
                            foreach (var sd in supportConceptListByScapCustomerBetreuungDict[scap.Oid.Value])
                            {
                                foreach (var ed in ro.EmployeeDetailList)
                                {
                                    ed.SupportConceptDetailList.Remove(sd);
                                }
                            }
                            supportConceptListByScapCustomerBetreuungDict.Remove(scap.Oid.Value);
                        }
                        var scDC = new MitarbeiterauslastungRO.SupportConceptDetail();

                        supportConceptByScapDict.Add(scap.Oid.Value, scDC);
                        scDC.Customer = sc.Customer;
                        scDC.SupportConceptApprovalPeriod = scap;
                        scDC.CostBearer2SupportConcept = cb2sc;

                        DateTime end = ro.ReportEndDate;
                        if (realEnd.HasValue && realEnd.Value < end)
                        {
                            end = realEnd.Value;
                        }
                        CalculateSoll(scDC, end);

                        ApplyBetreuungsschluessel(scDC, anteil);
                        empDetail.SupportConceptDetailList.Add(scDC);

                        if (!scapBetreuungExistDict.ContainsKey(scap.Oid.Value))
                        {
                            scapBetreuungExistDict.Add(scap.Oid.Value, true);
                        }
                    }
                        
                }



                if (emp.Employee2CustomerList != null)
                {
                    foreach (Employee2Customer e2c in emp.Employee2CustomerList)
                    {
                        bool isMainAttendant = false;
                        if (e2c.ValueList.Count > 0)
                        {
                            SystemEntryID? id = e2c.ValueList[0].Entry.SystemEntryID;
                            if (id.HasValue && id.Value == SystemEntryID.EmployeeRoleMainAttendant)
                                isMainAttendant = true;
                        }
                        if (isMainAttendant && e2c.Customer != null)
                        {
                            if (e2c.Customer.IsActive != ActivationTypeId.Deleted && e2c.Customer.SupportConcepts != null)
                            {
                                foreach (SupportConcept sc in e2c.Customer.SupportConcepts)
                                {
                                    if (sc.IsActive != ActivationTypeId.Deleted && sc.CostBearer2SupportConceptList != null)
                                    {
                                        foreach (CostBearer2SupportConcept c2s in sc.CostBearer2SupportConceptList)
                                        {

                                            foreach (var scap in c2s.ApprovalPeriodList)
                                            {
                                                if (!supportConceptByScapDict.ContainsKey(scap.Oid.Value) && !scapBetreuungExistDict.ContainsKey(scap.Oid.Value))
                                                {
                                                    bool inTimeRange = true;

                                                    var realEnd = scap.EndDate;

                                                    //Prüfe ob vorher abgemeldet
                                                    if (e2c.Customer.TerminationDate.HasValue &&
                                                        e2c.Customer.TerminationDate.Value < realEnd)
                                                    {
                                                        realEnd = e2c.Customer.TerminationDate.Value;
                                                    }


                                                    if (scap.StartDate.HasValue && realEnd.HasValue)
                                                    {
                                                        if (scap.StartDate >= ro.ReportEndDate ||
                                                            realEnd < ro.ReportStartDate)
                                                        {
                                                            inTimeRange = false;
                                                        }
                                                    }

                                                    if (inTimeRange)
                                                    {
                                                        var scDC = new MitarbeiterauslastungRO.SupportConceptDetail();

                                                        supportConceptByScapDict.Add(scap.Oid.Value, scDC);
                                                        scDC.Customer = e2c.Customer;
                                                        scDC.SupportConceptApprovalPeriod = scap;
                                                        scDC.CostBearer2SupportConcept = c2s;

                                                        DateTime end = ro.ReportEndDate;
                                                        if (realEnd.HasValue && realEnd.Value < end)
                                                        {
                                                            end = realEnd.Value;
                                                        }
                                                        CalculateSoll(scDC, end);

                                                        empDetail.SupportConceptDetailList.Add(scDC);

                                                        if (!supportConceptListByScapCustomerBetreuungDict.ContainsKey(scap.Oid.Value))
                                                        {
                                                            supportConceptListByScapCustomerBetreuungDict[scap.Oid.Value] = new List<MitarbeiterauslastungRO.SupportConceptDetail>();
                                                        }
                                                        supportConceptListByScapCustomerBetreuungDict[scap.Oid.Value].Add(scDC);
                                                        
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                CreateServiceRecordDetails(ro, empDetail);


				empDetail.SupportConceptDetailList.Sort((a, b) => (a.Customer.Person.LastName + ", " + a.Customer.Person.FirstName).CompareTo(b.Customer.Person.LastName + ", " + b.Customer.Person.FirstName));

			}

		}

        public virtual void ApplyBetreuungsschluessel(MitarbeiterauslastungRO.SupportConceptDetail scDc, AuslastungBerechnungHelper anteil)
        {
            if (anteil.AnteilAnBetreuung == 33)
            {
                anteil.AnteilAnBetreuung = 100m/3m;
            }
            else if (anteil.AnteilAnBetreuung == 67)
            {
                anteil.AnteilAnBetreuung = (100m / 3m) * 2;
            }
            
            scDc.ApprovedHoursPerWeek *= (anteil.AnteilAnBetreuung/100);
            scDc.ApprovedHoursPerWeekWithRateFactor *= (anteil.AnteilAnBetreuung / 100);
        }

	    public virtual IList<AuslastungBerechnungHelper> CreateAuslastungAnteilig(Employee emp)
	    {
	        var anteile = new List<AuslastungBerechnungHelper>();

            var sp2eList = DAOFactory.SearchDAO.FindSupportConceptApprovalPeriod2Employees(emp);

	        foreach (var sp2e in sp2eList)
	        {
	            var helper = new AuslastungBerechnungHelper();

                helper.SupportConceptApprovalPeriod = DAOFactory.GenericDAO.Unproxy(sp2e.SupportConceptApprovalPeriod);
	            helper.CostBearer2SupportConcept = DAOFactory.GenericDAO.Unproxy(helper.SupportConceptApprovalPeriod.CostBearer2SupportConcept);
	            helper.AnteilAnBetreuung = sp2e.Betreuungsschluessel;

                anteile.Add(helper);
	        }

	        return anteile;
	    }

	    private void CreateServiceRecordDetails(MitarbeiterauslastungRO ro, MitarbeiterauslastungRO.EmployeeDetail empDetail)
		{
			foreach (var scd in empDetail.SupportConceptDetailList)
			{
				if (scd.CostBearer2SupportConcept != null)
				{
					//var records = DAOFactory.SearchDAO.FindServiceRecordsDetailsForLastDays(scd.CostBearer2SupportConcept.Oid.Value, null);
					//Dictionary<long, bool> serviceRecordsProcessed = new Dictionary<long, bool>();
					

					////Dictionary<long, ServiceRecord> groupBookingDict = new Dictionary<long, ServiceRecord>();
					//foreach (ServiceRecord sr in records)
					//{
					//	if (!serviceRecordsProcessed.ContainsKey(sr.Oid.Value) && sr.Start.Value < ro.ReportEndDate)
					//	{

					//		//if (sr.GroupOid != null)
					//		//{
					//		//	groupBookingDict.Add(sr.GroupOid.Value, sr);
					//		//}
					//		AddServiceRecord(scd, sr);
							
					//		serviceRecordsProcessed.Add(sr.Oid.Value, true);
					//	}
					//}
				}
			}

			//var emprecords = DAOFactory.SearchDAO.FindEmployeeServiceRecordsWithoutCustomer(empDetail.Employee.Oid.Value);
			//foreach (var sr in emprecords)
			//{
			//	empDetail.MinutesOfOtherActivities += sr.RoundedDuration;
			//}
		}

		public virtual void AddServiceRecord(MitarbeiterauslastungRO.SupportConceptDetail scDC, ServiceRecord sr)
		{
			var minutes = GetBillableDurationInMinutes(sr);

			if (sr.ServiceDescription != null &&
						sr.ServiceDescription.ServiceCategory != null &&
						sr.ServiceDescription.ServiceCategory.IsBillable)
				scDC.DirectIst += minutes;
			else
				scDC.MittelbarIst += minutes;

		}

		public virtual decimal GetBillableDurationInMinutes(ServiceRecord record)
		{
			decimal rdMinutes = record.RoundedDuration;
			if (record.ServiceDescription.ServiceCategory.Percentage != 100)
			{
				rdMinutes = (rdMinutes * record.ServiceDescription.ServiceCategory.Percentage) / 100;
			}
			if (record.GroupEmployeeCount.HasValue)
				rdMinutes /= record.GroupEmployeeCount.Value;

			return rdMinutes;
		}

		public virtual void CalculateSoll(MitarbeiterauslastungRO.SupportConceptDetail scDC, DateTime date)
		{
            if (scDC.CostBearer2SupportConcept != null && scDC.SupportConceptApprovalPeriod != null)
            {
                var calc = PluginLoader.FindClass<Calculations>(scDC.CostBearer2SupportConcept.CostBearer.ID) ?? Calculations.GetInstance(scDC.CostBearer2SupportConcept.CostBearer.ID);
                var cb2scDc =
                    MapperFactory.SupportConceptCostBearerRelDC_CostBearer2SupportConcept.MapToNewDC(scDC.CostBearer2SupportConcept);
                var scapDc = MapperFactory.SupportConceptApprovalPeriodDC_SupportConceptApprovalPeriod.MapToNewDC(scDC.SupportConceptApprovalPeriod);

                //scDC.ApprovedHoursPerWeek = calc.GetApprovedHoursPerWeek(cb2scDc, date) ?? 0m;
                var bewilligt = calc.GetApprovedHoursPerWeek(scapDc, cb2scDc.CostBearer.CostRatePeriods) ?? 0m;
                scDC.ApprovedHoursPerWeek = calc.ApplyIsCalculationWithFactor(cb2scDc, bewilligt) ?? 0m;
                scDC.ApprovedHoursPerWeekWithRateFactor = bewilligt;

                scDC.StartDate = scDC.SupportConceptApprovalPeriod.StartDate;
                scDC.EndDate = scDC.SupportConceptApprovalPeriod.EndDate;
                scDC.WeekCountUntilNow = calc.GetDurationInWeeksTillNow(cb2scDc, date) ?? 0;

            }
            else if (scDC.CostBearer2SupportConcept != null && scDC.SupportConceptApprovalPeriod == null)
			{
				var calc = PluginLoader.FindClass<Calculations>(scDC.CostBearer2SupportConcept.CostBearer.ID) ?? Calculations.GetInstance(scDC.CostBearer2SupportConcept.CostBearer.ID);
				var cb2scDc =
					MapperFactory.SupportConceptCostBearerRelDC_CostBearer2SupportConcept.MapToNewDC(scDC.CostBearer2SupportConcept);
				
				scDC.ApprovedHoursPerWeek = calc.GetApprovedHoursPerWeek(cb2scDc, date) ?? 0m;
				scDC.ApprovedHoursPerWeekWithRateFactor = calc.GetApprovedHoursPerWeek(cb2scDc, date, false) ?? 0m;

				scDC.StartDate = scDC.CostBearer2SupportConcept.StartDate;
				scDC.EndDate = scDC.CostBearer2SupportConcept.EndDate;
				scDC.WeekCountUntilNow = calc.GetDurationInWeeksTillNow(cb2scDc, date) ?? 0;
				
			}
		}


		public Contract GetNextValidContractForDate(DateTime dt, Employee emp)
		{
			Contract contractNextToStartDate = emp.GetValidContractForDate(dt);

			if (contractNextToStartDate == null)
			{
				DateTime minDate = DateTime.MinValue;
				DateTime maxDate = DateTime.MaxValue;

				foreach (var item in emp.Contracts)
				{
					if (item.EntryDate.HasValue && item.EntryDate.Value >= dt)
					{
						if (contractNextToStartDate == null || item.EntryDate.Value < contractNextToStartDate.EntryDate.Value)
						{
							contractNextToStartDate = item;
						}
					}


				}
			}
			return contractNextToStartDate;
		}

	}

    public class AuslastungBerechnungHelper
    {
        public SupportConceptApprovalPeriod SupportConceptApprovalPeriod { get; set; }
        public CostBearer2SupportConcept CostBearer2SupportConcept { get; set; }
        public decimal AnteilAnBetreuung { get; set; }
    }
}
