<?php

namespace App\Http\Controllers\Modules\Sales;

use App\Http\Controllers\Controller;
use App\Http\Requests\Sales\StoreCreditNoteRequest;
use App\Http\Requests\Sales\StoreDebitNoteRequest;
use App\Http\Requests\Sales\StorePaymentRequest;
use App\Http\Traits\HasRoleViews;
use App\Models\CustomerVendor;
use App\Models\TaxInvoice;
use App\Models\SalesCreditNote;
use App\Models\SalesDebitNote;
use App\Models\SalesPayment;
use App\Services\ExportService;
use Carbon\Carbon;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

/**
 * Unified Sales Management Controller
 *
 * Handles customer payments, credit notes, debit notes, aging reports.
 * Reference: SuperAdminSalesManagementController
 */
class SalesManagementController extends Controller
{
    use HasRoleViews;

    protected ExportService $exportService;

    public function __construct(ExportService $exportService)
    {
        $this->exportService = $exportService;
    }

    /**
     * Display the sales management page
     */
    public function index()
    {
        $customers = CustomerVendor::whereIn('company_role', ['Customer', 'Both', 'customer', 'both'])
            ->orderBy('company')
            ->get();

        return $this->roleView('salesmanagement.index', compact('customers'));
    }

    // ==================== CUSTOMER PAYMENTS ====================

    /**
     * Get paginated payments data for DataTable
     */
    public function getPayments(Request $request): JsonResponse
    {
        try {
            $query = TaxInvoice::with('company');

            if ($request->filled('party_id')) {
                $query->where('company_id', $request->party_id);
            }

            if ($request->filled('date_range')) {
                $dates = explode(' - ', $request->date_range);
                if (count($dates) == 2) {
                    $query->whereBetween('invoice_date', [$dates[0], $dates[1]]);
                }
            }

            if ($request->filled('min_amount')) {
                $query->where('total_amount', '>=', $request->min_amount);
            }

            if ($request->filled('max_amount')) {
                $query->where('total_amount', '<=', $request->max_amount);
            }

            $invoices = $query->orderBy('invoice_date', 'desc')->get();

            // Preload payment, credit, debit data to avoid N+1
            $invoiceIds = $invoices->pluck('id')->toArray();
            $invoiceNos = $invoices->pluck('invoice_no')->filter()->toArray();

            $paidAmounts = SalesPayment::whereIn('invoice_id', $invoiceIds)
                ->selectRaw('invoice_id, SUM(payment_amount) as total_paid')
                ->groupBy('invoice_id')
                ->pluck('total_paid', 'invoice_id');

            $creditAmounts = SalesCreditNote::whereIn('invoice_no', $invoiceNos)
                ->selectRaw('invoice_no, SUM(amount) as total_credit')
                ->groupBy('invoice_no')
                ->pluck('total_credit', 'invoice_no');

            $debitAmounts = SalesDebitNote::whereIn('invoice_no', $invoiceNos)
                ->selectRaw('invoice_no, SUM(amount) as total_debit')
                ->groupBy('invoice_no')
                ->pluck('total_debit', 'invoice_no');

            $lastPayments = SalesPayment::whereIn('invoice_id', $invoiceIds)
                ->orderBy('payment_date', 'desc')
                ->get()
                ->unique('invoice_id')
                ->keyBy('invoice_id');

            $data = $invoices->map(function ($invoice) use ($paidAmounts, $creditAmounts, $debitAmounts, $lastPayments) {
                $paidAmount = floatval($paidAmounts[$invoice->id] ?? 0);
                $creditAmount = floatval($creditAmounts[$invoice->invoice_no] ?? 0);
                $debitAmount = floatval($debitAmounts[$invoice->invoice_no] ?? 0);
                $totalAmount = floatval($invoice->total_amount ?? 0);
                // Correct formula: Invoice + Debit Notes - Payments - Credit Notes
                $balance = $totalAmount + $debitAmount - $paidAmount - $creditAmount;

                $status = 'Unpaid';
                $statusClass = 'danger';
                if ($paidAmount >= $totalAmount && $totalAmount > 0) {
                    $status = 'Paid';
                    $statusClass = 'success';
                } elseif ($paidAmount > 0) {
                    $status = 'Partial';
                    $statusClass = 'warning';
                }

                $lastPayment = $lastPayments[$invoice->id] ?? null;

                return [
                    'id' => $invoice->id,
                    'invoice_no' => $invoice->invoice_no ?? '-',
                    'invoice_date' => $invoice->invoice_date ? Carbon::parse($invoice->invoice_date)->format('d-m-Y') : '-',
                    'customer' => $invoice->company->company ?? 'N/A',
                    'total_amount' => $totalAmount,
                    'paid_amount' => $paidAmount,
                    'balance' => $balance,
                    'status' => $status,
                    'status_class' => $statusClass,
                    'payment_method' => $lastPayment ? ucfirst(str_replace('_', ' ', $lastPayment->payment_method)) : '-',
                    'reference_no' => $lastPayment ? ($lastPayment->reference_no ?? '-') : '-',
                ];
            });

            return response()->json(['data' => $data]);
        } catch (Exception $e) {
            return response()->json(['data' => [], 'error' => $e->getMessage()]);
        }
    }

