package com.capgemini.reports.definition.r20report.checker;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.object.pom.status.StatusEnum;
import com.capgemini.reports.definition.r20report.ExecutionR20;
import com.capgemini.reports.exceptions.InvalidUserInputException;
import com.capgemini.reports.exceptions.UserInputErrorEnum;
import com.capgemini.reports.releasestatus.RevisionStatusAndDate;
import com.capgemini.reports.reports.parameters.ProductUnit;
import com.capgemini.reports.reports.parameters.checker.ProductUnitChecker;
import com.capgemini.reports.reports.parameters.checker.UnitRevisionChecker;
import com.capgemini.reports.teamcenter.ObjectInfoHelper;
import com.google.common.base.Optional;
import com.teamcenter.soa.client.model.strong.ItemRevision;

import java.util.ArrayList;
import java.util.List;

/**
 * This class checks if a given UnitRevision is valid for a R20 report.<br>
 * It assumes that the Product and the unit exist (using a {@link ProductUnitChecker}. <br>
 * It fails if the unit does not have an effectivity matching the product ID.
 */
public class R20UnitRevisionChecker extends UnitRevisionChecker {

    private final BeforeOrAfter beforeOrAfter;

    /**
     * Constructor for R20 report.
     *
     * @param productUnit the {@link ProductUnit Product and the Unit} that need to be checked.
     * @param isAfter     boolean Take true if this ProductUnit is an "After". Take false if it is a "Before".
     */
    public R20UnitRevisionChecker(final ProductUnit productUnit, final boolean isAfter) {
        super(productUnit);
        if (isAfter) {
            this.beforeOrAfter = BeforeOrAfter.AFTER;
        } else {
            this.beforeOrAfter = BeforeOrAfter.BEFORE;
        }
    }

    @Override
    protected List<ItemRevision> checkUnits(final TcSession session) throws InvalidUserInputException {
        List<ItemRevision> unitsOfProductInRange = super.checkUnits(session);
        StatusEnum[] allowedStatuses = beforeOrAfter == BeforeOrAfter.BEFORE ? ExecutionR20.R20_STATUS_BEFORE : ExecutionR20.R20_STATUS_AFTER;
        // Obtain the latest maturity status overall
        RevisionStatusAndDate latest;
        Optional<RevisionStatusAndDate> maybeLatestUnit =
                ObjectInfoHelper.getRevisionAndStatusWithMostRecentReleaseDate(session, unitsOfProductInRange, null, StatusEnum.UNIT_MATURITY);
        if (!maybeLatestUnit.isPresent()) {
            throw latestUnitDoesNotHaveTheCorrectMaturity(); // Actually, it doesn't have a maturity status at all :O
        } else {
            latest = maybeLatestUnit.get();
        }

        // Verify the latest maturity status is of an allowed type
        if (StatusEnum.allow(allowedStatuses).isExcluded(latest.getReleaseStatus())) {
            throw latestUnitDoesNotHaveTheCorrectMaturity(); // Maturity is excluded!
        }
        List<ItemRevision> result = new ArrayList<>();
        result.add(latest.getItemRevision());
        return result;
    }

    /**
     * Just adapting this error message for R20.
     *
     * @return the custom exception
     */
    @Override
    protected InvalidUserInputException unitNotAssociatedToProduct() {
        if (this.beforeOrAfter.equals(BeforeOrAfter.AFTER)) {
            return new InvalidUserInputException(UserInputErrorEnum.THE_UNIT_AFTER_IS_NOT_ASSOCIATED_TO_THE_PRODUCT_AFTER);
        } else {
            return new InvalidUserInputException(UserInputErrorEnum.THE_UNIT_BEFORE_IS_NOT_ASSOCIATED_TO_THE_PRODUCT_BEFORE);
        }
    }

    private InvalidUserInputException latestUnitDoesNotHaveTheCorrectMaturity() {
        if (this.beforeOrAfter.equals(BeforeOrAfter.AFTER)) {
            return new InvalidUserInputException(UserInputErrorEnum.THE_LATEST_UNIT_AFTER_DOES_NOT_HAVE_THE_APPROPRIATE_MATURITY);
        } else {
            return new InvalidUserInputException(UserInputErrorEnum.THE_LATEST_UNIT_BEFORE_DOES_NOT_HAVE_THE_APPROPRIATE_MATURITY);
        }
    }

    private enum BeforeOrAfter {
        BEFORE,
        AFTER
    }
}
