/**
 * Finance Math Utilities for Vareon Calculator Suite Lite
 * 
 * Core financial calculation functions used by loan, mortgage, and savings calculators.
 * This is a streamlined version of the Pro business-math.js library.
 */

/**
 * Ensures a value is a finite number.
 * @param {*} value - The value to check
 * @param {string} name - Name for error messages
 * @returns {number} The validated number
 * @throws {Error} If value is not a finite number
 */
export function ensureNumber(value, name = 'value') {
    const num = Number(value);
    if (!Number.isFinite(num)) {
        throw new Error(`${name} must be a finite number`);
    }
    return num;
}

/**
 * Ensures a value is a finite number, or returns a fallback.
 * @param {*} value - The value to check
 * @param {number} fallback - Default value if input is null/undefined/empty
 * @param {string} name - Name for error messages
 * @returns {number} The validated number or fallback
 */
export function ensureOptionalNumber(value, fallback = 0, name = 'value') {
    if (value === null || value === undefined || value === '') {
        return fallback;
    }
    return ensureNumber(value, name);
}

/**
 * Check if a value is a finite number.
 * @param {*} value - The value to check
 * @returns {boolean}
 */
export function isFiniteNumber(value) {
    return typeof value === 'number' && Number.isFinite(value);
}

/**
 * Safely divide two numbers, handling edge cases.
 * @param {number} numerator 
 * @param {number} denominator 
 * @returns {number} Result or NaN/Infinity for edge cases
 */
export function safeDivide(numerator, denominator) {
    if (!Number.isFinite(numerator) || !Number.isFinite(denominator)) {
        return NaN;
    }
    if (denominator === 0) {
        if (numerator === 0) {
            return NaN;
        }
        return numerator > 0 ? Infinity : -Infinity;
    }
    return numerator / denominator;
}

/**
 * Clamp a number between min and max values.
 * @param {number} value - The value to clamp
 * @param {number} min - Minimum value
 * @param {number} max - Maximum value
 * @returns {number} Clamped value
 */
export function clampNumber(value, min, max) {
    if (!Number.isFinite(value)) {
        return NaN;
    }
    if (min !== undefined && value < min) {
        return min;
    }
    if (max !== undefined && value > max) {
        return max;
    }
    return value;
}

/**
 * Calculate periodic payment for a loan (PMT function).
 * Returns a negative number representing the payment amount.
 * 
 * @param {number} rate - Interest rate per period (e.g., monthly rate = APR / 12 / 100)
 * @param {number} periods - Total number of payment periods
 * @param {number} principal - Loan amount (present value)
 * @returns {number} Payment amount (negative, representing outflow)
 * 
 * @example
 * // $100,000 loan at 6% APR for 30 years
 * const monthlyRate = 0.06 / 12;
 * const payment = calcPmt(monthlyRate, 360, 100000);
 * // Returns approximately -599.55
 */
export function calcPmt(rate, periods, principal) {
    if (periods <= 0) {
        return NaN;
    }
    if (rate === 0) {
        return -(principal / periods);
    }
    const factor = Math.pow(1 + rate, periods);
    if (!Number.isFinite(factor)) {
        return NaN;
    }
    return -(principal * rate * factor) / (factor - 1);
}

/**
 * Calculate future value of a lump sum with compound interest.
 * 
 * @param {number} presentValue - Initial amount
 * @param {number} rate - Interest rate per period
 * @param {number} periods - Number of compounding periods
 * @returns {number} Future value
 * 
 * @example
 * // $10,000 at 5% annual for 10 years (annual compounding)
 * const fv = futureValue(10000, 0.05, 10);
 * // Returns approximately 16288.95
 */
export function futureValue(presentValue, rate, periods) {
    if (rate === 0) {
        return presentValue;
    }
    const result = presentValue * Math.pow(1 + rate, periods);
    return Number.isFinite(result) ? result : NaN;
}

/**
 * Calculate future value of a series of equal payments (annuity).
 * 
 * @param {number} payment - Payment amount per period
 * @param {number} rate - Interest rate per period
 * @param {number} periods - Number of periods
 * @param {string} timing - 'end' (ordinary annuity) or 'begin' (annuity due)
 * @returns {number} Future value of the annuity
 * 
 * @example
 * // $500/month at 6% APR for 20 years
 * const monthlyRate = 0.06 / 12;
 * const fv = futureValueAnnuity(500, monthlyRate, 240, 'end');
 */
