<?php

namespace Oney\ThreeByFour\Controller\Payment;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\RequestInterface;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Invoice;
use Magento\Sales\Model\Order\Payment;
use Magento\Sales\Model\Order\InvoiceRepository;
use Magento\Sales\Model\Order\Payment\Transaction;
use Magento\Store\Model\StoreManagerInterface;
use Oney\ThreeByFour\Api\Payment\ConfirmInterface;
use Oney\ThreeByFour\Helper\Config;
use Oney\ThreeByFour\Logger\Logger;
use Oney\ThreeByFour\Model\Method\ThreeByFour;
use Magento\Sales\Model\OrderRepository;
use \Magento\Sales\Model\Convert\Order as OrderConverter;
use Magento\Sales\Model\Order\Status\History as OrderStatusHistory;

/**
 * Payment Callback Controller
 * 
 * Handles payment callbacks from Oney payment gateway.
 * Processes order status updates, invoice creation, and shipment management.
 * 
 * @package Oney\ThreeByFour\Controller\Payment
 * @version 1.0.0
 * @author Oney
 * @since 1.0.0
 */
class Callback extends Action implements CsrfAwareActionInterface
{
    /**
     * Configuration helper instance
     * 
     * @var Config
     * @version 1.0.0
     */
    protected $_helperConfig;
    
    /**
     * Logger instance
     * 
     * @var Logger
     * @version 1.0.0
     */
    protected $_logger;
    
    /**
     * Order model instance
     * 
     * @var Order
     * @version 1.0.0
     */
    protected $_order;
    
    /**
     * Order status history model
     * 
     * @var OrderStatusHistory
     * @version 1.0.0
     */
    protected $_orderStatusHistory;
    
    /**
     * Order repository instance
     * 
     * @var OrderRepository
     * @version 1.0.0
     */
    protected $_orderRepository;
    
    /**
     * Store manager interface
     * 
     * @var StoreManagerInterface
     * @version 1.0.0
     */
    protected $storeManager;
    /**
     * Transaction builder interface
     * 
     * @var Order\Payment\Transaction\BuilderInterface
     * @version 1.0.0
     */
    protected $_transactionBuilder;
    
    /**
     * Transaction repository instance
     * 
     * @var Transaction\Repository
     * @version 1.0.0
     */
    protected $_transactionRepository;
    
    /**
     * Payment repository instance
     * 
     * @var Payment\Repository
     * @version 1.0.0
     */
    protected $_paymentRepository;
    
    /**
     * Invoice repository instance
     * 
     * @var InvoiceRepository
     * @version 1.0.0
     */
    protected $_invoiceRepository;
    
    /**
     * Database transaction instance
     * 
     * @var \Magento\Framework\DB\Transaction
     * @version 1.0.0
     */
    protected $transaction;
    
    /**
     * Payment confirmation service
     * 
     * @var ConfirmInterface
     * @version 1.0.0
     */
    protected $_confirmService;
    
    /**
     * Order converter instance
     * 
     * @var OrderConverter
     * @version 1.0.0
     */
    protected $orderConverter;
    
    /**
     * Shipment repository instance
     * 
     * @var Order\ShipmentRepository
     * @version 1.0.0
     */
    protected $shipmentRepository;

    /**
     * Constructor
     * 
     * @param Context $context Action context
     * @param Config $helperConfig Configuration helper
     * @param Logger $logger Logger instance
     * @param Order $order Order model
     * @param StoreManagerInterface $storeManager Store manager
     * @param OrderRepository $orderResourceModel Order repository
     * @param \Magento\Framework\DB\Transaction $transaction Database transaction
     * @param ConfirmInterface $_confirmService Payment confirmation service
     * @param Transaction\Repository $_transactionRepository Transaction repository
     * @param InvoiceRepository $_invoiceRepository Invoice repository
     * @param Payment\Repository $_paymentRepository Payment repository
     * @param OrderStatusHistory $orderStatusHistory Order status history
     * @param OrderConverter $orderConverter Order converter
     * @param Order\ShipmentRepository $shipmentRepository Shipment repository
     * @param Order\Payment\Transaction\BuilderInterface $transactionBuilder Transaction builder
     * @version 1.0.0
     */
    public function __construct(
        Context $context,
        Config $helperConfig,
        Logger $logger,
        Order $order,
        StoreManagerInterface $storeManager,
        OrderRepository $orderResourceModel,
        \Magento\Framework\DB\Transaction $transaction,
        ConfirmInterface $_confirmService,
        Transaction\Repository $_transactionRepository,
        InvoiceRepository $_invoiceRepository,
        Payment\Repository $_paymentRepository,
        OrderStatusHistory $orderStatusHistory,
        OrderConverter $orderConverter,
        Order\ShipmentRepository $shipmentRepository,
        Order\Payment\Transaction\BuilderInterface $transactionBuilder
    ) {
        $this->_logger = $logger;
        $this->_confirmService = $_confirmService;
        $this->transaction = $transaction;
        $this->_orderRepository = $orderResourceModel;
        $this->_orderStatusHistory = $orderStatusHistory;
        $this->_paymentRepository = $_paymentRepository;
        $this->_transactionRepository = $_transactionRepository;
        $this->_invoiceRepository = $_invoiceRepository;
        $this->_order = $order;
        $this->_transactionBuilder = $transactionBuilder;
        $this->storeManager = $storeManager;
        $this->_helperConfig = $helperConfig;
        parent::__construct($context);
        $this->orderConverter = $orderConverter;
        $this->shipmentRepository = $shipmentRepository;
    }

