<?php

namespace App\Http\Controllers;

use App\Models\OperationalDocument;
use App\Services\SalesOrderPaymentService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class PaymentController extends Controller
{
    public function index(Request $request)
    {
        $businessId = $request->user()->business_id;
        $payments = DB::table('payments')
            ->where('payments.business_id', $businessId)
            ->select('payments.*')
            ->selectSub(function ($query) {
                $query->from('payment_allocations')
                    ->selectRaw('COALESCE(SUM(amount), 0)')
                    ->whereColumn('payment_allocations.payment_id', 'payments.id');
            }, 'allocated_amount')
            ->latest('payments.id')
            ->get();

        $invoices = OperationalDocument::where('business_id', $businessId)
            ->where('type', 'invoices')
            ->whereIn('status', ['sent', 'partially_paid'])
            ->get();

        return view('payments.index', compact('payments', 'invoices'));
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'account_id' => 'nullable|integer',
            'amount' => 'required|numeric|min:0.01',
            'method' => 'required|string|max:50',
            'reference' => 'required|string|max:255',
            'received_at' => 'required|date',
            'payer' => 'required|string|max:255',
            'attachment' => 'nullable|file|max:10240',
            'allocations' => 'nullable|array',
            'allocations.*.invoice_id' => 'required|integer',
            'allocations.*.amount' => 'nullable|numeric|min:0.01',
        ]);

        $allocations = collect($data['allocations'] ?? [])
            ->filter(fn (array $allocation) => filled($allocation['amount'] ?? null))
            ->values();

        if ($allocations->sum(fn (array $allocation) => (float) $allocation['amount']) > (float) $data['amount']) {
            throw ValidationException::withMessages([
                'allocations' => 'Total allocations cannot exceed the payment amount.',
            ]);
        }

        $businessId = $request->user()->business_id;
        $attachmentPath = $request->file('attachment')?->store("payment-attachments/{$businessId}", 'public');

        DB::transaction(function () use ($data, $allocations, $businessId, $attachmentPath) {
            $paymentId = DB::table('payments')->insertGetId([
                'business_id' => $businessId,
                'account_id' => $data['account_id'] ?? null,
                'amount' => $data['amount'],
                'method' => $data['method'],
                'reference' => $data['reference'],
                'received_at' => $data['received_at'],
                'payer' => $data['payer'],
                'attachment_path' => $attachmentPath,
                'status' => 'received',
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            foreach ($allocations as $allocation) {
                $invoice = OperationalDocument::whereKey($allocation['invoice_id'])
                    ->where('business_id', $businessId)
                    ->where('type', 'invoices')
                    ->whereIn('status', ['sent', 'partially_paid'])
                    ->lockForUpdate()
                    ->first();

                if (! $invoice) {
                    throw ValidationException::withMessages([
                        'allocations' => 'Each allocation must use an eligible invoice in the current business.',
                    ]);
                }

                $invoiceTotal = (float) ($invoice->details['total_amount'] ?? 0);
                $alreadyAllocated = (float) DB::table('payment_allocations')
                    ->join('payments', 'payments.id', '=', 'payment_allocations.payment_id')
                    ->where('payment_allocations.invoice_id', $invoice->id)
                    ->where('payments.business_id', $businessId)
                    ->whereIn('payments.status', ['received', 'verified'])
                    ->sum('payment_allocations.amount');
                $availableBalance = max(0, $invoiceTotal - $alreadyAllocated);

                if ((float) $allocation['amount'] > $availableBalance) {
                    throw ValidationException::withMessages([
                        'allocations' => "Allocation for invoice {$invoice->reference_number} cannot exceed its available balance.",
                    ]);
                }

                DB::table('payment_allocations')->insert([
                    'payment_id' => $paymentId,
                    'invoice_id' => $invoice->id,
                    'amount' => $allocation['amount'],
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        });

        return back()->with('success', 'Payment recorded.');
    }

    public function verify(Request $request, int $payment)
    {
        $record = DB::table('payments')
            ->where('id', $payment)
            ->where('business_id', $request->user()->business_id)
            ->first();

        abort_unless($record, 404);

        DB::table('payments')->where('id', $payment)->update([
            'status' => 'verified',
            'verified_by' => $request->user()->id,
            'verified_at' => now(),
            'updated_at' => now(),
        ]);

        foreach (DB::table('payment_allocations')->where('payment_id', $payment)->pluck('invoice_id') as $invoiceId) {
            $invoice = OperationalDocument::whereKey($invoiceId)
                ->where('business_id', $record->business_id)
                ->firstOrFail();
            $paid = DB::table('payment_allocations')
                ->join('payments', 'payments.id', '=', 'payment_allocations.payment_id')
                ->where('payment_allocations.invoice_id', $invoiceId)
                ->where('payments.status', 'verified')
                ->sum('payment_allocations.amount');
            $total = (float) ($invoice->details['total_amount'] ?? 0);
            $balance = max(0, $total - $paid);

            $invoice->update([
                'status' => $balance <= 0 ? 'paid' : ($paid > 0 ? 'partially_paid' : 'sent'),
                'details' => array_merge($invoice->details ?? [], [
                    'balance_due' => $balance,
                    'payment_status' => $balance <= 0 ? 'completed' : ($paid > 0 ? 'partially_paid' : 'pending'),
                ]),
            ]);
            app(SalesOrderPaymentService::class)->recalculateFromInvoice($invoice->fresh(), $request->user()->id);
        }

        DB::table('receipts')->updateOrInsert(
            ['payment_id' => $payment],
            [
                'business_id' => $record->business_id,
                'receipt_number' => "RCT-{$payment}",
                'issued_at' => now()->toDateString(),
                'updated_at' => now(),
                'created_at' => now(),
            ]
        );

        return back()->with('success', 'Payment verified and receipt created.');
    }
}
