using System; using System.Threading.Tasks; using System.Text; using Compliance.Domain.Models; using Compliance.DTOs; using Compliance.Infrastructure.Repositories; namespace Compliance.Services.ValidationRules { public class SeasonalTariffValidationRule(IDistributorRepository distributorRepository) : IValidationRule { private readonly IDistributorRepository _distributorRepository = distributorRepository; private const string RULE_NAME = "Seasonal Tariff Validation"; private const string TUSD_ERROR = "TUSD amount with seasonal adjustment doesn't match the expected value"; private const string TE_ERROR = "TE amount with seasonal adjustment doesn't match the expected value"; private const string SEASON_ERROR = "Incorrect season applied for the billing period"; private const decimal TOLERANCE = 0.01m; public int Priority => 4; // Run after reactive energy validation public async Task ValidateAsync( BillComplianceRequest request, DistributorInformation distributorInfo, Client clientInfo) { if (!ShouldApplySeasonalTariff(request)) { return new ValidationResult { IsValid = true, RuleName = RULE_NAME, Message = string.Empty }; } var seasonalInfo = await _distributorRepository.GetSeasonalTariffInformationAsync( request.DistributorName, request.ConsumerGroup, request.Month) ?? throw new InvalidOperationException("Seasonal tariff information not found"); var tariffInfo = await _distributorRepository.GetTariffInformationAsync( request.DistributorName, request.Month) ?? throw new InvalidOperationException("Tariff information not found"); var isValid = true; var message = new StringBuilder(); var billingPeriod = DateOnly.ParseExact(request.Month, "MM/yyyy"); var isDrySeason = IsInDrySeason(billingPeriod, seasonalInfo); var seasonalMultiplier = isDrySeason ? seasonalInfo.DrySeasonMultiplier : seasonalInfo.WetSeasonMultiplier; if (request.AppliedSeason != (isDrySeason ? "DRY" : "WET")) { isValid = false; message.AppendLine($"{SEASON_ERROR}. Expected: {(isDrySeason ? "DRY" : "WET")}, Found: {request.AppliedSeason}"); } // Validate TUSD with seasonal adjustment var expectedTUSD = request.ConsumptionAmount * tariffInfo.TUSDValue * seasonalMultiplier; if (Math.Abs(request.SeasonalTUSDAmount - expectedTUSD) > TOLERANCE) { isValid = false; message.AppendLine($"{TUSD_ERROR}. Expected: {expectedTUSD:F2}, Found: {request.SeasonalTUSDAmount:F2}"); } // Validate TE with seasonal adjustment var expectedTE = request.ConsumptionAmount * tariffInfo.TEValue * seasonalMultiplier; if (Math.Abs(request.SeasonalTEAmount - expectedTE) > TOLERANCE) { isValid = false; message.AppendLine($"{TE_ERROR}. Expected: {expectedTE:F2}, Found: {request.SeasonalTEAmount:F2}"); } return new ValidationResult { IsValid = isValid, RuleName = RULE_NAME, Message = message.ToString().TrimEnd() }; } private static bool ShouldApplySeasonalTariff(BillComplianceRequest request) { // According to ANEEL, seasonal tariffs typically apply to irrigation/rural consumers return request.ConsumerGroup.ToUpper() switch { "RURAL" => true, "IRRIGATION" => true, "AQUACULTURE" => true, _ => false }; } private static bool IsInDrySeason(DateOnly date, SeasonalTariffInformation seasonalInfo) { // Handle year wrap-around case if (seasonalInfo.DrySeasonStart.Month > seasonalInfo.DrySeasonEnd.Month) { return date.Month >= seasonalInfo.DrySeasonStart.Month || date.Month <= seasonalInfo.DrySeasonEnd.Month; } return date.Month >= seasonalInfo.DrySeasonStart.Month && date.Month <= seasonalInfo.DrySeasonEnd.Month; } } }