    /**
     * Execute callback action
     * 
     * Processes payment callback from Oney gateway, updates order status,
     * creates invoices and shipments based on payment status.
     * 
     * @return void
     * @version 1.0.0
     */
    public function execute(): void
    {
        $response = json_decode($this->getRequest()->getContent(), true);

        $purchase = $response['purchase'];
        $orderId = $purchase['external_reference'];

        $order = $this->_order->loadByIncrementId($orderId);

        if (!$order->getId()) {
            $this->_logger->info("[ERROR] [RESPONSE] Payment API 1.2 - No order found for increment id: ", [
                'increment_id' => $orderId
            ]);
            return;
        }
        if ($order->getState() === Order::STATE_CANCELED) {
            $this->_logger->info("[ERROR] [RESPONSE] Payment API 1.2 - Order already canceled: ", [
                'order_id' => $order->getId(),
                'state' => $order->getState()
            ]);
            return;
        }
        if ($order->getState() === Order::STATE_CLOSED) {
            $this->_logger->info("[ERROR] [RESPONSE] Payment API 1.2 - Order already closed: ", [
                'order_id' => $order->getId(),
                'state' => $order->getState()
            ]);
            return;
        }

        $this->storeManager->setCurrentStore($order->getStoreId());
        $merchant_guid = $this->_helperConfig->getGeneralConfigValue('merchant_guid');

        if (!isset($response['merchant_guid']) || $merchant_guid !== $response['merchant_guid']) {
            $this->_logger->info("[ERROR] [RESPONSE] Payment API 1.2 - Merchant GUID mismatch: ", [
                'expected' => $merchant_guid,
                'received' => $response['merchant_guid'] ?? null,
                'order_id' => $order->getId()
            ]);
            echo json_encode(["error" => "Merchant GUID does not match the configuration"]);
            return;
        }

        if (!isset($purchase['status_code'])) {
            $this->_logger->info("[ERROR] [RESPONSE] Payment API 1.2 - Missing purchase status_code: ", [
                'order_id' => $order->getId(),
                'purchase' => $purchase ?? null
            ]);
            return;
        }

        $history = $this->_orderStatusHistory
            ->setStatus($order->getStatus())
            ->setComment($purchase['status_label'] . ', status : ' . $purchase['status_code'])
            ->setEntityName(Order::ENTITY)
            ->setIsCustomerNotified(false)
            ->setCreatedAt(date('Y-m-d H:i:s'));

        $order->addStatusHistory($history);
        $this->_orderRepository->save($order);

        if (in_array($purchase['status_code'], [ThreeByFour::STATUS_REFUSED, ThreeByFour::STATUS_ABORTED, ThreeByFour::STATUS_CANCELLED]) && !count($order->getInvoiceCollection())) {
            $history = $this->_orderStatusHistory
                ->setStatus($order->getStatus())
                ->setComment($purchase['status_label'] . ', status : ' . $purchase['status_code'])
                ->setEntityName(Order::ENTITY)
                ->setIsCustomerNotified(false)
                ->setCreatedAt(date('Y-m-d H:i:s'));
            $order->addStatusHistory($history);
            $order->cancel();
            $this->_orderRepository->save($order);
        }

        if (in_array($purchase['status_code'], [ThreeByFour::STATUS_PENDING, ThreeByFour::STATUS_FAVORABLE]) && !count($order->getInvoiceCollection())) {
            $this->addTransactionToOrder($order, $purchase['external_reference']);
            $history = $this->_orderStatusHistory
                ->setStatus($order->getStatus())
                ->setComment($purchase['status_label'] . ', status : ' . $purchase['status_code'])
                ->setEntityName(Order::ENTITY)
                ->setIsCustomerNotified(false)
                ->setCreatedAt(date('Y-m-d H:i:s'));
            $order->addStatusHistory($history);
            if ($purchase['status_code'] === ThreeByFour::STATUS_FAVORABLE) {
                $order->setStatus(Order::STATE_PROCESSING);
                if ($this->_helperConfig->getGeneralConfigValue("automatic_ship")) {
                    $this->_logger->info('[SUCCESS] [RESPONSE] Payment API 1.2 - FAVORABLE status received, creating shipment: ', [
                        'order_id' => $order->getId()
                    ]);
                    $this->shipOrder($order);
                }
            } else {
                $order->setStatus(Order::STATE_PAYMENT_REVIEW);
            }
            $this->_orderRepository->save($order);
        }
        $this->getResponse()->setHttpResponseCode(200);
    }

