All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.robozonky.app.delinquencies.Category Maven / Gradle / Ivy

/*
 * Copyright 2020 The RoboZonky Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.robozonky.app.delinquencies;

import static com.github.robozonky.app.events.impl.EventFactory.loanDefaultedLazy;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent10plus;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent10plusLazy;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent30plus;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent30plusLazy;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent60plus;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent60plusLazy;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent90plus;
import static com.github.robozonky.app.events.impl.EventFactory.loanDelinquent90plusLazy;
import static com.github.robozonky.app.events.impl.EventFactory.loanNowDelinquent;
import static com.github.robozonky.app.events.impl.EventFactory.loanNowDelinquentLazy;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.github.robozonky.api.notifications.LoanDefaultedEvent;
import com.github.robozonky.api.notifications.LoanDelinquent10DaysOrMoreEvent;
import com.github.robozonky.api.notifications.LoanDelinquent30DaysOrMoreEvent;
import com.github.robozonky.api.notifications.LoanDelinquent60DaysOrMoreEvent;
import com.github.robozonky.api.notifications.LoanDelinquent90DaysOrMoreEvent;
import com.github.robozonky.api.notifications.LoanNowDelinquentEvent;
import com.github.robozonky.api.notifications.SessionEvent;
import com.github.robozonky.api.remote.entities.Investment;
import com.github.robozonky.api.remote.entities.Loan;
import com.github.robozonky.app.events.impl.EventFactory;
import com.github.robozonky.app.tenant.PowerTenant;
import com.github.robozonky.internal.tenant.LazyEvent;
import com.github.robozonky.internal.tenant.Tenant;
import com.github.robozonky.internal.test.DateUtil;

/**
 * Keeps active delinquencies over a given threshold. When a new delinquency over a particular threshold arrives, an
 * event is sent. When a delinquency is no longer among active, it is silently removed.
 */
enum Category {

    NEW(0),
    MILD(10),
    SEVERE(30),
    CRITICAL(60),
    HOPELESS(90),
    /**
     * This has a bit of a special meaning. Any delinquent can be both defaulted and either new, mild, severe, critical
     * or hopeless.
     */
    DEFAULTED(-1);

    private static final Logger LOGGER = LogManager.getLogger(Category.class);
    private final int thresholdInDays;

    Category(final int thresholdInDays) {
        this.thresholdInDays = thresholdInDays;
    }

    private static EventSupplier getEventSupplierConstructor(final int threshold) {
        switch (threshold) {
            case -1:
                return EventFactory::loanDefaulted;
            case 0:
                return (investment, loan, since) -> loanNowDelinquent(investment, loan);
            case 10:
                return (investment, loan, since) -> loanDelinquent10plus(investment, loan);
            case 30:
                return (investment, loan, since) -> loanDelinquent30plus(investment, loan);
            case 60:
                return (investment, loan, since) -> loanDelinquent60plus(investment, loan);
            case 90:
                return (investment, loan, since) -> loanDelinquent90plus(investment, loan);
            default:
                throw new IllegalArgumentException("Wrong delinquency threshold: " + threshold);
        }
    }

    @SuppressWarnings("unchecked")
    private static LazyEvent getLazyEventSupplier(final int threshold,
            final Supplier e) {
        switch (threshold) {
            case -1:
                return loanDefaultedLazy((Supplier) e);
            case 0:
                return loanNowDelinquentLazy((Supplier) e);
            case 10:
                return loanDelinquent10plusLazy((Supplier) e);
            case 30:
                return loanDelinquent30plusLazy((Supplier) e);
            case 60:
                return loanDelinquent60plusLazy((Supplier) e);
            case 90:
                return loanDelinquent90plusLazy((Supplier) e);
            default:
                throw new IllegalArgumentException("Wrong delinquency threshold: " + threshold);
        }
    }

    private static SessionEvent supplyEvent(final Tenant tenant, final Investment investment, final int threshold) {
        LOGGER.trace("Retrieving event for investment #{}.", investment.getId());
        var since = DateUtil.zonedNow()
            .minusDays(investment.getLoan()
                .getDpd())
            .toLocalDate();
        var loanId = investment.getLoan()
            .getId();
        var loan = tenant.getLoan(loanId);
        var e = getEventSupplierConstructor(threshold)
            .apply(investment, loan, since);
        LOGGER.trace("Done.");
        return e;
    }

    private static LazyEvent getEvent(final Tenant tenant, final Investment investment,
            final int threshold) {
        return getLazyEventSupplier(threshold, () -> supplyEvent(tenant, investment, threshold));
    }

    public int getThresholdInDays() {
        return thresholdInDays;
    }

    public Stream getLesser() {
        return Arrays.stream(Category.values())
            .filter(category -> category.thresholdInDays < this.thresholdInDays && category.thresholdInDays >= 0);
    }

    /**
     * Update internal state trackers and send events if necessary.
     * 
     * @param tenant     Tenant to execute over.
     * @param investment Investment to process.
     */
    public void process(final PowerTenant tenant, final Investment investment) {
        LOGGER.debug("Updating {}.", this);
        tenant.fire(getEvent(tenant, investment, thresholdInDays));
    }

    @FunctionalInterface
    private interface EventSupplier {

        SessionEvent apply(final Investment i, final Loan l, final LocalDate d);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy