<?php

namespace Mtc\Modules\B2B\Services;

use Illuminate\Support\Facades\DB;
use Mtc\Modules\B2B\Enums\AccountOrderStatus;
use Mtc\Modules\B2B\Enums\TradeCustomerStatus;
use Mtc\Modules\B2B\Events\AccountOrderPaidEvent;
use Mtc\Modules\B2B\Models\AccountOrder;
use Mtc\Modules\B2B\Models\TradeCustomer;
use Mtc\Shop\Order;

class AccountPaymentService
{
    public function __construct(private PoNumberService $poNumberService) {}

    /**
     * Render "Pay on Account" payment option for trade customers at checkout.
     * Returns empty string for non-trade customers.
     */
    public function renderPayOnAccountForm($basket): string
    {
        $memberId = $basket->member ?? null;
        if (!$memberId) {
            return '';
        }

        $tradeCustomer = TradeCustomer::where('member_id', $memberId)
            ->where('status', TradeCustomerStatus::Approved->value)
            ->first();

        if (!$tradeCustomer) {
            return '';
        }

        $twig = app('twig');
        return $twig->render('b2b/payment_form.twig', [
            'trade_customer' => $tradeCustomer,
            'basket' => $basket,
        ]);
    }

    /**
     * Create an account order record with auto-generated PO number.
     */
    public function createAccountOrder(Order $order, TradeCustomer $customer): AccountOrder
    {
        $poNumber = $this->poNumberService->generate();

        return AccountOrder::firstOrCreate(
            ['order_id' => $order->id],
            [
                'trade_customer_id' => $customer->id,
                'po_number'         => $poNumber,
                'amount_due'        => $order->cost_total,
                'status'            => AccountOrderStatus::Unpaid->value,
            ]
        );
    }

    /**
     * Process payment for selected account orders.
     * All-or-nothing: single charge for summed total, atomic DB transaction.
     * Events are dispatched after the transaction commits to prevent email
     * failures from rolling back a completed payment.
     *
     * @param array $orderIds Account order IDs to pay
     * @param TradeCustomer $customer
     * @param string $paymentReference Gateway reference from payment callback
     * @return bool
     */
    public function markOrdersPaid(array $orderIds, TradeCustomer $customer, string $paymentReference = ''): bool
    {
        // Validate all selected orders belong to this customer and are unpaid
        $orders = AccountOrder::whereIn('id', $orderIds)
            ->where('trade_customer_id', $customer->id)
            ->where('status', AccountOrderStatus::Unpaid->value)
            ->get();

        if ($orders->count() !== count($orderIds)) {
            return false;
        }

        // Collect paid orders to dispatch events after the transaction
        $paid = [];

        DB::transaction(function () use ($orders, $paymentReference, &$paid) {
            foreach ($orders as $accountOrder) {
                $accountOrder->markPaid('card', $paymentReference);
                $paid[] = $accountOrder->load('tradeCustomer');
            }
        });

        // Dispatch outside the transaction — email failures won't roll back payments
        foreach ($paid as $accountOrder) {
            AccountOrderPaidEvent::dispatch($accountOrder);
        }

        return true;
    }

    /**
     * Calculate total amount due for selected orders.
     */
    public function calculateTotal(array $orderIds, TradeCustomer $customer): float
    {
        return AccountOrder::whereIn('id', $orderIds)
            ->where('trade_customer_id', $customer->id)
            ->where('status', AccountOrderStatus::Unpaid->value)
            ->sum('amount_due');
    }
}
