<?php

namespace Modules\Bms\Services\Adapters\Erp;

use App\Transaction;
use App\TransactionPayment;
use App\Utils\TransactionUtil;
use Illuminate\Support\Facades\DB;
use Modules\Bms\Entities\BmsBooking;
use Modules\Bms\Services\Contracts\InvoiceServiceContract;

/**
 * ERP adapter for invoicing. A confirmed booking produces an ERP sell
 * Transaction (header-level "service" invoice — banquet sales are services,
 * so no product/stock lines), linked back via bms_bookings.erp_invoice_id.
 * Reuses the ERP's TransactionUtil for invoice numbering + payment status.
 */
class ErpInvoiceService implements InvoiceServiceContract
{
    public function createOrUpdateFromBooking(BmsBooking $booking): ?int
    {
        // Idempotent: if already invoiced, return the existing transaction.
        if ($booking->erp_invoice_id && Transaction::where('id', $booking->erp_invoice_id)->exists()) {
            return $booking->erp_invoice_id;
        }

        $businessId = (int) $booking->business_id;
        $total = (float) $booking->total_amount;
        $userId = $this->resolveUser($booking);
        $tu = app(TransactionUtil::class);

        $input = [
            'location_id'         => $booking->location_id,
            'status'              => 'final',
            'contact_id'          => $booking->contact_id,
            'transaction_date'    => now()->toDateTimeString(),
            'final_total'         => $total,
            'discount_amount'     => (float) $booking->discount_amount,
            'discount_type'       => 'fixed',
            'source'              => 'bms',
            'sale_note'           => 'Banquet booking ' . $booking->booking_no,
            'is_created_from_api' => 1,
        ];
        $invoiceTotal = [
            'total_before_tax' => $total - (float) $booking->discount_amount,
            'tax'              => 0,
        ];

        $transaction = DB::transaction(function () use ($tu, $businessId, $input, $invoiceTotal, $userId) {
            return $tu->createSellTransaction($businessId, $input, $invoiceTotal, $userId, false);
        });

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

        $booking->forceFill(['erp_invoice_id' => $transaction->id])->save();

        return $transaction->id;
    }

    public function statusForBooking(BmsBooking $booking): array
    {
        if (! $booking->erp_invoice_id) {
            return ['total' => 0.0, 'paid' => 0.0, 'due' => 0.0, 'status' => 'none', 'invoice_no' => null];
        }

        $t = Transaction::find($booking->erp_invoice_id);
        if (! $t) {
            return ['total' => 0.0, 'paid' => 0.0, 'due' => 0.0, 'status' => 'none', 'invoice_no' => null];
        }

        $paid = (float) TransactionPayment::where('transaction_id', $t->id)->where('is_return', 0)->sum('amount');
        $total = (float) $t->final_total;

        return [
            'total'      => $total,
            'paid'       => $paid,
            'due'        => round($total - $paid, 2),
            'status'     => $t->payment_status,
            'invoice_no' => $t->invoice_no,
        ];
    }

    /**
     * Adjust the linked invoice total in place. Our banquet invoices are
     * header-level service invoices (no product/stock lines), so a direct,
     * guarded update of final_total / total_before_tax is safe — we then let
     * the ERP recompute the payment status. Used by cancellation (reduce to
     * the retained penalty) and reschedule (add a rebooking fee).
     */
    public function adjustInvoiceTotal(BmsBooking $booking, float $newTotal, ?string $note = null): ?int
    {
        if (! $booking->erp_invoice_id) {
            return null;
        }
        $t = Transaction::find($booking->erp_invoice_id);
        if (! $t) {
            return null;
        }

        $newTotal = round(max(0, $newTotal), 2);
        $t->final_total = $newTotal;
        $t->total_before_tax = $newTotal;
        if ($note) {
            $t->additional_notes = trim((string) $t->additional_notes . "\n" . $note);
        }
        $t->save();

        app(TransactionUtil::class)->updatePaymentStatus($t->id, $newTotal);

        return (int) $t->id;
    }

    /** A non-null creator for the ERP transaction (no web user in API flows). */
    protected function resolveUser(BmsBooking $booking): ?int
    {
        return $booking->created_by
            ?? auth()->id()
            ?? optional(\App\Business::find($booking->business_id))->owner_id
            ?? \App\User::where('business_id', $booking->business_id)->min('id');
    }
}