    /**
     * Get unpaid invoices for payment dropdown
     */
    public function getUnpaidInvoices(): JsonResponse
    {
        try {
            $invoices = TaxInvoice::with('company')
                ->orderBy('invoice_date', 'desc')
                ->get();

            // Preload aggregates to avoid N+1
            $invoiceIds = $invoices->pluck('id')->toArray();
            $invoiceNos = $invoices->pluck('invoice_no')->filter()->toArray();

            $paidAmounts = SalesPayment::whereIn('invoice_id', $invoiceIds)
                ->selectRaw('invoice_id, SUM(payment_amount) as total_paid')
                ->groupBy('invoice_id')
                ->pluck('total_paid', 'invoice_id');

            $creditAmounts = SalesCreditNote::whereIn('invoice_no', $invoiceNos)
                ->selectRaw('invoice_no, SUM(amount) as total_credit')
                ->groupBy('invoice_no')
                ->pluck('total_credit', 'invoice_no');

            $debitAmounts = SalesDebitNote::whereIn('invoice_no', $invoiceNos)
                ->selectRaw('invoice_no, SUM(amount) as total_debit')
                ->groupBy('invoice_no')
                ->pluck('total_debit', 'invoice_no');

            $data = [];
            foreach ($invoices as $invoice) {
                $paidAmount = floatval($paidAmounts[$invoice->id] ?? 0);
                $creditAmount = floatval($creditAmounts[$invoice->invoice_no] ?? 0);
                $debitAmount = floatval($debitAmounts[$invoice->invoice_no] ?? 0);
                $totalAmount = floatval($invoice->total_amount ?? 0);
                // Correct formula: Invoice + Debit Notes - Payments - Credit Notes
                $balance = $totalAmount + $debitAmount - $paidAmount - $creditAmount;

                if ($balance > 0) {
                    $data[] = [
                        'id' => $invoice->id,
                        'invoice_no' => $invoice->invoice_no,
                        'customer' => $invoice->company->company ?? 'N/A',
                        'balance' => $balance,
                        'display' => $invoice->invoice_no . ' - ' . ($invoice->company->company ?? 'N/A') . ' (Balance: ₹' . number_format($balance, 2) . ')',
                    ];
                }
            }

            return response()->json(['success' => true, 'data' => $data]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'data' => [], 'error' => $e->getMessage()]);
        }
    }

    /**
     * Store a new payment
     * Uses StorePaymentRequest for validation including balance check
     */
    public function storePayment(StorePaymentRequest $request): JsonResponse
    {
        try {
            $invoice = TaxInvoice::findOrFail($request->invoice_id);

            // Generate payment number
            $lastPayment = SalesPayment::orderBy('id', 'desc')->first();
            $nextNum = $lastPayment ? (intval(substr($lastPayment->payment_no, -4)) + 1) : 1;
            $paymentNo = 'PAY/' . date('Y') . '/' . str_pad($nextNum, 4, '0', STR_PAD_LEFT);

            $payment = SalesPayment::create([
                'payment_no' => $paymentNo,
                'invoice_id' => $request->invoice_id,
                'invoice_no' => $invoice->invoice_no,
                'party_id' => $invoice->company_id,
                'payment_date' => $request->payment_date,
                'payment_amount' => $request->payment_amount,
                'payment_method' => $request->payment_method,
                'reference_no' => $request->reference_no,
                'notes' => $request->notes,
            ]);

            return response()->json([
                'success' => true,
                'message' => 'Payment recorded successfully',
                'data' => $payment,
            ]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    /**
     * Get a single payment
     */
    public function getPayment(int $id): JsonResponse
    {
        $payment = SalesPayment::with(['invoice', 'party'])->find($id);

        if (!$payment) {
            return response()->json(['success' => false, 'message' => 'Payment not found'], 404);
        }

        return response()->json(['success' => true, 'data' => $payment]);
    }

    /**
     * Update a payment
     */
    public function updatePayment(Request $request, int $id): JsonResponse
    {
        $payment = SalesPayment::find($id);

        if (!$payment) {
            return response()->json(['success' => false, 'message' => 'Payment not found'], 404);
        }

        $request->validate([
            'payment_date' => 'required|date',
            'payment_amount' => 'required|numeric|min:0.01',
            'payment_method' => 'required|string',
        ]);

        $payment->update([
            'payment_date' => $request->payment_date,
            'payment_amount' => $request->payment_amount,
            'payment_method' => $request->payment_method,
            'reference_no' => $request->reference_no,
            'notes' => $request->notes,
        ]);

        return response()->json(['success' => true, 'message' => 'Payment updated successfully']);
    }

    /**
     * Delete a payment
     */
    public function deletePayment(int $id): JsonResponse
    {
        // Authorization check - only SuperAdmin and Manager can delete payments
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized. Only SuperAdmin and Manager can delete payments.',
            ], 403);
        }

        $payment = SalesPayment::find($id);

        if (!$payment) {
            return response()->json(['success' => false, 'message' => 'Payment not found'], 404);
        }

        $payment->delete();

        return response()->json(['success' => true, 'message' => 'Payment deleted successfully']);
    }

    // ==================== CREDIT NOTES ====================

    /**
     * Get credit notes for DataTable
     */
    public function getCreditNotes(Request $request): JsonResponse
    {
        try {
            $query = SalesCreditNote::with('party');

            if ($request->filled('party_id')) {
                $query->where('party_id', $request->party_id);
            }

            if ($request->filled('date_range')) {
                $dates = explode(' - ', $request->date_range);
                if (count($dates) == 2) {
                    $query->whereBetween('date', [$dates[0], $dates[1]]);
                }
            }

            if ($request->filled('credit_note_no')) {
                $query->where('credit_note_no', 'like', '%' . $request->credit_note_no . '%');
            }

            $creditNotes = $query->orderBy('date', 'desc')->get();

            $data = $creditNotes->map(function ($cn) {
                return [
                    'id' => $cn->id,
                    'credit_note_no' => $cn->credit_note_no,
                    'date' => $cn->date ? Carbon::parse($cn->date)->format('Y-m-d') : null,
                    'party' => $cn->party->company ?? 'N/A',
                    'invoice_no' => $cn->invoice_no ?? '-',
                    'amount' => $cn->amount,
                    'reason' => $cn->reason ?? '-',
                ];
            });

            return response()->json(['data' => $data]);
        } catch (Exception $e) {
            return response()->json(['data' => [], 'error' => $e->getMessage()]);
        }
    }

    /**
     * Get next credit note number
     */
    public function getNextCreditNoteNumber(): JsonResponse
    {
        $lastCreditNote = SalesCreditNote::orderBy('id', 'desc')->first();
        $nextNum = $lastCreditNote ? (intval(substr($lastCreditNote->credit_note_no, -4)) + 1) : 1;
        $creditNoteNo = 'SCN/' . date('Y') . '/' . str_pad($nextNum, 4, '0', STR_PAD_LEFT);

        return response()->json(['success' => true, 'credit_note_no' => $creditNoteNo]);
    }

    /**
     * Store a new credit note
     * Uses StoreCreditNoteRequest for validation
     */
    public function storeCreditNote(StoreCreditNoteRequest $request): JsonResponse
    {
        try {
            $creditNote = SalesCreditNote::create([
                'credit_note_no' => $request->credit_note_no,
                'date' => $request->date,
                'party_id' => $request->party_id,
                'invoice_no' => $request->invoice_no,
                'amount' => $request->amount,
                'reason' => $request->reason,
                'description' => $request->description,
            ]);

            return response()->json([
                'success' => true,
                'message' => 'Credit note created successfully',
                'data' => $creditNote,
            ]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    /**
     * Get a single credit note
     */
    public function getCreditNote(int $id): JsonResponse
    {
        $creditNote = SalesCreditNote::with('party')->find($id);

        if (!$creditNote) {
            return response()->json(['success' => false, 'message' => 'Credit note not found'], 404);
        }

        $dateFormatted = null;
        if ($creditNote->date) {
            $dateFormatted = $creditNote->date instanceof Carbon
                ? $creditNote->date->format('Y-m-d')
                : Carbon::parse($creditNote->date)->format('Y-m-d');
        }

        return response()->json([
            'success' => true,
            'data' => [
                'id' => $creditNote->id,
                'credit_note_no' => $creditNote->credit_note_no,
                'date' => $dateFormatted,
                'party_id' => $creditNote->party_id,
                'invoice_no' => $creditNote->invoice_no,
                'amount' => $creditNote->amount,
                'reason' => $creditNote->reason,
                'description' => $creditNote->description,
            ],
        ]);
    }

    /**
     * Update a credit note
     */
    public function updateCreditNote(Request $request, int $id): JsonResponse
    {
        $creditNote = SalesCreditNote::find($id);

        if (!$creditNote) {
            return response()->json(['success' => false, 'message' => 'Credit note not found'], 404);
        }

        $request->validate([
            'date' => 'required|date',
            'party_id' => 'required|exists:customer_vendors,id',
            'amount' => 'required|numeric|min:0.01',
            'reason' => 'required|string',
        ]);

        $creditNote->update([
            'date' => $request->date,
            'party_id' => $request->party_id,
            'invoice_no' => $request->invoice_no,
            'amount' => $request->amount,
            'reason' => $request->reason,
            'description' => $request->description,
        ]);

        return response()->json(['success' => true, 'message' => 'Credit note updated successfully']);
    }

    /**
     * Delete a credit note
     */
    public function deleteCreditNote(int $id): JsonResponse
    {
        // Authorization check - only SuperAdmin and Manager can delete credit notes
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized. Only SuperAdmin and Manager can delete credit notes.',
            ], 403);
        }

        $creditNote = SalesCreditNote::find($id);

        if (!$creditNote) {
            return response()->json(['success' => false, 'message' => 'Credit note not found'], 404);
        }

        $creditNote->delete();

        return response()->json(['success' => true, 'message' => 'Credit note deleted successfully']);
    }

    // ==================== DEBIT NOTES ====================

    /**
     * Get Debit Notes list
     */
    public function getDebitNotes(Request $request): JsonResponse
    {
        try {
            $query = SalesDebitNote::with('party');

            if ($request->filled('party_id')) {
                $query->where('party_id', $request->party_id);
            }

            if ($request->filled('date_range')) {
                $dates = explode(' - ', $request->date_range);
                if (count($dates) == 2) {
                    $query->whereBetween('date', [$dates[0], $dates[1]]);
                }
            }

            if ($request->filled('debit_note_no')) {
                $query->where('debit_note_no', 'like', '%' . $request->debit_note_no . '%');
            }

            $debitNotes = $query->orderBy('date', 'desc')->get();

            $data = $debitNotes->map(function ($note) {
                return [
                    'id' => $note->id,
                    'debit_note_no' => $note->debit_note_no,
                    'date' => $note->date->format('Y-m-d'),
                    'party' => $note->party->company ?? 'N/A',
                    'party_id' => $note->party_id,
                    'invoice_no' => $note->invoice_no,
                    'amount' => $note->amount,
                    'reason' => $note->reason,
                    'description' => $note->description,
                ];
            });

            return response()->json(['success' => true, 'data' => $data]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()]);
        }
    }

    /**
     * Get next debit note number
     */
    public function getNextDebitNoteNumber(): JsonResponse
    {
        $lastNote = SalesDebitNote::orderBy('id', 'desc')->first();
        $nextNumber = $lastNote ? ((int) substr($lastNote->debit_note_no, -4) + 1) : 1;
        $debitNoteNo = 'UEPL/DN/' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT);

        return response()->json(['success' => true, 'debit_note_no' => $debitNoteNo]);
    }

    /**
     * Store a new debit note
     * Uses StoreDebitNoteRequest for validation
     */
    public function storeDebitNote(StoreDebitNoteRequest $request): JsonResponse
    {
        try {
            $debitNote = SalesDebitNote::create([
                'debit_note_no' => $request->debit_note_no,
                'date' => $request->date,
                'party_id' => $request->party_id,
                'invoice_no' => $request->invoice_no,
                'amount' => $request->amount,
                'reason' => $request->reason,
                'description' => $request->description,
            ]);

            return response()->json([
                'success' => true,
                'message' => 'Debit note created successfully',
                'data' => $debitNote,
            ]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    /**
     * Get single debit note
     */
    public function getDebitNote(int $id): JsonResponse
    {
        $debitNote = SalesDebitNote::with('party')->find($id);

        if (!$debitNote) {
            return response()->json(['success' => false, 'message' => 'Debit note not found'], 404);
        }

        $dateFormatted = null;
        if ($debitNote->date) {
            $dateFormatted = $debitNote->date instanceof Carbon
                ? $debitNote->date->format('Y-m-d')
                : Carbon::parse($debitNote->date)->format('Y-m-d');
        }

        return response()->json([
            'success' => true,
            'data' => [
                'id' => $debitNote->id,
                'debit_note_no' => $debitNote->debit_note_no,
                'date' => $dateFormatted,
                'party_id' => $debitNote->party_id,
                'invoice_no' => $debitNote->invoice_no,
                'amount' => $debitNote->amount,
                'reason' => $debitNote->reason,
                'description' => $debitNote->description,
            ],
        ]);
    }

    /**
     * Update a debit note
     */
    public function updateDebitNote(Request $request, int $id): JsonResponse
    {
        $debitNote = SalesDebitNote::find($id);

        if (!$debitNote) {
            return response()->json(['success' => false, 'message' => 'Debit note not found'], 404);
        }

        $request->validate([
            'date' => 'required|date',
            'party_id' => 'required|exists:customer_vendors,id',
            'amount' => 'required|numeric|min:0.01',
            'reason' => 'required|string',
        ]);

        $debitNote->update([
            'date' => $request->date,
            'party_id' => $request->party_id,
            'invoice_no' => $request->invoice_no,
            'amount' => $request->amount,
            'reason' => $request->reason,
            'description' => $request->description,
        ]);

        return response()->json(['success' => true, 'message' => 'Debit note updated successfully']);
    }

    /**
     * Delete a debit note
     */
    public function deleteDebitNote(int $id): JsonResponse
    {
        // Authorization check - only SuperAdmin and Manager can delete debit notes
        if (!$this->isSuperAdmin() && !$this->isManager()) {
            return response()->json([
                'success' => false,
                'message' => 'Unauthorized. Only SuperAdmin and Manager can delete debit notes.',
            ], 403);
        }

        $debitNote = SalesDebitNote::find($id);

        if (!$debitNote) {
            return response()->json(['success' => false, 'message' => 'Debit note not found'], 404);
        }

        $debitNote->delete();

        return response()->json(['success' => true, 'message' => 'Debit note deleted successfully']);
    }

    // ==================== AGING REPORT ====================

    /**
     * Get aging report data
     */
    public function getAgingReport(Request $request): JsonResponse
    {
        try {
            $asOfDate = $request->input('as_of_date', date('Y-m-d'));
            $partyId = $request->input('party_id');
            $reportType = $request->input('report_type', 'summary');

            $query = TaxInvoice::with('company')
                ->whereDate('invoice_date', '<=', $asOfDate);

            if ($partyId) {
                $query->where('company_id', $partyId);
            }

            $invoices = $query->get();

            // Preload aggregates to avoid N+1
            $invoiceIds = $invoices->pluck('id')->toArray();
            $invoiceNos = $invoices->pluck('invoice_no')->filter()->toArray();

            $paidAmounts = SalesPayment::whereIn('invoice_id', $invoiceIds)
                ->whereDate('payment_date', '<=', $asOfDate)
                ->selectRaw('invoice_id, SUM(payment_amount) as total_paid')
                ->groupBy('invoice_id')
                ->pluck('total_paid', 'invoice_id');

            $creditAmounts = SalesCreditNote::whereIn('invoice_no', $invoiceNos)
                ->whereDate('date', '<=', $asOfDate)
                ->selectRaw('invoice_no, SUM(amount) as total_credit')
                ->groupBy('invoice_no')
                ->pluck('total_credit', 'invoice_no');

            $debitAmounts = SalesDebitNote::whereIn('invoice_no', $invoiceNos)
                ->whereDate('date', '<=', $asOfDate)
                ->selectRaw('invoice_no, SUM(amount) as total_debit')
                ->groupBy('invoice_no')
                ->pluck('total_debit', 'invoice_no');

            $customerData = [];
            $totals = [
                'current' => 0,
                '1_15' => 0,
                '16_30' => 0,
                '31_45' => 0,
                'over_45' => 0,
                'total_due' => 0,
            ];

            foreach ($invoices as $invoice) {
                $paidAmount = floatval($paidAmounts[$invoice->id] ?? 0);
                $creditAmount = floatval($creditAmounts[$invoice->invoice_no] ?? 0);
                $debitAmount = floatval($debitAmounts[$invoice->invoice_no] ?? 0);

                // Correct formula: Invoice + Debit Notes - Payments - Credit Notes
                $balance = floatval($invoice->total_amount ?? 0) + $debitAmount - $paidAmount - $creditAmount;

                if ($balance <= 0) {
                    continue;
                }

                $customerId = $invoice->company_id;
                $customerName = $invoice->company->company ?? 'Unknown Customer';

                if (!isset($customerData[$customerId])) {
                    $customerData[$customerId] = [
                        'customer_id' => $customerId,
                        'customer_name' => $customerName,
                        'current' => 0,
                        '1_15' => 0,
                        '16_30' => 0,
                        '31_45' => 0,
                        'over_45' => 0,
                        'total_due' => 0,
                        'invoices' => [],
                    ];
                }

                $invoiceDate = Carbon::parse($invoice->invoice_date);
                $asOf = Carbon::parse($asOfDate);
                $daysOverdue = $invoiceDate->diffInDays($asOf);

                $bucket = 'current';
                if ($daysOverdue > 45) {
                    $bucket = 'over_45';
                } elseif ($daysOverdue > 30) {
                    $bucket = '31_45';
                } elseif ($daysOverdue > 15) {
                    $bucket = '16_30';
                } elseif ($daysOverdue > 0) {
                    $bucket = '1_15';
                }

                $customerData[$customerId][$bucket] += $balance;
                $customerData[$customerId]['total_due'] += $balance;
                $totals[$bucket] += $balance;
                $totals['total_due'] += $balance;

                if ($reportType === 'detailed') {
                    $customerData[$customerId]['invoices'][] = [
                        'invoice_no' => $invoice->invoice_no,
                        'invoice_date' => $invoice->invoice_date,
                        'days_overdue' => $daysOverdue,
                        'grand_total' => $invoice->total_amount,
                        'paid_amount' => $paidAmount,
                        'balance_amount' => $balance,
                        'bucket' => $bucket,
                    ];
                }
            }

            $thirtyDaysAgo = Carbon::parse($asOfDate)->subDays(30)->format('Y-m-d');
            $receivedLast30Days = SalesPayment::whereBetween('payment_date', [$thirtyDaysAgo, $asOfDate])->sum('payment_amount');

            return response()->json([
                'success' => true,
                'data' => array_values($customerData),
                'totals' => $totals,
                'received_last_30_days' => floatval($receivedLast30Days),
                'as_of_date' => $asOfDate,
            ]);
        } catch (Exception $e) {
            return response()->json([
                'success' => false,
                'data' => [],
                'totals' => ['current' => 0, '1_15' => 0, '16_30' => 0, '31_45' => 0, 'over_45' => 0, 'total_due' => 0],
                'received_last_30_days' => 0,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Export aging report as styled Excel
     */
    public function exportAgingReport(Request $request)
    {
        $asOfDate = $request->input('as_of_date', date('Y-m-d'));

        $agingResponse = $this->getAgingReport($request);
        $agingData = json_decode($agingResponse->getContent(), true);

        $spreadsheet = $this->exportService->createStyledSpreadsheet('Sales Aging Report - As of ' . $asOfDate);
        $sheet = $spreadsheet->getActiveSheet();
        $headerRow = 4;

        $headers = ['Customer', 'Current', '1-15 Days', '16-30 Days', '31-45 Days', '>45 Days', 'Total Due'];
        $colCount = count($headers);
        $sheet->fromArray($headers, null, 'A' . $headerRow);
        $this->exportService->styleHeaderRow($sheet, $headerRow, $colCount);

        $rowNum = $headerRow + 1;
        foreach ($agingData['data'] ?? [] as $row) {
            $sheet->fromArray([
                $row['customer_name'] ?? 'Unknown',
                $row['current'] ?? 0,
                $row['1_15'] ?? 0,
                $row['16_30'] ?? 0,
                $row['31_45'] ?? 0,
                $row['over_45'] ?? 0,
                $row['total_due'] ?? 0,
            ], null, 'A' . $rowNum);
            $rowNum++;
        }

        $this->exportService->styleDataRows($sheet, $headerRow + 1, $rowNum - 1, $colCount);

        $sheet->fromArray([
            'TOTAL',
            $agingData['totals']['current'] ?? 0,
            $agingData['totals']['1_15'] ?? 0,
            $agingData['totals']['16_30'] ?? 0,
            $agingData['totals']['31_45'] ?? 0,
            $agingData['totals']['over_45'] ?? 0,
            $agingData['totals']['total_due'] ?? 0,
        ], null, 'A' . $rowNum);
        $this->exportService->styleTotalsRow($sheet, $rowNum, $colCount);

        $this->exportService->autoSizeColumns($sheet, $colCount);

        $filename = 'Sales-Aging-Report-' . date('Ymd_His') . '.xlsx';
        $this->exportService->download($spreadsheet, $filename);
    }

    // ==================== STATEMENT OF ACCOUNTS ====================

    /**
     * Get statement of accounts
     */
    public function getStatementOfAccounts(Request $request): JsonResponse
    {
        try {
            $partyId = $request->party_id;
            $fromDate = $request->from_date;
            $toDate = $request->to_date;

            if (!$partyId) {
                return response()->json(['success' => false, 'message' => 'Please select a customer']);
            }

            $invoices = TaxInvoice::where('company_id', $partyId)
                ->whereBetween('invoice_date', [$fromDate, $toDate])
                ->get()
                ->map(function ($inv) {
                    return [
                        'date' => $inv->invoice_date,
                        'type' => 'Invoice',
                        'ref_no' => $inv->invoice_no,
                        'debit' => floatval($inv->total_amount),
                        'credit' => 0,
                        'description' => 'Invoice generation',
                    ];
                });

            $payments = SalesPayment::where('party_id', $partyId)
                ->whereBetween('payment_date', [$fromDate, $toDate])
                ->get()
                ->map(function ($pay) {
                    return [
                        'date' => $pay->payment_date->format('Y-m-d'),
                        'type' => 'Payment',
                        'ref_no' => $pay->payment_no,
                        'debit' => 0,
                        'credit' => floatval($pay->payment_amount),
                        'description' => $pay->payment_method . ' - ' . $pay->reference_no,
                    ];
                });

            $creditNotes = SalesCreditNote::where('party_id', $partyId)
                ->whereBetween('date', [$fromDate, $toDate])
                ->get()
                ->map(function ($cn) {
                    return [
                        'date' => $cn->date->format('Y-m-d'),
                        'type' => 'Credit Note',
                        'ref_no' => $cn->credit_note_no,
                        'debit' => 0,
                        'credit' => floatval($cn->amount),
                        'description' => $cn->reason,
                    ];
                });

            $debitNotes = SalesDebitNote::where('party_id', $partyId)
                ->whereBetween('date', [$fromDate, $toDate])
                ->get()
                ->map(function ($dn) {
                    return [
                        'date' => $dn->date->format('Y-m-d'),
                        'type' => 'Debit Note',
                        'ref_no' => $dn->debit_note_no,
                        'debit' => floatval($dn->amount),
                        'credit' => 0,
                        'description' => $dn->reason,
                    ];
                });

            $transactions = $invoices->concat($payments)->concat($creditNotes)->concat($debitNotes)
                ->sortBy('date')->values();

            $openingDebit = TaxInvoice::where('company_id', $partyId)->where('invoice_date', '<', $fromDate)->sum('total_amount')
                + SalesDebitNote::where('party_id', $partyId)->where('date', '<', $fromDate)->sum('amount');

            $openingCredit = SalesPayment::where('party_id', $partyId)->where('payment_date', '<', $fromDate)->sum('payment_amount')
                + SalesCreditNote::where('party_id', $partyId)->where('date', '<', $fromDate)->sum('amount');

            $openingBalance = $openingDebit - $openingCredit;

            $runningBalance = $openingBalance;
            $data = $transactions->map(function ($txn) use (&$runningBalance) {
                $runningBalance += $txn['debit'] - $txn['credit'];
                $txn['balance'] = $runningBalance;

                return $txn;
            });

            return response()->json([
                'success' => true,
                'opening_balance' => $openingBalance,
                'data' => $data,
            ]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()]);
        }
    }

    // ==================== RECEIPTS ====================

    /**
     * Get receipts list
     */
    public function getReceipts(Request $request): JsonResponse
    {
        try {
            $query = SalesPayment::with(['party', 'invoice']);

            if ($request->filled('party_id')) {
                $query->where('party_id', $request->party_id);
            }

            if ($request->filled('date_range')) {
                $dates = explode(' - ', $request->date_range);
                if (count($dates) == 2) {
                    $query->whereBetween('payment_date', [$dates[0], $dates[1]]);
                }
            }

            if ($request->filled('payment_no')) {
                $query->where('payment_no', 'like', '%' . $request->payment_no . '%');
            }

            $payments = $query->orderBy('payment_date', 'desc')->get()->map(function ($pay) {
                return [
                    'id' => $pay->id,
                    'payment_no' => $pay->payment_no,
                    'date' => $pay->payment_date->format('Y-m-d'),
                    'party' => $pay->party->company ?? 'N/A',
                    'invoice_no' => $pay->invoice_no ?? '-',
                    'amount' => floatval($pay->payment_amount),
                    'method' => ucfirst($pay->payment_method),
                    'reference' => $pay->reference_no ?? '-',
                ];
            });

            return response()->json(['success' => true, 'data' => $payments]);
        } catch (Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()]);
        }
    }

    /**
     * Download receipt PDF
     */
    public function downloadReceipt(int $id)
    {
        $payment = SalesPayment::with('invoice', 'party')->find($id);

        if (!$payment) {
            return back()->with('error', 'Payment not found');
        }

        return $this->roleView('salesmanagement.pdf.receipt', compact('payment'));
    }
}