export function futureValueAnnuity(payment, rate, periods, timing = 'end') {
    if (periods <= 0) {
        return 0;
    }
    if (rate === 0) {
        return payment * periods;
    }

    const timingFactor = timing === 'begin' ? (1 + rate) : 1;
    const factor = Math.pow(1 + rate, periods);

    if (!Number.isFinite(factor)) {
        return NaN;
    }

    return payment * ((factor - 1) / rate) * timingFactor;
}

/**
 * Calculate compound interest growth with regular contributions.
 * This is the main function used by the Savings calculator.
 * 
 * @param {Object} input - Input parameters
 * @param {number} input.principal - Initial investment
 * @param {number} input.monthlyContribution - Monthly contribution amount
 * @param {number} input.annualRate - Annual interest rate (as decimal, e.g., 0.06 for 6%)
 * @param {number} input.years - Investment period in years
 * @param {string} input.contributionTiming - 'begin' or 'end' of period
 * @returns {Object} Calculation results
 */
export function calcSavingsGrowth(input) {
    const {
        principal = 0,
        monthlyContribution = 0,
        annualRate = 0,
        years = 0,
        contributionTiming = 'end',
    } = input;

    const periods = years * 12;
    const monthlyRate = annualRate / 12;

    const fvPrincipal = futureValue(principal, monthlyRate, periods);

    const fvContributions = futureValueAnnuity(monthlyContribution, monthlyRate, periods, contributionTiming);

    const totalFutureValue = fvPrincipal + fvContributions;
    const totalContributions = principal + (monthlyContribution * periods);
    const totalInterest = totalFutureValue - totalContributions;

    return {
        futureValue: totalFutureValue,
        totalContributions,
        totalInterest,
        effectiveRate: years > 0 ? Math.pow(totalFutureValue / Math.max(principal || 1, 1), 1 / years) - 1 : 0,
    };
}

/**
 * Calculate loan amortization details.
 * 
 * @param {Object} input - Input parameters
 * @param {number} input.principal - Loan amount
 * @param {number} input.annualRate - Annual interest rate (as decimal)
 * @param {number} input.years - Loan term in years
 * @returns {Object} Loan calculation results
 */
export function calcLoanPayment(input) {
    const {
        principal = 0,
        annualRate = 0,
        years = 0,
    } = input;

    if (principal <= 0 || years <= 0) {
        return {
            monthlyPayment: 0,
            totalPayment: 0,
            totalInterest: 0,
        };
    }

    const monthlyRate = annualRate / 12;
    const totalPayments = years * 12;

    const monthlyPayment = Math.abs(calcPmt(monthlyRate, totalPayments, principal));
    const totalPayment = monthlyPayment * totalPayments;
    const totalInterest = totalPayment - principal;

    return {
        monthlyPayment,
        totalPayment,
        totalInterest,
        numberOfPayments: totalPayments,
    };
}

/**
 * Calculate mortgage payment with additional costs (taxes, insurance, HOA).
 * 
 * @param {Object} input - Input parameters
 * @param {number} input.homePrice - Home purchase price
 * @param {number} input.downPayment - Down payment amount
 * @param {number} input.annualRate - Annual interest rate (as decimal)
 * @param {number} input.years - Loan term in years
 * @param {number} input.propertyTax - Annual property tax
 * @param {number} input.insurance - Annual insurance
 * @param {number} input.hoa - Monthly HOA fee
 * @returns {Object} Mortgage calculation results
 */
export function calcMortgagePayment(input) {
    const {
        homePrice = 0,
        downPayment = 0,
        annualRate = 0,
        years = 30,
        propertyTax = 0,
        insurance = 0,
        hoa = 0,
    } = input;

    const loanAmount = homePrice - downPayment;

    if (loanAmount <= 0 || years <= 0) {
        return {
            principalAndInterest: 0,
            monthlyTax: 0,
            monthlyInsurance: 0,
            monthlyHoa: hoa,
            totalMonthlyPayment: hoa,
            totalPayment: 0,
            totalInterest: 0,
            loanAmount: 0,
        };
    }

    const loanDetails = calcLoanPayment({
        principal: loanAmount,
        annualRate,
        years,
    });

    const monthlyTax = propertyTax / 12;
    const monthlyInsurance = insurance / 12;
    const totalMonthlyPayment = loanDetails.monthlyPayment + monthlyTax + monthlyInsurance + hoa;

    return {
        principalAndInterest: loanDetails.monthlyPayment,
        monthlyTax,
        monthlyInsurance,
        monthlyHoa: hoa,
        totalMonthlyPayment,
        totalPayment: loanDetails.totalPayment,
        totalInterest: loanDetails.totalInterest,
        loanAmount,
        numberOfPayments: loanDetails.numberOfPayments,
    };
}

/**
 * Calculate ROI (Return on Investment).
 * 
 * @param {number} gain - Total gain (final value - initial investment)
 * @param {number} cost - Initial investment cost
 * @returns {number} ROI as a decimal (e.g., 0.25 for 25%)
 */
export function calcRoi(gain, cost) {
    return safeDivide(gain, cost);
}

/**
 * Calculate simple discount.
 * 
 * @param {number} originalPrice - Original price
 * @param {number} discountPercent - Discount percentage (e.g., 20 for 20%)
 * @returns {Object} Discount calculation results
 */
export function calcDiscount(originalPrice, discountPercent) {
    const discountAmount = originalPrice * (discountPercent / 100);
    const finalPrice = originalPrice - discountAmount;

    return {
        originalPrice,
        discountPercent,
        discountAmount,
        finalPrice,
        savings: discountAmount,
    };
}

/**
 * Calculate tip and split.
 * 
 * @param {number} billAmount - Bill total
 * @param {number} tipPercent - Tip percentage (e.g., 18 for 18%)
 * @param {number} numberOfPeople - Number of people to split between
 * @returns {Object} Tip calculation results
 */
export function calcTip(billAmount, tipPercent, numberOfPeople = 1) {
    const tipAmount = billAmount * (tipPercent / 100);
    const totalAmount = billAmount + tipAmount;
    const perPersonBill = safeDivide(billAmount, numberOfPeople);
    const perPersonTip = safeDivide(tipAmount, numberOfPeople);
    const perPersonTotal = safeDivide(totalAmount, numberOfPeople);

    return {
        billAmount,
        tipPercent,
        tipAmount,
        totalAmount,
        perPersonBill,
        perPersonTip,
        perPersonTotal,
        numberOfPeople,
    };
}

/**
 * Calculate credit card payoff.
 * 
 * @param {number} balance - Current balance
 * @param {number} annualRate - Annual interest rate (as decimal)
 * @param {number} monthlyPayment - Monthly payment amount
 * @returns {Object} Payoff calculation results
 */
export function calcCreditCardPayoff(balance, annualRate, monthlyPayment) {
    if (balance <= 0) {
        return {
            monthsToPayoff: 0,
            totalInterest: 0,
            totalPayment: 0,
        };
    }

    const monthlyRate = annualRate / 12;

    const minPaymentRequired = balance * monthlyRate;
    if (monthlyPayment <= minPaymentRequired) {
        return {
            monthsToPayoff: Infinity,
            totalInterest: Infinity,
            totalPayment: Infinity,
            error: 'Payment too low to pay off balance',
        };
    }

    let monthsToPayoff;
    if (monthlyRate === 0) {
        monthsToPayoff = Math.ceil(balance / monthlyPayment);
    } else {
        monthsToPayoff = Math.ceil(
            -Math.log(1 - (balance * monthlyRate) / monthlyPayment) / Math.log(1 + monthlyRate)
        );
    }

    const totalPayment = monthlyPayment * monthsToPayoff;
    const totalInterest = totalPayment - balance;

    return {
        monthsToPayoff,
        yearsToPayoff: monthsToPayoff / 12,
        totalInterest,
        totalPayment,
        monthlyPayment,
    };
}

/**
 * Calculate fuel cost for a trip.
 * 
 * @param {number} distance - Trip distance
 * @param {number} fuelEfficiency - Miles per gallon (or km per liter)
 * @param {number} fuelPrice - Price per gallon (or liter)
 * @returns {Object} Fuel cost calculation results
 */
export function calcFuelCost(distance, fuelEfficiency, fuelPrice) {
    const fuelNeeded = safeDivide(distance, fuelEfficiency);
    const totalCost = fuelNeeded * fuelPrice;
    const costPerMile = safeDivide(fuelPrice, fuelEfficiency);

    return {
        distance,
        fuelEfficiency,
        fuelPrice,
        fuelNeeded,
        totalCost,
        costPerMile,
    };
}
