
export const calculateBankFeeDeduction = (paymentAmount, bankFeeBucket) => {
  const payBank = Math.min(Math.max(0, bankFeeBucket), paymentAmount);
  return payBank;
};

export const calculateRemainingFees = (paymentAmount, feeBucket) => {
    const payFee = Math.min(Math.max(0, feeBucket), paymentAmount);
    return payFee;
}

export const buildAllocationOrder = (buckets, paymentAmount) => {
    // Returns object with allocated amounts for this payment and updated buckets
    let remaining = paymentAmount;
    const allocations = {
        bankFee: 0,
        interest: 0,
        lateFee: 0,
        legalFee: 0,
        adminFee: 0,
        violation: 0,
        assessment: 0,
        unappliedCredit: 0
    };
    
    // Payments MUST only apply to Income Charges (fees, assessments, etc.)
    // Priority 1: Bank Fees (Income Charge)
    allocations.bankFee = calculateBankFeeDeduction(remaining, buckets.bankFee);
    remaining -= allocations.bankFee;
    buckets.bankFee -= allocations.bankFee;
    
    // Priority 2: Interest (Income Charge)
    allocations.interest = calculateRemainingFees(remaining, buckets.interest);
    remaining -= allocations.interest;
    buckets.interest -= allocations.interest;

    // Priority 3: Late Fees (Income Charge)
    allocations.lateFee = calculateRemainingFees(remaining, buckets.late);
    remaining -= allocations.lateFee;
    buckets.late -= allocations.lateFee;

    // Priority 4: Legal Fees (Income Charge)
    allocations.legalFee = calculateRemainingFees(remaining, buckets.legal);
    remaining -= allocations.legalFee;
    buckets.legal -= allocations.legalFee;

    // Priority 5: Admin Fees (Income Charge)
    allocations.adminFee = calculateRemainingFees(remaining, buckets.admin);
    remaining -= allocations.adminFee;
    buckets.admin -= allocations.adminFee;
    
    // Priority 6: Violations (Income Charge)
    allocations.violation = calculateRemainingFees(remaining, buckets.violation);
    remaining -= allocations.violation;
    buckets.violation -= allocations.violation;

    // Priority 7: Assessments (Income Charge - Remainder)
    if (remaining > 0) {
        const payAssessment = Math.min(Math.max(0, buckets.assessment), remaining);
        allocations.assessment = payAssessment;
        remaining -= payAssessment;
        buckets.assessment -= payAssessment;
    }

    if (remaining > 0) {
        allocations.unappliedCredit = remaining;
        buckets.unappliedCredit += remaining;
    }
    
    return { allocations, updatedBuckets: buckets };
};

export const calculateLedger = (rows, interestRate = 0.0) => {
    let runningBalance = 0;
    
    // Buckets
    const buckets = {
        assessment: 0,
        late: 0,
        legal: 0,
        admin: 0,
        violation: 0,
        interest: 0,
        bankFee: 0,
        unappliedCredit: 0
    };
    
    const processedRows = rows.map(row => {
        // Parse values
        const assessment = parseFloat(row.assessment) || 0;
        const lateFee = parseFloat(row.lateFee) || 0;
        const adminFee = parseFloat(row.adminFee) || 0;
        const legalFee = parseFloat(row.legalFee) || 0;
        const violation = parseFloat(row.violation) || 0;
        const bankFee = parseFloat(row.bankFee) || 0;
        
        // Ensure payments (credits) only offset income charges
        const payment = parseFloat(row.payment) || 0;
        
        let interest = parseFloat(row.interest) || 0;
        
        // Auto-Calculate Interest
        if (row.isAutoInterest) {
             const baseForInterest = Math.max(0, buckets.assessment); 
             interest = parseFloat((baseForInterest * parseFloat(interestRate)).toFixed(2));
        }
        
        // Add charges / debits. Existing unapplied credits offset future charges first.
        const applyCharge = (bucketName, amount) => {
            if (amount > 0 && buckets.unappliedCredit > 0) {
                const offset = Math.min(amount, buckets.unappliedCredit);
                buckets.unappliedCredit -= offset;
                buckets[bucketName] += amount - offset;
            } else {
                buckets[bucketName] = Math.max(0, buckets[bucketName] + amount);
            }
        };

        applyCharge('assessment', assessment);
        applyCharge('late', lateFee);
        applyCharge('admin', adminFee);
        applyCharge('legal', legalFee);
        applyCharge('violation', violation);
        applyCharge('bankFee', bankFee);
        applyCharge('interest', interest);
        
        // Apply Payment (Credits)
        let allocations = {};
        if (payment > 0) {
             // We pass a reference to buckets, which gets modified in place by buildAllocationOrder
             const result = buildAllocationOrder(buckets, payment); 
             allocations = result.allocations;
        }
        
        // Update Running Balance
        runningBalance = Object.entries(buckets).reduce((total, [key, value]) => {
            return key === 'unappliedCredit' ? total - value : total + value;
        }, 0);
        
        return {
            ...row,
            interest: row.isAutoInterest ? interest : (parseFloat(row.interest) || 0),
            balance: runningBalance,
            allocations
        };
    });
    
    // Calculate Totals
    const totals = {
        assessment: processedRows.reduce((acc, r) => acc + (parseFloat(r.assessment) || 0), 0),
        lateFee: processedRows.reduce((acc, r) => acc + (parseFloat(r.lateFee) || 0), 0),
        adminFee: processedRows.reduce((acc, r) => acc + (parseFloat(r.adminFee) || 0), 0),
        legalFee: processedRows.reduce((acc, r) => acc + (parseFloat(r.legalFee) || 0), 0),
        violation: processedRows.reduce((acc, r) => acc + (parseFloat(r.violation) || 0), 0),
        interest: processedRows.reduce((acc, r) => acc + (r.interest || 0), 0),
        bankFee: processedRows.reduce((acc, r) => acc + (parseFloat(r.bankFee) || 0), 0),
        payment: processedRows.reduce((acc, r) => acc + (parseFloat(r.payment) || 0), 0),
        creditBalance: buckets.unappliedCredit,
        totalDue: runningBalance
    };
    
    return {
        rows: processedRows,
        totals,
        buckets
    };
};

export const validateIncomeIsDebit = (type, amount) => {
    if (type === 'income' && amount < 0) return false;
    return true;
};
