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

declare(strict_types=1);

namespace BookiFlex\Extensions\Payment\Api\Controller;

use BookiFlex\Core\Api\Controller\AbstractController;
use BookiFlex\Core\Exception\Exception as CoreException;
use BookiFlex\Extensions\Payment\Service\PaymentManager;
use Throwable;
use WP_Error;
use WP_REST_Request;

defined('ABSPATH') || exit;

/**
 * Payment Gateway Callback Controller
 *
 * Handles callbacks from external payment gateways (Stripe, PayPal, etc.).
 * These endpoints are called by payment systems during payment flow.
 * They perform redirects or output HTML, not REST API responses.
 */
class PaymentCallbackController extends AbstractController
{
    public const API_ENDPOINT_NAME = 'payment';

    public function __construct(
        private readonly PaymentManager $paymentManager,
        string                          $namespace = 'bflex/v1'
    ) {
        parent::__construct($namespace);
    }

    /**
     * Register payment gateway callback routes
     */
    public function registerRoutes(): void
    {
        // Public endpoint: payment gateway capture callback. This endpoint is intentionally public
        // and does not use WordPress nonces, as it is called by external payment gateways (Stripe,
        // PayPal, etc.) via Payum-registered callbacks. Request authenticity is validated internally
        // by the payment gateway using Payum's HttpRequestVerifier.
        register_rest_route(
            $this->namespace, '/' . self::API_ENDPOINT_NAME . '/capture', [
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'capture'],
                'permission_callback' => '__return_true',
            ],
            ]
        );

        // Public endpoint: payment gateway authorize callback. This endpoint is intentionally public
        // and does not use WordPress nonces, as it is called by external payment gateways (Stripe,
        // PayPal, etc.) via Payum-registered callbacks. Request authenticity is validated internally
        // by the payment gateway using Payum's HttpRequestVerifier.
        register_rest_route(
            $this->namespace, '/' . self::API_ENDPOINT_NAME . '/authorize', [
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'authorize'],
                'permission_callback' => '__return_true',
            ],
            ]
        );

        // Public endpoint: payment completion callback. This endpoint is intentionally public
        // and does not use WordPress nonces, as it is called by external payment gateways (Stripe,
        // PayPal, etc.) via Payum-registered callbacks. Request authenticity is validated internally
        // by the payment gateway using Payum's HttpRequestVerifier.
        register_rest_route(
            $this->namespace, '/' . self::API_ENDPOINT_NAME . '/done', [
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'done'],
                'permission_callback' => '__return_true',
            ],
            ]
        );

        // Public endpoint: payment cancellation callback. This endpoint is intentionally public
        // and does not use WordPress nonces, as it is called by external payment gateways (Stripe,
        // PayPal, etc.) via Payum-registered callbacks. Request authenticity is validated internally
        // by the payment gateway using Payum's HttpRequestVerifier.
        register_rest_route(
            $this->namespace, '/' . self::API_ENDPOINT_NAME . '/cancel', [
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'cancel'],
                'permission_callback' => '__return_true',
            ],
            ]
        );

        // Public endpoint: payment gateway webhook notification. This endpoint is intentionally public
        // and does not use WordPress nonces, as it is called by external payment gateways (Stripe,
        // PayPal, etc.) via Payum-registered callbacks. Request authenticity is validated internally
        // by the payment gateway using Payum's HttpRequestVerifier.
        register_rest_route(
            $this->namespace, '/' . self::API_ENDPOINT_NAME . '/notify', [
            [
                'methods'             => 'POST',
                'callback'            => [$this, 'notify'],
                'permission_callback' => '__return_true',
            ],
            ]
        );

        // Public endpoint: gateway-specific webhook notification. This endpoint is intentionally public
        // and does not use WordPress nonces, as it is called by external payment gateways (Stripe,
        // PayPal, etc.) via Payum-registered callbacks. Request authenticity is validated internally
        // by the payment gateway using Payum's HttpRequestVerifier.
        register_rest_route(
            $this->namespace, '/' . self::API_ENDPOINT_NAME . '/notify/unsafe/(?P<gatewayName>[a-zA-Z0-9\-_]+)', [
            [
                'methods'             => 'POST',
                'callback'            => [$this, 'notify'],
                'permission_callback' => '__return_true',
            ],
            ]
        );
    }

    /**
     * GET /payment/capture - Payment gateway capture callback
     *
     * Security: This endpoint is intentionally public and does not use WordPress nonces, as it
     * is called by external payment gateways (Stripe, PayPal, etc.) via Payum-registered callbacks.
     * Request authenticity is validated by Payum using $payum->getHttpRequestVerifier()->verify($request).
     * The $_REQUEST superglobal is passed directly to Payum for verification and processing.
     *
     * This endpoint is called by payment gateways after payment processing.
     * On success: redirects user (never returns)
     * On error: returns WP_Error
     *
     * @return WP_Error|never
     */
    public function capture(): \WP_Error
    {
        try {
            // Input validation is handled by the Payum library internally.
            $this->paymentManager->captureByToken($_REQUEST);
        } catch (CoreException $e) {
            $context = $e->getContext();
            $errorCode = $context['errorCode'] ?? 'unknown_error';

            return $this->error(
                'validation_failed',
                $e->getMessage(),
                $e->getCode() ?: 400,
                [
                    'params' => [
                        $errorCode => $e->getMessage()
                    ]
                ]
            );
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'payment_capture_failed',
                __('Failed to capture a payment.', 'bookiflex'),
            );
        }
    }

    /**
     * GET /payment/authorize - Payment gateway authorize callback
     *
     * Security: This endpoint is intentionally public and does not use WordPress nonces, as it
     * is called by external payment gateways (Stripe, PayPal, etc.) via Payum-registered callbacks.
     * Request authenticity is validated by Payum using $payum->getHttpRequestVerifier()->verify($request).
     * The $_REQUEST superglobal is passed directly to Payum for verification and processing.
     *
     * This endpoint is called by payment gateways after authorization.
     * On success: redirects user (never returns)
     * On error: returns WP_Error
     *
     * @return WP_Error|never
     */
    public function authorize(): \WP_Error
    {
        try {
            // Input validation is handled by the Payum library internally.
            $this->paymentManager->authorizeByToken($_REQUEST);
        } catch (CoreException $e) {
            $context = $e->getContext();
            $errorCode = $context['errorCode'] ?? 'unknown_error';

            return $this->error(
                'validation_failed',
                $e->getMessage(),
                $e->getCode() ?: 400,
                [
                    'params' => [
                        $errorCode => $e->getMessage()
                    ]
                ]
            );
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'payment_authorize_failed',
                __('Failed to authorize a payment.', 'bookiflex'),
            );
        }
    }

    /**
     * GET /payment/done - Payment completion callback
     *
     * Security: This endpoint is intentionally public and does not use WordPress nonces, as it
     * is called by external payment gateways (Stripe, PayPal, etc.) via Payum-registered callbacks.
     * Request authenticity is validated by Payum using $payum->getHttpRequestVerifier()->verify($request).
     * The $_REQUEST superglobal is passed directly to Payum for verification and processing.
     *
     * This endpoint is called after successful payment processing.
     * Displays success page with redirect (never returns on success)
     * On error: returns WP_Error
     *
     * @return WP_Error|never
     */
    public function done(): \WP_Error
    {
        try {
            // Input validation is handled by the Payum library internally.
            $this->paymentManager->done($_REQUEST);
        } catch (CoreException $e) {
            $context = $e->getContext();
            $errorCode = $context['errorCode'] ?? 'unknown_error';

            return $this->error(
                'validation_failed',
                $e->getMessage(),
                $e->getCode() ?: 400,
                [
                    'params' => [
                        $errorCode => $e->getMessage()
                    ]
                ]
            );
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'payment_done_failed',
                __('Failed to done a payment.', 'bookiflex'),
            );
        }
    }

    /**
     * GET /payment/cancel - Payment cancellation callback
     *
     * Security: This endpoint is intentionally public and does not use WordPress nonces, as it
     * is called by external payment gateways (Stripe, PayPal, etc.) via Payum-registered callbacks.
     * Request authenticity is validated by Payum using $payum->getHttpRequestVerifier()->verify($request).
     * The $_REQUEST superglobal is passed directly to Payum for verification and processing.
     *
     * This endpoint is called when payment is cancelled.
     * On success: redirects user (never returns)
     * On error: returns WP_Error
     *
     * @return WP_Error|never
     */
    public function cancel(): \WP_Error
    {
        try {
            // Input validation is handled by the Payum library internally.
            $this->paymentManager->cancel($_REQUEST);
        } catch (CoreException $e) {
            $context = $e->getContext();
            $errorCode = $context['errorCode'] ?? 'unknown_error';

            return $this->error(
                'validation_failed',
                $e->getMessage(),
                $e->getCode() ?: 400,
                [
                    'params' => [
                        $errorCode => $e->getMessage()
                    ]
                ]
            );
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'payment_cancel_failed',
                __('Failed to cancel a payment.', 'bookiflex'),
            );
        }
    }

    /**
     * POST /payment/notify - Payment gateway webhook/notification callback
     *
     * Security: This endpoint is intentionally public and does not use WordPress nonces, as it
     * is called by external payment gateways (Stripe, PayPal, etc.) via Payum-registered callbacks.
     * Request authenticity is validated by Payum using $payum->getHttpRequestVerifier()->verify($request).
     * The $_REQUEST superglobal is passed directly to Payum for verification and processing.
     *
     * This endpoint receives notifications from payment gateways (webhooks).
     * Processes async payment status updates.
     * On success: returns 204 status (never returns normally)
     * On error: returns WP_Error
     *
     * @return WP_Error|never
     */
    public function notify(WP_REST_Request $request): \WP_Error
    {
        try {
            // Input validation is handled by the Payum library internally.
            $this->paymentManager->notify($_REQUEST, $request->get_param('gatewayName'));
        } catch (CoreException $e) {
            $context = $e->getContext();
            $errorCode = $context['errorCode'] ?? 'unknown_error';

            return $this->error(
                'validation_failed',
                $e->getMessage(),
                $e->getCode() ?: 400,
                [
                    'params' => [
                        $errorCode => $e->getMessage()
                    ]
                ]
            );
        } catch (Throwable $e) {
            return $this->handleError(
                $e,
                'payment_notify_failed',
                __('Failed to notify a payment.', 'bookiflex'),
            );
        }
    }
}
