<?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\Service;

use BookiFlex\Core\Service\ConfigurationService;
use BookiFlex\Core\Service\Reservation\ReservationStatusManager;
use BookiFlex\Extensions\Payment\Model\Payment;
use Payum\Core\GatewayInterface;
use Payum\Core\Payum;
use Payum\Core\Request\GetHumanStatus;
use Payum\Core\Security\TokenInterface;

defined( 'ABSPATH' ) || exit;

class SyncPaymentStatusWithGateway
{
    public function __construct(
        private readonly Payum $payum,
        private readonly ReservationStatusManager $reservationStatusManager,
        private readonly ConfigurationService $config,
    ) {
    }

    /**
     * Synchronizes payment status directly using payment object (no token required).
     * Use when no valid Payum token exists, e.g. for manual admin sync.
     */
    public function syncDirect(GatewayInterface $gateway, Payment $payment): void
    {
        $status = new GetHumanStatus($payment);
        $gateway->execute($status);
        $this->updatePaymentStatus($payment, $status);

        $storage = $this->payum->getStorage(Payment::class);
        $storage->update($payment);

        if ($payment->isPaid()) {
            $this->confirmReservations($payment);
        }
    }

    /**
     * Synchronizes payment status with gateway model
     */
    public function sync(GatewayInterface $gateway, TokenInterface $token): void
    {
        $gateway->execute($status = new GetHumanStatus($token));

        $payment = $this->getPaymentFromToken($token);
        if (!$payment) {
            return;
        }

        $this->updatePaymentStatus($payment, $status);
        $this->savePayment($payment, $token);

        if ($payment->isPaid()) {
            $this->confirmReservations($payment);
        }
    }

    /**
     * Retrieves payment from token
     */
    private function getPaymentFromToken(TokenInterface $token): ?Payment
    {
        $identity = $token->getDetails();
        $storage  = $this->payum->getStorage($identity->getClass());

        /** @var Payment|null $payment */
        $payment = $storage->find($identity);
        return $payment;
    }

    /**
     * Updates payment status based on gateway status
     */
    private function updatePaymentStatus(Payment $payment, GetHumanStatus $status): void
    {
		// if payment is not started
		if ($payment->getDetails() && isset($payment->getDetails()['status'])) {
			if ($payment->getDetails()['status'] === 'canceled') {
				$payment->markCancelled();
				return;
			}
		}

        // success
        if ($status->isCaptured()) {
            $payment->markPaid();
        }

        if ($status->isAuthorized()) {
            $payment->markAuthorized();
        }

        if ($status->isNew() || $status->isPending()) {
            $payment->markWaiting();
        }
//		var_dump($status->getValue(), $payment->getDetails()); die;
//	    if ($status->isExpired()) {
//		    $payment->markExpired();
//	    }

        // block changing gateway for payment
        // the payment has failed or user canceled it.
        if ($status->isFailed() || $status->isCanceled() || $status->isExpired()) {
            $payment->markCancelled();
        }
    }

    /**
     * Saves payment to storage
     */
    private function savePayment(Payment $payment, TokenInterface $token): void
    {
        $identity = $token->getDetails();
        $storage  = $this->payum->getStorage($identity->getClass());
        $storage->update($payment);
    }

    /**
     * Confirms all reservations associated with the payment
     */
    public function confirmReservations(Payment $payment): void
    {
        foreach ($payment->getPaymentAllocations() as $paymentAllocation) {
            $reservationId = $paymentAllocation->getReservation()->getId();

            if ($this->config->autoConfirmationAfterPaymentIsPaid()) {
                $this->reservationStatusManager->confirmReservation($reservationId);
            } else {
                $this->reservationStatusManager->needConfirmation($reservationId);
            }
        }
    }
}
