<?php
/**
 * BookiFlex Stripe Payment Extension
 *
 * @package    BookiFlex
 * @subpackage StripePayment
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Extensions\StripePayment\Action;

use BookiFlex\Extensions\StripePayment\Service\StripeLineItemBuilder;
use FluxSE\PayumStripe\Action\StripeCheckoutSession\Api\StripeCheckoutSessionApiAwareTrait;
use Payum\Core\Action\ActionInterface;
use Payum\Core\ApiAwareInterface;
use Payum\Core\Bridge\Spl\ArrayObject;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\GatewayAwareInterface;
use Payum\Core\GatewayAwareTrait;
use Payum\Core\Model\PaymentInterface;
use Payum\Core\Request\Convert;
use Psr\Log\LoggerInterface;
use Stripe\Checkout\Session;

defined( 'ABSPATH' ) || exit;

/**
 * Enhanced ConvertPaymentAction with detailed line items support.
 *
 * Based on FluxSE\PayumStripe\Action\StripeCheckoutSession\ConvertPaymentAction
 * but extended with StripeLineItemBuilder for rich product information.
 *
 * This is a PREMIUM-ONLY feature.
 */
final class ConvertPaymentAction implements ActionInterface, ApiAwareInterface, GatewayAwareInterface
{
    use GatewayAwareTrait;
    use StripeCheckoutSessionApiAwareTrait;

    private ?StripeLineItemBuilder $lineItemBuilder = null;
    private ?LoggerInterface $logger = null;

    /**
     * Set line item builder service for creating detailed line items.
     *
     * @param StripeLineItemBuilder $builder Builder service for creating line items
     */
    public function setLineItemBuilder(StripeLineItemBuilder $builder): void
    {
        $this->lineItemBuilder = $builder;
    }

    /**
     * Set logger for error logging.
     *
     * @param LoggerInterface|null $logger Logger instance
     */
    public function setLogger(?LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    /**
     * @param Convert $request
     */
    public function execute($request): void
    {
        RequestNotSupportedException::assertSupports($this, $request);

        /** @var PaymentInterface $payment */
        $payment = $request->getSource();

        $details = ArrayObject::ensureArrayObject($payment->getDetails());

        // Set customer email if not provided
        if (false === $details->offsetExists('customer_email')) {
            $details->offsetSet('customer_email', $payment->getClientEmail());
        }

        // Create line_items if not in setup mode and line_items don't exist
        if (
            Session::MODE_SETUP !== $details->offsetGet('mode')
            && false === $details->offsetExists('line_items')
        ) {
            $details->offsetSet('mode', Session::MODE_PAYMENT);

            // Try to build detailed line items with builder
            if ($this->lineItemBuilder !== null) {
                try {
                    $lineItems = $this->lineItemBuilder->buildLineItems($payment);

                    if (!empty($lineItems)) {
                        $details->offsetSet('line_items', $lineItems);
                    } else {
                        // Fallback to default line item
                        $this->setDefaultLineItem($details, $payment);
                    }
                } catch (\Throwable $e) {
                    $this->logger?->error('Stripe line_items build error', [
                        'payment_number' => $payment->getNumber(),
                        'error' => $e->getMessage(),
                    ]);

                    // Fallback to default line item on error
                    $this->setDefaultLineItem($details, $payment);
                }
            } else {
                // No builder available - use default line item
                $this->setDefaultLineItem($details, $payment);
            }
        }

        // Add payment method types from API configuration
        $paymentMethodTypes = $this->api->getPaymentMethodTypes();
        if (
            [] !== $paymentMethodTypes
            && false === $details->offsetExists('payment_method_types')
        ) {
            $details->offsetSet('payment_method_types', $paymentMethodTypes);
        }

        $request->setResult($details);
    }

    public function supports($request): bool
    {
        if (false === $request instanceof Convert) {
            return false;
        }

        if ('array' !== $request->getTo()) {
            return false;
        }

        $payment = $request->getSource();
        return false !== $payment instanceof PaymentInterface;
    }

    /**
     * Set default line item with basic payment information.
     *
     * This is a fallback when StripeLineItemBuilder is not available,
     * returns empty results, or throws an exception.
     *
     * @param ArrayObject $details Session details
     * @param PaymentInterface $payment Payment object
     */
    private function setDefaultLineItem(ArrayObject $details, PaymentInterface $payment): void
    {
        $priceData = [
            'currency' => $payment->getCurrencyCode(),
            'unit_amount' => $payment->getTotalAmount(),
            'product_data' => [
                'name' => $payment->getDescription(),
            ]
        ];

        $details->offsetSet('line_items', [
            [
                'quantity' => 1,
                'price_data' => $priceData,
            ],
        ]);
    }
}
