<?php

namespace App\Services;

use App\Models\Customer;
use App\Models\CustomerCreditScore;
use App\Models\DisbursedLoan;
use App\Models\PaymentSchedule;
use App\Models\Repayment;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class CreditScoreService
{
    private $baseScore = 300;
    private $maxScore = 850;

    /**
     * Initialize credit score for a new customer
     */
    public function initializeCustomerScore($customerId)
    {
        return CustomerCreditScore::create([
            'customer_id' => $customerId,
            'credit_score' => $this->baseScore,
            'score_band' => 'Very Poor',
            'score_date' => now(),
            'is_current' => true,
            'created_by' => auth()->id() ?? 1,
            'change_reason' => 'Initial credit score',
            'change_type' => 'initialization',
        ]);
    }

    /**
     * Update credit score based on specific event
     */
    public function updateScoreForEvent($customerId, $eventType, $eventData = [])
    {
        return DB::transaction(function () use ($customerId, $eventType, $eventData) {
            // Get current score
            $currentScore = CustomerCreditScore::current()
                ->byCustomer($customerId)
                ->first();

            if (!$currentScore) {
                $currentScore = $this->initializeCustomerScore($customerId);
            }

            // Calculate new score
            $newScoreData = $this->calculateNewScore($currentScore, $eventType, $eventData);
            
            // Create new score record
            $newScore = CustomerCreditScore::create($newScoreData);
            
            // Mark previous scores as historical
            $newScore->markPreviousScoresAsHistorical();
            
            return $newScore;
        });
    }

    /**
     * Daily batch update for all customers
     */
    public function runDailyUpdate()
    {
        $customers = Customer::where('status', 'active')->get();
        $updatedCount = 0;

        foreach ($customers as $customer) {
            try {
                $this->updateScoreForEvent($customer->id, 'daily_update');
                $updatedCount++;
            } catch (\Exception $e) {
                // Log error but continue with other customers
                \Log::error("Failed to update credit score for customer {$customer->id}: " . $e->getMessage());
            }
        }

        return $updatedCount;
    }

    /**
     * Calculate new score based on event
     */
    private function calculateNewScore(CustomerCreditScore $currentScore, $eventType, $eventData = [])
    {
        $newScoreValue = $currentScore->credit_score;
        $changeReason = '';
        $changePoints = 0;

        switch ($eventType) {
            case 'repayment_made':
                $changePoints = $this->calculateRepaymentImpact($eventData);
                $changeReason = 'Loan repayment received';
                break;
                
            case 'interest_booked':
                $changePoints = $this->calculateInterestBookingImpact($eventData);
                $changeReason = 'Interest income booked - Active credit usage';
                break;
                
            case 'loan_disbursed':
                $changePoints = $this->calculateLoanDisbursementImpact($eventData);
                $changeReason = 'New loan disbursed - Credit inquiry';
                break;
                
            case 'payment_missed':
                $changePoints = -25;
                $changeReason = 'Payment missed or late - Negative payment behavior';
                break;
                
            case 'loan_defaulted':
                $changePoints = -50;
                $changeReason = 'Loan defaulted - Serious credit issue';
                break;
                
            case 'loan_closed':
                $changePoints = $this->calculateLoanClosureImpact($eventData);
                $changeReason = 'Loan successfully closed - Positive credit history';
                break;
                
            case 'daily_update':
                $changePoints = $this->calculateDailyUpdateImpact($currentScore);
                $changeReason = 'Daily credit assessment update';
                break;
                
            default:
                $changePoints = 0;
                $changeReason = 'Score maintenance adjustment';
        }

        // Apply boundaries
        $newScoreValue = max($this->baseScore, min($this->maxScore, $newScoreValue + $changePoints));

        // Calculate all metrics
        $metrics = $this->calculateAllMetrics($currentScore->customer_id);

        return array_merge([
            'customer_id' => $currentScore->customer_id,
            'credit_score' => $newScoreValue,
            'score_band' => CustomerCreditScore::getScoreBand($newScoreValue),
            'previous_score' => $currentScore->credit_score,
            'score_change' => $changePoints,
            'change_reason' => $changeReason,
            'change_type' => $eventType,
            'score_date' => now(),
            'is_current' => true,
            'created_by' => auth()->id() ?? 1,
        ], $metrics);
    }

    /**
     * Calculate impact of repayment on credit score
     */
    private function calculateRepaymentImpact($repaymentData)
    {
        $points = 0;
        
        // On-time repayment
        if ($repaymentData['is_on_time'] ?? true) {
            $points += 5; // Base reward for on-time payment
            
            // Reward larger payments (shows good cash flow)
            $amount = $repaymentData['amount'] ?? 0;
            if ($amount > 1000) $points += 2;
            if ($amount > 5000) $points += 3;
            
            // Extra points for paying more than minimum
            if (isset($repaymentData['is_above_minimum']) && $repaymentData['is_above_minimum']) {
                $points += 3;
            }
        } else {
            $points -= 10; // Late payment penalty
            
            // Additional penalty for very late payments
            $daysLate = $repaymentData['days_late'] ?? 0;
            if ($daysLate > 30) $points -= 5;
            if ($daysLate > 60) $points -= 10;
        }
        
        return $points;
    }

    /**
     * Calculate impact of interest booking on credit score
     */
    private function calculateInterestBookingImpact($interestData)
    {
        $points = 0;
        
        // Interest booking itself is neutral, but we assess the overall credit health
        $principalBalance = $interestData['principal_balance'] ?? 0;
        $totalBalance = $interestData['total_balance'] ?? 0;
        $interestAmount = $interestData['interest_amount'] ?? 0;
        
        // If high balance relative to likely income, small negative impact
        if ($totalBalance > 50000) {
            $points -= 1;
        }
        
        // If interest is accumulating significantly, it might indicate stretched finances
        if ($interestAmount > 1000) {
            $points -= 1;
        }
        
        // If this is back-dated interest (catching up), it's a positive sign of system health
        if (isset($interestData['days_processed']) && $interestData['days_processed'] > 1) {
            $points += 1; // System is catching up on proper accounting
        }
        
        return $points;
    }

    /**
     * Calculate impact of new loan disbursement
     */
    private function calculateLoanDisbursementImpact($loanData)
    {
        $points = -2; // Small temporary decrease for new credit inquiry
        
        // If customer has good history, reduce the negative impact
        $customerId = $loanData['customer_id'] ?? null;
        if ($customerId) {
            $currentScore = CustomerCreditScore::current()->byCustomer($customerId)->first();
            if ($currentScore && $currentScore->credit_score > 600) {
                $points = -1; // Less impact for customers with good scores
            }
        }
        
        return $points;
    }

    /**
     * Calculate impact of loan closure
     */
    private function calculateLoanClosureImpact($loanData)
    {
        $points = 10; // Base reward for successful loan completion
        
        // Extra points for early repayment
        if (isset($loanData['closed_early']) && $loanData['closed_early']) {
            $points += 5;
        }
        
        // Extra points for perfect payment history
        if (isset($loanData['perfect_payment_history']) && $loanData['perfect_payment_history']) {
            $points += 5;
        }
        
        return $points;
    }

    /**
     * Calculate daily update impact based on current metrics
     */
    private function calculateDailyUpdateImpact(CustomerCreditScore $currentScore)
    {
        $points = 0;
        
        // Positive behavior rewards
        if ($currentScore->on_time_repayment_rate > 95) $points += 3;
        if ($currentScore->on_time_repayment_rate > 98) $points += 2; // Extra for excellent record
        
        if ($currentScore->credit_utilization_ratio < 30) $points += 2;
        if ($currentScore->credit_utilization_ratio < 10) $points += 2; // Extra for very low utilization
        
        if ($currentScore->days_delinquent == 0) $points += 2;
        
        if (!$currentScore->has_active_default) $points += 5;
        
        // Credit mix diversity (having different types of credit)
        if ($currentScore->total_loans > 1 && $currentScore->closed_loans > 0) {
            $points += 2; // Reward for diverse credit history
        }
        
        // Negative behavior penalties
        if ($currentScore->has_recent_late_payment) $points -= 5;
        if ($currentScore->has_high_utilization) $points -= 3;
        if ($currentScore->has_multiple_active_loans) $points -= 2;
        
        // Recent credit inquiries (multiple new loans in short period)
        $recentLoans = $this->countRecentLoans($currentScore->customer_id, 90); // Last 90 days
        if ($recentLoans > 2) {
            $points -= 3; // Penalty for seeking too much credit quickly
        }
        
        // Length of credit history bonus
        $creditHistoryLength = $this->getCreditHistoryLength($currentScore->customer_id);
        if ($creditHistoryLength > 365) { // More than 1 year
            $points += 3;
        }
        if ($creditHistoryLength > 730) { // More than 2 years
            $points += 2;
        }
        
        return $points;
    }

    /**
     * Calculate all credit metrics for a customer
     */
    private function calculateAllMetrics($customerId)
    {
        return [
            'days_delinquent' => $this->calculateDaysDelinquent($customerId),
            'total_loans' => $this->calculateTotalLoans($customerId),
            'active_loans' => $this->calculateActiveLoans($customerId),
            'closed_loans' => $this->calculateClosedLoans($customerId),
            'defaulted_loans' => $this->calculateDefaultedLoans($customerId),
            'total_borrowed' => $this->calculateTotalBorrowed($customerId),
            'current_outstanding' => $this->calculateCurrentOutstanding($customerId),
            'total_repaid' => $this->calculateTotalRepaid($customerId),
            'on_time_repayment_rate' => $this->calculateOnTimeRepaymentRate($customerId),
            'late_payments_count' => $this->calculateLatePaymentsCount($customerId),
            'missed_payments_count' => $this->calculateMissedPaymentsCount($customerId),
            'credit_utilization_ratio' => $this->calculateCreditUtilizationRatio($customerId),
            'available_credit' => $this->calculateAvailableCredit($customerId),
            'credit_limit' => $this->calculateCreditLimit($customerId),
            'last_repayment_date' => $this->getLastRepaymentDate($customerId),
            'last_repayment_amount' => $this->getLastRepaymentAmount($customerId),
            'last_loan_date' => $this->getLastLoanDate($customerId),
            'last_credit_inquiry' => $this->getLastCreditInquiry($customerId),
            'has_active_default' => $this->hasActiveDefault($customerId),
            'has_recent_late_payment' => $this->hasRecentLatePayment($customerId),
            'has_high_utilization' => $this->hasHighUtilization($customerId),
            'has_multiple_active_loans' => $this->hasMultipleActiveLoans($customerId),
        ];
    }

    // ==================== METRIC CALCULATION METHODS ====================

    private function calculateDaysDelinquent($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->where('status', 'active')
            ->sum('daysdelinquent');
    }

    private function calculateTotalLoans($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)->count();
    }

    private function calculateActiveLoans($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->where('status', 'active')
            ->count();
    }

    private function calculateClosedLoans($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->where('status', 'closed')
            ->count();
    }

    private function calculateDefaultedLoans($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->whereIn('status', ['default', 'written_off'])
            ->count();
    }

    private function calculateTotalBorrowed($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)->sum('amount');
    }

    private function calculateCurrentOutstanding($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->where('status', 'active')
            ->sum('totalbalance');
    }

    private function calculateTotalRepaid($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->sum('totalpaid');
    }

    private function calculateOnTimeRepaymentRate($customerId)
    {
        $totalPayments = PaymentSchedule::whereHas('loan', function($query) use ($customerId) {
            $query->where('customerid', $customerId);
        })->where('transactiontype', 'Payment')->count();

        $onTimePayments = PaymentSchedule::whereHas('loan', function($query) use ($customerId) {
            $query->where('customerid', $customerId);
        })->where('transactiontype', 'Payment')
          ->where('status', 'paid')
          ->whereColumn('paid_date', '<=', 'paymentdate')
          ->count();

        if ($totalPayments === 0) return 100.0;
        return round(($onTimePayments / $totalPayments) * 100, 2);
    }

    private function calculateLatePaymentsCount($customerId)
    {
        return PaymentSchedule::whereHas('loan', function($query) use ($customerId) {
            $query->where('customerid', $customerId);
        })->where('transactiontype', 'Payment')
          ->where('status', 'paid')
          ->whereColumn('paid_date', '>', 'paymentdate')
          ->count();
    }

    private function calculateMissedPaymentsCount($customerId)
    {
        return PaymentSchedule::whereHas('loan', function($query) use ($customerId) {
            $query->where('customerid', $customerId);
        })->where('transactiontype', 'Payment')
          ->where('status', 'overdue')
          ->count();
    }

    private function calculateCreditUtilizationRatio($customerId)
    {
        $outstanding = $this->calculateCurrentOutstanding($customerId);
        $creditLimit = $this->calculateCreditLimit($customerId);
        
        return CustomerCreditScore::calculateUtilizationRatio($outstanding, $creditLimit);
    }

    private function calculateAvailableCredit($customerId)
    {
        $creditLimit = $this->calculateCreditLimit($customerId);
        $outstanding = $this->calculateCurrentOutstanding($customerId);
        
        return max(0, $creditLimit - $outstanding);
    }

    private function calculateCreditLimit($customerId)
    {
        // Get customer income for credit limit calculation
        $customer = Customer::find($customerId);
        $income = $customer->income ?? 0;
        
        // Simple algorithm: 3x monthly income as credit limit
        // If no income data, use a conservative default
        if ($income <= 0) {
            $income = 5000; // Default assumption for customers without income data
        }
        
        $baseLimit = $income * 3;
        
        // Adjust based on credit history
        $creditScore = CustomerCreditScore::current()->byCustomer($customerId)->first();
        if ($creditScore) {
            if ($creditScore->credit_score >= 700) {
                $baseLimit *= 1.5; // 50% higher limit for excellent scores
            } elseif ($creditScore->credit_score >= 600) {
                $baseLimit *= 1.25; // 25% higher for good scores
            }
        }
        
        return round($baseLimit, 2);
    }

    private function getLastRepaymentDate($customerId)
    {
        $lastRepayment = Repayment::where('customerid', $customerId)
            ->orderBy('rdate', 'desc')
            ->first();
            
        return $lastRepayment ? $lastRepayment->rdate : null;
    }

    private function getLastRepaymentAmount($customerId)
    {
        $lastRepayment = Repayment::where('customerid', $customerId)
            ->orderBy('rdate', 'desc')
            ->first();
            
        return $lastRepayment ? $lastRepayment->rtotal : 0;
    }

    private function getLastLoanDate($customerId)
    {
        $lastLoan = DisbursedLoan::where('customerid', $customerId)
            ->orderBy('disburseddate', 'desc')
            ->first();
            
        return $lastLoan ? $lastLoan->disburseddate : null;
    }

    private function getLastCreditInquiry($customerId)
    {
        // This would typically come from application dates
        $lastApplication = DisbursedLoan::where('customerid', $customerId)
            ->orderBy('disburseddate', 'desc')
            ->first();
            
        return $lastApplication ? $lastApplication->disburseddate : null;
    }

    private function hasActiveDefault($customerId)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->where('status', 'default')
            ->exists();
    }

    private function hasRecentLatePayment($customerId)
    {
        return PaymentSchedule::whereHas('loan', function($query) use ($customerId) {
            $query->where('customerid', $customerId);
        })->where('transactiontype', 'Payment')
          ->where('status', 'paid')
          ->whereColumn('paid_date', '>', 'paymentdate')
          ->where('paid_date', '>=', now()->subDays(90)) // Last 90 days
          ->exists();
    }

    private function hasHighUtilization($customerId)
    {
        return $this->calculateCreditUtilizationRatio($customerId) > 75;
    }

    private function hasMultipleActiveLoans($customerId)
    {
        return $this->calculateActiveLoans($customerId) > 2;
    }

    // ==================== ADDITIONAL HELPER METHODS ====================

    /**
     * Count recent loan applications (credit inquiries)
     */
    private function countRecentLoans($customerId, $days = 90)
    {
        return DisbursedLoan::where('customerid', $customerId)
            ->where('disburseddate', '>=', now()->subDays($days))
            ->count();
    }

    /**
     * Get length of credit history in days
     */
    private function getCreditHistoryLength($customerId)
    {
        $firstLoan = DisbursedLoan::where('customerid', $customerId)
            ->orderBy('disburseddate', 'asc')
            ->first();
            
        if (!$firstLoan) {
            return 0;
        }
        
        return now()->diffInDays($firstLoan->disburseddate);
    }

    /**
     * Get customer's credit score trend (improving, declining, stable)
     */
    public function getScoreTrend($customerId, $periodDays = 30)
    {
        $scores = CustomerCreditScore::where('customer_id', $customerId)
            ->where('score_date', '>=', now()->subDays($periodDays))
            ->orderBy('score_date', 'asc')
            ->pluck('credit_score')
            ->toArray();

        if (count($scores) < 2) {
            return 'stable'; // Not enough data
        }

        $firstScore = $scores[0];
        $lastScore = end($scores);
        $change = $lastScore - $firstScore;

        if ($change > 10) return 'improving';
        if ($change < -10) return 'declining';
        return 'stable';
    }

    /**
     * Get credit score factors breakdown for a customer
     */
    public function getScoreFactors($customerId)
    {
        $currentScore = CustomerCreditScore::current()->byCustomer($customerId)->first();
        
        if (!$currentScore) {
            return [];
        }

        return [
            'payment_history' => $this->calculatePaymentHistoryFactor($currentScore),
            'credit_utilization' => $this->calculateUtilizationFactor($currentScore),
            'credit_history_length' => $this->calculateHistoryLengthFactor($customerId),
            'credit_mix' => $this->calculateCreditMixFactor($currentScore),
            'new_credit' => $this->calculateNewCreditFactor($customerId),
        ];
    }

    private function calculatePaymentHistoryFactor($score)
    {
        $factor = 'good';
        if ($score->on_time_repayment_rate < 80) $factor = 'poor';
        elseif ($score->on_time_repayment_rate < 95) $factor = 'fair';
        return $factor;
    }

    private function calculateUtilizationFactor($score)
    {
        $factor = 'good';
        if ($score->credit_utilization_ratio > 75) $factor = 'poor';
        elseif ($score->credit_utilization_ratio > 50) $factor = 'fair';
        return $factor;
    }

    private function calculateHistoryLengthFactor($customerId)
    {
        $length = $this->getCreditHistoryLength($customerId);
        $factor = 'good';
        if ($length < 180) $factor = 'poor';
        elseif ($length < 365) $factor = 'fair';
        return $factor;
    }

    private function calculateCreditMixFactor($score)
    {
        $factor = 'good';
        if ($score->total_loans <= 1) $factor = 'poor';
        elseif ($score->total_loans <= 2) $factor = 'fair';
        return $factor;
    }

    private function calculateNewCreditFactor($customerId)
    {
        $recentLoans = $this->countRecentLoans($customerId, 90);
        $factor = 'good';
        if ($recentLoans > 3) $factor = 'poor';
        elseif ($recentLoans > 1) $factor = 'fair';
        return $factor;
    }
}