<?php

namespace App\Http\Controllers;

use App\Models\Payment;
use App\Models\Sale;
use App\Models\Customer;
use App\Models\Installment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Barryvdh\DomPDF\Facade\Pdf;

class PaymentController extends Controller
{
    /**
     * Display a listing of payments.
     */
    public function index(Request $request)
    {
        $query = Payment::with(['customer', 'sale', 'installment'])
            ->latest();
        
        // Apply filters
        if ($request->filled('type')) {
            if ($request->type == 'sale') {
                $query->whereNotNull('sale_id');
            } elseif ($request->type == 'installment') {
                $query->whereNotNull('installment_id');
            } elseif ($request->type == 'other') {
                $query->whereNull('sale_id')->whereNull('installment_id');
            }
        }
        
        if ($request->filled('payment_method')) {
            $query->where('payment_method', $request->payment_method);
        }
        
        if ($request->filled('customer_id')) {
            $query->where('customer_id', $request->customer_id);
        }
        
        if ($request->filled('start_date') && $request->filled('end_date')) {
            $query->whereBetween('payment_date', [$request->start_date, $request->end_date]);
        }
        
        if ($request->filled('search')) {
            $search = $request->search;
            $query->where(function($q) use ($search) {
                $q->where('reference_no', 'like', "%{$search}%")
                  ->orWhere('payment_code', 'like', "%{$search}%")
                  ->orWhereHas('customer', function($q) use ($search) {
                      $q->where('name', 'like', "%{$search}%")
                        ->orWhere('phone', 'like', "%{$search}%");
                  });
            });
        }
        
        $payments = $query->paginate(20);
        $customers = Customer::where('status', 'active')->orderBy('name')->get();
        
        // Calculate stats
        $stats = [
            'total_payments' => Payment::count(),
            'total_amount' => Payment::sum('amount'),
            'today_amount' => Payment::whereDate('payment_date', today())->sum('amount'),
            'this_month_amount' => Payment::whereMonth('payment_date', now()->month)
                ->whereYear('payment_date', now()->year)
                ->sum('amount'),
        ];
        
        return view('payments.index', compact('payments', 'customers', 'stats'));
    }
    
    /**
     * Show the form for creating a new payment.
     */
    public function create(Request $request)
    {
        $customers = Customer::where('status', 'active')->orderBy('name')->get();
        $sales = Sale::where('status', '!=', 'cancelled')
            ->where(function($q) {
                $q->where('payment_status', '!=', 'paid')
                  ->orWhere('due_amount', '>', 0);
            })
            ->orderBy('created_at', 'desc')
            ->get();
        
        $installments = Installment::where('status', 'pending')
            ->orderBy('due_date')
            ->get();
        
        // Pre-select if parameters are passed
        $selectedCustomer = $request->customer_id;
        $selectedSale = $request->sale_id;
        $selectedInstallment = $request->installment_id;
        
        return view('payments.create', compact(
            'customers', 
            'sales', 
            'installments',
            'selectedCustomer',
            'selectedSale',
            'selectedInstallment'
        ));
    }
    
