EligibilityAndEntitlementService.java

package uk.gov.dhsc.htbhf.claimant.service;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import uk.gov.dhsc.htbhf.claimant.entitlement.PaymentCycleEntitlementCalculator;
import uk.gov.dhsc.htbhf.claimant.entitlement.PaymentCycleVoucherEntitlement;
import uk.gov.dhsc.htbhf.claimant.entity.Claim;
import uk.gov.dhsc.htbhf.claimant.entity.Claimant;
import uk.gov.dhsc.htbhf.claimant.entity.EligibilityOverride;
import uk.gov.dhsc.htbhf.claimant.entity.PaymentCycle;
import uk.gov.dhsc.htbhf.claimant.model.eligibility.EligibilityAndEntitlementDecision;
import uk.gov.dhsc.htbhf.claimant.repository.ClaimRepository;
import uk.gov.dhsc.htbhf.dwp.model.*;
import uk.gov.dhsc.htbhf.eligibility.model.CombinedIdentityAndEligibilityResponse;

import java.time.LocalDate;
import java.util.Collections;
import java.util.Optional;
import java.util.UUID;

import static uk.gov.dhsc.htbhf.claimant.model.eligibility.EligibilityAndEntitlementDecision.buildDuplicateDecisionWithExistingClaimId;
import static uk.gov.dhsc.htbhf.dwp.model.VerificationOutcome.NOT_MATCHED;
import static uk.gov.dhsc.htbhf.dwp.model.VerificationOutcome.NOT_SUPPLIED;

@Primary
@Service
@AllArgsConstructor
@Slf4j
public class EligibilityAndEntitlementService {

    private final EligibilityClient client;
    private final DuplicateClaimChecker duplicateClaimChecker;
    private final ClaimRepository claimRepository;
    private final PaymentCycleEntitlementCalculator paymentCycleEntitlementCalculator;
    private final EligibilityAndEntitlementDecisionFactory eligibilityAndEntitlementDecisionFactory;

    /**
     * Determines the eligibility and entitlement for the given new claimant using v3 of the service. If the claimant's NINO is not found in the database,
     * the external eligibility service is called.
     * Claimants determined to be eligible by the external eligibility service must still either be pregnant or have children under 4,
     * otherwise they will be ineligible.
     *
     * @param claimant                   the claimant to check the eligibility for
     * @param eligibilityOverride to override the eligibility outcome
     * @return the eligibility and entitlement for the claimant
     */
    public EligibilityAndEntitlementDecision evaluateNewClaimant(Claimant claimant, EligibilityOverride eligibilityOverride) {
        log.debug("Looking for live claims for the given NINO");
        if (StringUtils.isNotEmpty(claimant.getNino())) {
            Optional<UUID> liveClaimsWithNino = claimRepository.findLiveClaimWithNino(claimant.getNino());
            if (liveClaimsWithNino.isPresent()) {
                return buildDuplicateDecisionWithExistingClaimId(liveClaimsWithNino.get());
            }
        }

        CombinedIdentityAndEligibilityResponse identityAndEligibilityResponse
                = getCombinedIdentityAndEligibilityResponse(claimant, eligibilityOverride, LocalDate.now());
        PaymentCycleVoucherEntitlement entitlement = paymentCycleEntitlementCalculator.calculateEntitlement(
                Optional.ofNullable(claimant.getExpectedDeliveryDate()),
                identityAndEligibilityResponse.getDobOfChildrenUnder4(),
                LocalDate.now(),
                identityAndEligibilityResponse.getQualifyingReason());
        boolean duplicateHouseholdIdentifierFound = duplicateClaimChecker.liveClaimExistsForHousehold(identityAndEligibilityResponse);
        return eligibilityAndEntitlementDecisionFactory.buildDecision(identityAndEligibilityResponse,
                entitlement, duplicateHouseholdIdentifierFound);
    }