    /**
     * Add transaction to order
     * 
     * Creates and saves a payment transaction for the order with authorization type.
     * 
     * @param Order $order Order instance
     * @param string $transaction_id Transaction ID
     * @return void
     * @version 1.0.0
     */
    protected function addTransactionToOrder(Order $order, string $transaction_id): void
    {
        $payment = $order->getPayment();
        $formatedPrice = $order->getBaseCurrency()->formatTxt($order->getGrandTotal());
        $payment->setLastTransId($transaction_id)
            ->setTransactionId($transaction_id);
        $transaction = $this->_transactionBuilder->setTransactionId($transaction_id)
            ->setOrder($order)
            ->setPayment($payment)
            ->build(Transaction::TYPE_AUTH);
        $payment->addTransactionCommentsToOrder($transaction, __('Authorized payment with amount %1', $formatedPrice));
        $payment->setParentTransactionId(null);

        $this->_paymentRepository->save($payment);
        $this->_orderRepository->save($order);
        $this->_transactionRepository->save($transaction);
    }

    /**
     * Create CSRF validation exception
     * 
     * @param RequestInterface $request Request instance
     * @return InvalidRequestException|null
     * @version 1.0.0
     */
    public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
    {
        return null;
    }

    /**
     * Validate for CSRF
     * 
     * @param RequestInterface $request Request instance
     * @return bool|null
     * @version 1.0.0
     */
    public function validateForCsrf(RequestInterface $request): ?bool
    {
        return true;
    }

    /**
     * Ship order
     * 
     * Creates shipment for the order if possible.
     * 
     * @param Order $order Order instance
     * @return bool True if shipment created successfully
     * @version 1.0.0
     */
    private function shipOrder(Order $order): bool
    {
        if (!$order->canShip()) {
            $this->_logger->info("[ERROR] [CALLBACK] Payment API 1.2 - Shipment creation not allowed", [
                'order_id' => $order->getId()
            ]);
            return false;
        }
        try {
            $shipment = $this->orderConverter->toShipment($order);
        }catch (\Exception $e) {
            $this->_logger->info("[ERROR] [CALLBACK] Payment API 1.2 - Order to shipment conversion error", [
                'order_id' => $order->getId(),
                'error' => $e->getMessage()
            ]);
            return false;
        }
        foreach ($order->getAllItems() as $item) {
            if (!$item->getQtyToShip() || $item->getIsVirtual()) {
                continue;
            }
            $qtyShipped = $item->getQtyToShip();
            $shipmentItem = $this->orderConverter->itemToShipmentItem($item)->setQty($qtyShipped);
            $shipment->addItem($shipmentItem);
        }
        $shipment->register();
        $shipment->getOrder()->setIsInProcess(true);

        try {
            $this->shipmentRepository->save($shipment);
            $this->_orderRepository->save($shipment->getOrder());
            $this->_logger->info('[SUCCESS] [POST] Payment API 1.2 - Shipment successfully created: ', [
                'order_id' => $order->getId(),
                'shipment_id' => $shipment->getId()
            ]);
            return true;
        } catch (\Exception $e) {
            $this->_logger->info("[ERROR] [CALLBACK] Payment API 1.2 - Shipment save error: ", [
                'order_id' => $order->getId(),
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }
}
