<?php

namespace Modules\Bms\Services\Adapters\Erp;

use App\Transaction;
use App\TransactionPayment;
use App\Utils\TransactionUtil;
use Modules\Bms\Entities\BmsBooking;
use Modules\Bms\Services\Contracts\PaymentServiceContract;

/**
 * ERP adapter for payment recording. Records a payment against a booking's
 * ERP invoice via the ERP's TransactionUtil (cash/transfer/installments —
 * recording only, no card processing), then refreshes payment status.
 */
class ErpPaymentService implements PaymentServiceContract
{
    public function recordPayment(int $businessId, int $bookingId, array $payment): ?int
    {
        $booking = BmsBooking::where('business_id', $businessId)->findOrFail($bookingId);
        if (! $booking->erp_invoice_id) {
            throw new \RuntimeException('Booking has no ERP invoice yet — confirm it first.');
        }

        $transaction = Transaction::findOrFail($booking->erp_invoice_id);
        $tu = app(TransactionUtil::class);
        $userId = $payment['user_id'] ?? auth()->id() ?? \App\User::where('business_id', $businessId)->min('id');

        // Add a SINGLE payment (mirrors TransactionPaymentController::store).
        // NOT createOrUpdatePaymentLines() — that syncs/deletes the full set.
        $inputs = [
            'transaction_id' => $transaction->id,
            'amount'         => (float) $payment['amount'],
            'method'         => $payment['method'] ?? 'cash',
            'paid_on'        => $payment['paid_on'] ?? now()->toDateTimeString(),
            'created_by'     => $userId,
            'payment_for'    => $transaction->contact_id,
            'business_id'    => $businessId,
            'note'           => $payment['note'] ?? ('Banquet booking ' . $booking->booking_no),
        ];
        if (! empty($payment['account_id'])) {
            $inputs['account_id'] = $payment['account_id'];
        }
        $refCount = $tu->setAndGetReferenceCount('sell_payment', $businessId);
        $inputs['payment_ref_no'] = $tu->generateReferenceNumber('sell_payment', $refCount, $businessId);

        $tp = TransactionPayment::create($inputs);

        // Best-effort account-ledger integration (fires only if present).
        try {
            if (class_exists(\App\Events\TransactionPaymentAdded::class)) {
                $inputs['transaction_type'] = $transaction->type;
                event(new \App\Events\TransactionPaymentAdded($tp, $inputs));
            }
        } catch (\Throwable $e) {
            \Log::warning('[BMS] payment account-ledger event failed: ' . $e->getMessage());
        }

        $tu->updatePaymentStatus($transaction->id, $transaction->final_total);

        // Reflect collected total on the booking + allocate across the plan.
        $paid = (float) TransactionPayment::where('transaction_id', $transaction->id)->where('is_return', 0)->sum('amount');
        $booking->forceFill(['deposit_amount' => $paid])->save();
        app(\Modules\Bms\Services\Finance\PaymentPlanService::class)->syncPaid($booking, $paid);

        \Modules\Bms\Entities\BmsBookingEvent::record($booking, 'payment', [
            'amount' => (float) $payment['amount'],
            'method' => $payment['method'] ?? 'cash',
            'paid'   => $paid,
        ], $userId);

        return (int) TransactionPayment::where('transaction_id', $transaction->id)->latest('id')->value('id');
    }

    /**
     * Record a REFUND (is_return) against the booking's ERP invoice — used by
     * the cancellation flow to return the over-collected balance. "paid" sums
     * elsewhere filter is_return=0, so refunds net out correctly and the
     * invoice reconciles. Returns the refund payment id.
     */
    public function refund(int $businessId, int $bookingId, float $amount, ?string $note = null): ?int
    {
        if ($amount <= 0) {
            return null;
        }

        $booking = BmsBooking::where('business_id', $businessId)->findOrFail($bookingId);
        if (! $booking->erp_invoice_id) {
            throw new \RuntimeException('Booking has no ERP invoice — nothing to refund.');
        }

        $transaction = Transaction::findOrFail($booking->erp_invoice_id);
        $tu = app(TransactionUtil::class);
        $userId = auth()->id() ?? \App\User::where('business_id', $businessId)->min('id');

        $inputs = [
            'transaction_id' => $transaction->id,
            'amount'         => round($amount, 2),
            'method'         => 'cash',
            'paid_on'        => now()->toDateTimeString(),
            'created_by'     => $userId,
            'payment_for'    => $transaction->contact_id,
            'business_id'    => $businessId,
            'is_return'      => 1,
            'note'           => $note ?? ('Refund — banquet booking ' . $booking->booking_no),
        ];
        $refCount = $tu->setAndGetReferenceCount('sell_payment', $businessId);
        $inputs['payment_ref_no'] = $tu->generateReferenceNumber('sell_payment', $refCount, $businessId);

        $tp = TransactionPayment::create($inputs);

        $tu->updatePaymentStatus($transaction->id, $transaction->final_total);

        return (int) $tp->id;
    }

    public function scheduleForBooking(int $businessId, int $bookingId): array
    {
        $booking = BmsBooking::where('business_id', $businessId)->findOrFail($bookingId);
        if (! $booking->erp_invoice_id) {
            return ['total' => (float) $booking->total_amount, 'paid' => 0.0, 'due' => (float) $booking->total_amount, 'payments' => []];
        }

        $transaction = Transaction::find($booking->erp_invoice_id);
        $payments = TransactionPayment::where('transaction_id', $booking->erp_invoice_id)->where('is_return', 0)
            ->get(['id', 'amount', 'method', 'paid_on', 'payment_ref_no']);
        $paid = (float) $payments->sum('amount');
        $total = (float) optional($transaction)->final_total;

        return [
            'total'    => $total,
            'paid'     => $paid,
            'due'      => round($total - $paid, 2),
            'payments' => $payments->map(fn ($p) => [
                'id' => $p->id, 'amount' => (float) $p->amount, 'method' => $p->method,
                'paid_on' => $p->paid_on, 'ref' => $p->payment_ref_no,
            ])->all(),
        ];
    }
}