    /**
     * Store a newly created payment in storage.
     */
    public function store(Request $request)
    {
        $request->validate([
            'customer_id' => 'required|exists:customers,id',
            'payment_date' => 'required|date',
            'amount' => 'required|numeric|min:0.01',
            'payment_method' => 'required|string',
        ]);
        
        DB::beginTransaction();
        
        try {
            // Generate payment code
            $paymentCode = $this->generatePaymentCode();
            
            // Create payment
            $payment = Payment::create([
                'payment_code' => $paymentCode,
                'customer_id' => $request->customer_id,
                'sale_id' => $request->sale_id,
                'installment_id' => $request->installment_id,
                'payment_date' => $request->payment_date,
                'amount' => $request->amount,
                'payment_method' => $request->payment_method,
                'reference_no' => $request->reference_no,
                'bank_name' => $request->bank_name,
                'bank_account' => $request->bank_account,
                'check_no' => $request->check_no,
                'transaction_id' => $request->transaction_id,
                'notes' => $request->notes,
                'status' => 'completed',
                'created_by' => auth()->id(),
            ]);
            
            // Update sale if payment is for sale
            if ($request->sale_id) {
                $sale = Sale::find($request->sale_id);
                if ($sale) {
                    $newPaidAmount = $sale->paid_amount + $request->amount;
                    $newDueAmount = max(0, $sale->grand_total - $newPaidAmount);
                    
                    $paymentStatus = $newDueAmount == 0 ? 'paid' : ($newPaidAmount > 0 ? 'partial' : 'due');
                    
                    $sale->update([
                        'paid_amount' => $newPaidAmount,
                        'due_amount' => $newDueAmount,
                        'payment_status' => $paymentStatus,
                    ]);
                }
            }
            
            // Update installment if payment is for installment
            if ($request->installment_id) {
                $installment = Installment::find($request->installment_id);
                if ($installment) {
                    $installment->update([
                        'status' => 'paid',
                        'paid_date' => $request->payment_date,
                        'payment_method' => $request->payment_method,
                        'reference_no' => $request->reference_no,
                    ]);
                }
            }
            
            // Update customer balance
            $customer = Customer::find($request->customer_id);
            if ($customer) {
                $customer->decrement('outstanding_balance', $request->amount);
            }
            
            DB::commit();
            
            return redirect()->route('payments.show', $payment)
                ->with('success', 'Payment recorded successfully!');
                
        } catch (\Exception $e) {
            DB::rollBack();
            
            return redirect()->back()
                ->with('error', 'Failed to record payment: ' . $e->getMessage())
                ->withInput();
        }
    }
    
    /**
     * Display the specified payment.
     */
    public function show(Payment $payment)
    {
        $payment->load(['customer', 'sale', 'installment', 'creator']);
        return view('payments.show', compact('payment'));
    }
    
    /**
     * Show the form for editing the specified payment.
     */
    public function edit(Payment $payment)
    {
        $customers = Customer::where('status', 'active')->orderBy('name')->get();
        $sales = Sale::where('customer_id', $payment->customer_id)
            ->where('status', '!=', 'cancelled')
            ->orderBy('created_at', 'desc')
            ->get();
        
        $installments = Installment::where('customer_id', $payment->customer_id)
            ->where('status', 'pending')
            ->orderBy('due_date')
            ->get();
        
        return view('payments.edit', compact('payment', 'customers', 'sales', 'installments'));
    }
    
    /**
     * Update the specified payment in storage.
     */
    public function update(Request $request, Payment $payment)
    {
        $request->validate([
            'customer_id' => 'required|exists:customers,id',
            'payment_date' => 'required|date',
            'amount' => 'required|numeric|min:0.01',
            'payment_method' => 'required|string',
        ]);
        
        DB::beginTransaction();
        
        try {
            // Reverse old payment effects
            $this->reversePaymentEffects($payment);
            
            // Update payment
            $payment->update([
                'customer_id' => $request->customer_id,
                'sale_id' => $request->sale_id,
                'installment_id' => $request->installment_id,
                'payment_date' => $request->payment_date,
                'amount' => $request->amount,
                'payment_method' => $request->payment_method,
                'reference_no' => $request->reference_no,
                'bank_name' => $request->bank_name,
                'bank_account' => $request->bank_account,
                'check_no' => $request->check_no,
                'transaction_id' => $request->transaction_id,
                'notes' => $request->notes,
                'updated_by' => auth()->id(),
            ]);
            
            // Apply new payment effects
            $this->applyPaymentEffects($payment, $request->amount);
            
            DB::commit();
            
            return redirect()->route('payments.show', $payment)
                ->with('success', 'Payment updated successfully!');
                
        } catch (\Exception $e) {
            DB::rollBack();
            
            return redirect()->back()
                ->with('error', 'Failed to update payment: ' . $e->getMessage())
                ->withInput();
        }
    }
    
    /**
     * Remove the specified payment from storage.
     */
    public function destroy(Payment $payment)
    {
        DB::beginTransaction();
        
        try {
            // Reverse payment effects
            $this->reversePaymentEffects($payment);
            
            // Delete payment
            $payment->delete();
            
            DB::commit();
            
            return redirect()->route('payments.index')
                ->with('success', 'Payment deleted successfully!');
                
        } catch (\Exception $e) {
            DB::rollBack();
            
            return redirect()->back()
                ->with('error', 'Failed to delete payment: ' . $e->getMessage());
        }
    }
    