    /**
     * Determines the eligibility and entitlement for the given existing claimant using v3 of the service. No check is made on the NINO as they already exist
     * in the database. The eligibility status is checked by calling the external service.
     * Claimants determined to be eligible by the external eligibility service must still either be pregnant or have children under 4,
     * otherwise they will be ineligible.
     *
     * @param claim          the claim to check eligibility for
     * @param cycleStartDate the start date of the payment cycle
     * @param previousCycle  the previous payment cycle
     * @return the eligibility and entitlement for the claimant
     */
    public EligibilityAndEntitlementDecision evaluateClaimantForPaymentCycle(Claim claim,
                                                                             LocalDate cycleStartDate,
                                                                             PaymentCycle previousCycle) {
        CombinedIdentityAndEligibilityResponse identityAndEligibilityResponse
                = getCombinedIdentityAndEligibilityResponse(claim.getClaimant(), claim.getEligibilityOverride(), cycleStartDate);
        PaymentCycleVoucherEntitlement entitlement = paymentCycleEntitlementCalculator.calculateEntitlement(
                Optional.ofNullable(claim.getClaimant().getExpectedDeliveryDate()),
                identityAndEligibilityResponse.getDobOfChildrenUnder4(),
                cycleStartDate,
                previousCycle.getVoucherEntitlement(),
                identityAndEligibilityResponse.getQualifyingReason());
        return eligibilityAndEntitlementDecisionFactory.buildDecision(identityAndEligibilityResponse,
                entitlement, false);
    }

    private CombinedIdentityAndEligibilityResponse getCombinedIdentityAndEligibilityResponse(Claimant claimant,
                                                                                             EligibilityOverride eligibilityOverride,
                                                                                             LocalDate eligibleAtDate) {
        if (isOverride(eligibilityOverride, eligibleAtDate)) {
            VerificationOutcome verificationOutcome = getVerificationOutcome(eligibilityOverride);
            return buildOverrideResponse(eligibilityOverride, claimant, verificationOutcome, IdentityOutcome.MATCHED);
        }

        if (StringUtils.isEmpty(claimant.getNino())) {
            return buildOverrideResponse(null, claimant, NOT_MATCHED, IdentityOutcome.NOT_MATCHED);
        }

        return client.checkIdentityAndEligibility(claimant);
    }

    private boolean isOverride(EligibilityOverride eligibilityOverride, LocalDate eligibleAtDate) {
        return eligibilityOverride != null && eligibleAtDate.isBefore(eligibilityOverride.getOverrideUntil());
    }

    private CombinedIdentityAndEligibilityResponse buildOverrideResponse(EligibilityOverride eligibilityOverride,
                                                                         Claimant claimant,
                                                                         VerificationOutcome verificationOutcome,
                                                                         IdentityOutcome identityOutcome) {

        return getCombinedIdentityAndEligibilityResponseBuilder(eligibilityOverride)
                .identityStatus(identityOutcome)
                .pregnantChildDOBMatch(verificationOutcome)
                .addressLine1Match(verificationOutcome)
                .emailAddressMatch(claimant.getEmailAddress() == null ? NOT_SUPPLIED : verificationOutcome)
                .mobilePhoneMatch(claimant.getPhoneNumber() == null ? NOT_SUPPLIED : verificationOutcome)
                .postcodeMatch(verificationOutcome)
                .deathVerificationFlag(DeathVerificationFlag.N_A)
                .build();
    }

    private CombinedIdentityAndEligibilityResponse.CombinedIdentityAndEligibilityResponseBuilder getCombinedIdentityAndEligibilityResponseBuilder(
            EligibilityOverride eligibilityOverride) {

        if (eligibilityOverride == null) {
            return CombinedIdentityAndEligibilityResponse.builder()
                    .eligibilityStatus(EligibilityOutcome.NOT_CONFIRMED)
                    .dobOfChildrenUnder4(Collections.emptyList())
                    .qualifyingReason(QualifyingReason.NOT_SET);
        }

        return CombinedIdentityAndEligibilityResponse.builder()
                .eligibilityStatus(eligibilityOverride.getEligibilityOutcome())
                .dobOfChildrenUnder4(eligibilityOverride.getChildrenDob())
                .qualifyingReason(eligibilityOverride.getQualifyingReason());
    }

    private VerificationOutcome getVerificationOutcome(EligibilityOverride eligibilityOverride) {
        return eligibilityOverride.getEligibilityOutcome() == EligibilityOutcome.CONFIRMED
                ? VerificationOutcome.MATCHED
                : VerificationOutcome.NOT_SET;
    }

}