package cpsc2150.banking.models;

// Import Math library for pow()
import java.lang.Math;

/**
 * Class which determines information about a customer's Mortgage application, including the interest rate, the
 * monthly payment, the principal amount, the number of payments, whether the mortgage should be approved, and etc.
 *
 * @correspondence
 *      Payment = monthlyPayment AND
 *      Rate = interestRate AND
 *      Customer = customer AND
 *      DebtToIncomeRatio = debtToIncomeRatio AND
 *      Principal = principalAmount AND
 *      NumberOfPayments = numPayments AND
 *      PercentDown = percentDown
 *
 * @invariant
 *      monthlyPayment = (interestRate * principalAmount) / (1 - (1 + interestRate)^-numPayments) AND
 *      0 <= interestRate <= 1 AND
 *      0 < debtToIncomeRatio AND
 *      MIN_YEARS * MONTHS_IN_YEAR <= numPayments <= MAX_YEARS * MONTHS_IN_YEAR AND
 *      0 < principalAmount AND
 *      0 <= percentDown < 1
 */
public class Mortgage extends AbsMortgage implements IMortgage {

    private double monthlyPayment;
    private double interestRate;
    private ICustomer customer;
    private double debtToIncomeRatio;
    private double principalAmount;
    private int numPayments;
    private double percentDown;

    /**
     * Constructor for Mortgage that initializes values for private variables based on input of
     * the home's total cost, the down payment, the number of years for the mortgage, and existing
     * customer info.
     *
     * @param costOfHome The total cost of the home
     * @param downPayment The down payment
     * @param numYears The number of years for the mortgage
     * @param cust The customer with their existing financial info
     *
     * @pre
     *      0 < costOfHome AND 0 <= downPayment < costOfHome AND MIN_YEARS <= numYears <= MAX_YEARS AND [ cust is initialized ]
     *
     * @post
     *      customer = #cust AND
     *      numPayments = #numYears * MONTHS_IN_YEAR AND
     *      principalAmount = #costOfHome - #downPayment AND
     *      percentDown = #downPayment / #costOfHome AND
     *      interestRate = (BASERATE + [ rate dependent on length of mortgage ] + [ rate dependent on down payment ] + [ rate dependent on credit score ]) / MONTHS_IN_YEAR AND
     *      monthlyPayment = (#interestRate * #principalAmount) / (1 - (1 + #interestRate)^(-#numPayments)) AND
     *      debtToIncomeRatio = (#customer.getMonthlyDebtPayments + #monthlyPayment) / (#customer.getIncome / MONTHS_IN_YEAR)
     */
    Mortgage(double costOfHome, double downPayment, int numYears, ICustomer cust) {

        // Assign private customer variable to parameter cust
        customer = cust;

        // Calculate numPayments
        numPayments = (numYears * MONTHS_IN_YEAR);

        // Calculate principalAmount
        principalAmount = (costOfHome - downPayment);

        // Calculate percentDown
        percentDown = (downPayment / costOfHome);

        // Start by setting interestRate to the base rate
        interestRate = BASERATE;
        // If the length of the mortgage is less than the maximum for a certain
        // rate, add the "good" rate
        if (numYears < MAX_YEARS) {

            interestRate += GOODRATEADD;

            // Otherwise, add the "normal" rate
        } else {

            interestRate += NORMALRATEADD;

        }
        // If percentDown is less than the preferred percent down, add the "good" rate
        if (percentDown < PREFERRED_PERCENT_DOWN) {

            interestRate += GOODRATEADD;

        }
        // If the customer's credit score is below the threshold for "bad" credit, add the "very bad" rate
        if (customer.getCreditScore() < BADCREDIT) {

            interestRate += VERYBADRATEADD;

            // If the customer's credit score is above the threshold for "bad" credit, but below
            // the threshold for "fair" credit, add the "bad" rate
        } else if (customer.getCreditScore() >= BADCREDIT && customer.getCreditScore() < FAIRCREDIT) {

            interestRate += BADRATEADD;

            // If the customer's credit score is above the threshold for "fair" credit, but below
            // the threshold for "good" credit, add the "normal" rate
        } else if (customer.getCreditScore() >= FAIRCREDIT && customer.getCreditScore() < GOODCREDIT) {

            interestRate += NORMALRATEADD;

            // If the customer's credit score is above the threshold for "good" credit, but below
            // the threshold for "great" credit, add the "good" rate
        } else if (customer.getCreditScore() >= GOODCREDIT && customer.getCreditScore() < GREATCREDIT) {

            interestRate += GOODRATEADD;

        }

        // Divide interestRate by the number of months in a year
        interestRate = interestRate / MONTHS_IN_YEAR;

        // Calculate monthlyPayment
        monthlyPayment = ((interestRate * principalAmount) / (1 - Math.pow((1 + interestRate), (-1 * (numPayments)))));

        // Calculate debtToIncomeRatio
        debtToIncomeRatio = ((customer.getMonthlyDebtPayments() + monthlyPayment) / (customer.getIncome() / MONTHS_IN_YEAR));

    }

    public boolean loanApproved() {

        // Compare interest rate by calling helper function
        if (getRate() >= RATETOOHIGH || percentDown < MIN_PERCENT_DOWN || debtToIncomeRatio > DTOITOOHIGH) {

            return false;

        } else {

            return true;

        }

    }

    public double getPayment() {

        return monthlyPayment;

    }

    public double getRate() {

        // Calculate yearly interest rate based on the monthly interest rate
        return (interestRate * MONTHS_IN_YEAR);

    }

    public double getPrincipal() {

        return principalAmount;

    }

    public int getYears() {

        // Calculate number of years based on the number of monthly payments
        return (numPayments / MONTHS_IN_YEAR);

    }

}