    /**
     * Generate payment receipt.
     */
    public function receipt(Payment $payment)
    {
        $payment->load(['customer', 'sale', 'installment', 'creator']);
        
        $pdf = PDF::loadView('payments.receipt', compact('payment'));
        
        return $pdf->download('receipt-' . $payment->payment_code . '.pdf');
    }
    
    /**
     * View payment receipt.
     */
    public function viewReceipt(Payment $payment)
    {
        $payment->load(['customer', 'sale', 'installment', 'creator']);
        return view('payments.receipt-view', compact('payment'));
    }
    
    /**
     * Generate payment code.
     */
    private function generatePaymentCode()
    {
        $prefix = 'PAY-';
        $year = date('Y');
        $month = date('m');
        
        $lastPayment = Payment::where('payment_code', 'like', "{$prefix}{$year}-{$month}-%")
            ->orderBy('payment_code', 'desc')
            ->first();
        
        if ($lastPayment) {
            $lastNumber = intval(substr($lastPayment->payment_code, -3));
            $nextNumber = str_pad($lastNumber + 1, 3, '0', STR_PAD_LEFT);
        } else {
            $nextNumber = '001';
        }
        
        return "{$prefix}{$year}-{$month}-{$nextNumber}";
    }
    
    /**
     * Reverse payment effects (for update/delete).
     */
    private function reversePaymentEffects(Payment $payment)
    {
        // Reverse sale update
        if ($payment->sale_id) {
            $sale = Sale::find($payment->sale_id);
            if ($sale) {
                $newPaidAmount = max(0, $sale->paid_amount - $payment->amount);
                $newDueAmount = $sale->grand_total - $newPaidAmount;
                
                $paymentStatus = $newDueAmount == 0 ? 'paid' : ($newPaidAmount > 0 ? 'partial' : 'due');
                
                $sale->update([
                    'paid_amount' => $newPaidAmount,
                    'due_amount' => $newDueAmount,
                    'payment_status' => $paymentStatus,
                ]);
            }
        }
        
        // Reverse installment update
        if ($payment->installment_id) {
            $installment = Installment::find($payment->installment_id);
            if ($installment) {
                $installment->update([
                    'status' => 'pending',
                    'paid_date' => null,
                    'payment_method' => null,
                    'reference_no' => null,
                ]);
            }
        }
        
        // Reverse customer balance update
        if ($payment->customer_id) {
            $customer = Customer::find($payment->customer_id);
            if ($customer) {
                $customer->increment('outstanding_balance', $payment->amount);
            }
        }
    }
    
    /**
     * Apply payment effects.
     */
    private function applyPaymentEffects(Payment $payment, $amount)
    {
        // Apply sale update
        if ($payment->sale_id) {
            $sale = Sale::find($payment->sale_id);
            if ($sale) {
                $newPaidAmount = $sale->paid_amount + $amount;
                $newDueAmount = max(0, $sale->grand_total - $newPaidAmount);
                
                $paymentStatus = $newDueAmount == 0 ? 'paid' : ($newPaidAmount > 0 ? 'partial' : 'due');
                
                $sale->update([
                    'paid_amount' => $newPaidAmount,
                    'due_amount' => $newDueAmount,
                    'payment_status' => $paymentStatus,
                ]);
            }
        }
        
        // Apply installment update
        if ($payment->installment_id) {
            $installment = Installment::find($payment->installment_id);
            if ($installment) {
                $installment->update([
                    'status' => 'paid',
                    'paid_date' => $payment->payment_date,
                    'payment_method' => $payment->payment_method,
                    'reference_no' => $payment->reference_no,
                ]);
            }
        }
        
        // Apply customer balance update
        if ($payment->customer_id) {
            $customer = Customer::find($payment->customer_id);
            if ($customer) {
                $customer->decrement('outstanding_balance', $amount);
            }
        }
    }
}