public decimal GetTax(string city, DateTime date)
        {
            CityTax cityTaxes;

            if (_cityTaxesCache.TryGetValue(city, out cityTaxes) == false)
            {
                return(NoTax);
            }

            YearTax cityTax;

            if (!cityTaxes.YearTaxes.TryGetValue(date.Year, out cityTax))
            {
                return(NoTax);
            }

            decimal dailyTax, weeklyTax, monthlyTax;

            if (cityTax.DayTaxes.TryGetValue(date.DayOfYear, out dailyTax))
            {
                return(dailyTax);
            }

            // fallback to Weekly Taxes
            var weekOfYear = CalendarHelper.GetWeekOfYear(date);

            if (cityTax.WeekTaxes.TryGetValue(weekOfYear, out weeklyTax))
            {
                return(weeklyTax);
            }

            // fallback to Montly and Yearly Taxes
            return(cityTax.MonthTaxes.TryGetValue(date.Month, out monthlyTax) ? monthlyTax : cityTax.YearlyTax);
        }
        public void UpdateTaxes(TaxRecord taxRecord)
        {
            if (taxRecord == null)
            {
                throw new ArgumentException("Input data is invalid. See manual (Readme) for expected input formats");
            }
            if (string.IsNullOrEmpty(taxRecord.City))
            {
                throw new ArgumentException("City property is missing or invalid");
            }
            if (taxRecord.Tax.HasValue == false || taxRecord.Tax.Value == 0)
            {
                throw new ArgumentException("Tax must have a non-zero value");
            }
            if (string.IsNullOrEmpty(taxRecord.Day) == false)
            {
                DateTime dayDateTime;
                var      parseResult = DateTime.TryParse(taxRecord.Day, out dayDateTime);
                if (!parseResult)
                {
                    throw new ArgumentException("Day property is invalid. Expected date in acceptable format (e.g. YYYY.MM.DD)");
                }

                if (taxRecord.Year.HasValue || taxRecord.Month.HasValue || taxRecord.WeekOfYear.HasValue)
                {
                    throw new ArgumentException("Only one type of tax can be entered in one request.");
                }

                _taxRepository.AddDailyTax(taxRecord.City, dayDateTime, taxRecord.Tax.Value);
                return;
            }


            var year = taxRecord.Year ?? -1;

            if (year < 0)
            {
                throw new ArgumentException("Year property is missing or invalid");
            }

            if (taxRecord.Month.HasValue)
            {
                if (taxRecord.Month.Value < 1 || taxRecord.Month.Value > 12)
                {
                    throw new ArgumentException("Month property is invalid. Valid range: 1-12");
                }
                if (taxRecord.WeekOfYear.HasValue)
                {
                    throw new ArgumentException("Only one type of tax can be entered in one request.");
                }
                _taxRepository.AddMonthlyTax(taxRecord.City, year, taxRecord.Month.Value, taxRecord.Tax.Value);
                return;
            }
            if (taxRecord.WeekOfYear.HasValue)
            {
                var weekOfYear     = taxRecord.WeekOfYear.Value;
                var lastWeekOfYear = CalendarHelper.GetWeekOfYear(new DateTime(year, 12, 31));
                if (weekOfYear < 1 || weekOfYear > lastWeekOfYear)
                {
                    throw new ArgumentException("Week property is invalid. Valid range: 1..52 (or 53, or 54 - depends on year)");
                }
                _taxRepository.AddWeeklyTax(taxRecord.City, year, weekOfYear, taxRecord.Tax.Value);
            }

            _taxRepository.AddYearlyTax(taxRecord.City, year, taxRecord.Tax.Value);
        }