<?php

namespace Oney\ThreeByFour\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Invoice;
use Magento\Sales\Model\Order\InvoiceRepository;
use Magento\Sales\Model\Order\Payment\Repository as PaymentRepository;
use Magento\Sales\Model\Order\Payment\Transaction;
use Magento\Sales\Model\Order\Shipment;
use Magento\Sales\Model\Order\Status\History as OrderStatusHistory;
use Magento\Sales\Model\OrderRepository;
use Oney\ThreeByFour\Api\Payment\ConfirmInterface;
use Oney\ThreeByFour\Logger\Logger;

/**
 * On Save Shipment Observer
 * 
 * Observer triggered when a shipment is saved.
 * Handles payment confirmation and invoice creation for Oney ThreeByFour orders.
 * 
 * @package Oney\ThreeByFour\Observer
 * @version 1.0.0
 * @author Oney
 * @since 1.0.0
 */
class OnSaveShipment implements ObserverInterface
{
    /**
     * Logger instance
     * 
     * @var Logger
     * @version 1.0.0
     */
    protected $logger;
    
    /**
     * Payment confirmation service
     * 
     * @var ConfirmInterface
     * @version 1.0.0
     */
    protected $confirm;
    
    /**
     * Order status history model
     * 
     * @var OrderStatusHistory
     * @version 1.0.0
     */
    protected $orderStatusHistory;
    
    /**
     * Transaction repository instance
     * 
     * @var Transaction\Repository
     * @version 1.0.0
     */
    protected $_transactionRepository;
    
    /**
     * Invoice repository instance
     * 
     * @var InvoiceRepository
     * @version 1.0.0
     */
    protected $_invoiceRepository;
    
    /**
     * Payment repository instance
     * 
     * @var PaymentRepository
     * @version 1.0.0
     */
    protected $_paymentRepository;
    
    /**
     * Order repository instance
     * 
     * @var OrderRepository
     * @version 1.0.0
     */
    protected $_orderRepository;
    
    /**
     * Transaction builder interface
     * 
     * @var Transaction\BuilderInterface
     * @version 1.0.0
     */
    protected $transactionBuilder;
    
    /**
     * Database transaction instance
     * 
     * @var \Magento\Framework\DB\Transaction
     * @version 1.0.0
     */
    protected $transaction;

    /**
     * Constructor
     * 
     * @param ConfirmInterface $confirm Payment confirmation service
     * @param OrderStatusHistory $orderStatusHistory Order status history
     * @param Transaction\Repository $_transactionRepository Transaction repository
     * @param InvoiceRepository $_invoiceRepository Invoice repository
     * @param PaymentRepository $_paymentRepository Payment repository
     * @param OrderRepository $_orderRepository Order repository
     * @param Order\Payment\Transaction\BuilderInterface $transactionBuilder Transaction builder
     * @param \Magento\Framework\DB\Transaction $transaction Database transaction
     * @param Logger $logger Logger instance
     * @version 1.0.0
     */
    public function __construct(
        ConfirmInterface $confirm,
        OrderStatusHistory $orderStatusHistory,
        Transaction\Repository $_transactionRepository,
        InvoiceRepository $_invoiceRepository,
        PaymentRepository $_paymentRepository,
        OrderRepository $_orderRepository,
        Order\Payment\Transaction\BuilderInterface $transactionBuilder,
        \Magento\Framework\DB\Transaction $transaction,
        Logger $logger
    ) {
        $this->logger = $logger;
        $this->confirm = $confirm;
        $this->orderStatusHistory = $orderStatusHistory;
        $this->_transactionRepository = $_transactionRepository;
        $this->_invoiceRepository = $_invoiceRepository;
        $this->_paymentRepository = $_paymentRepository;
        $this->_orderRepository = $_orderRepository;
        $this->transactionBuilder = $transactionBuilder;
        $this->transaction = $transaction;
    }

    /**
     * Execute observer
     * 
     * Handles shipment save event for Oney ThreeByFour payment orders.
     * Confirms payment and creates invoice when all items are shipped.
     * 
     * @param Observer $observer Observer instance
     * @return void
     * @version 1.0.0
     */
    public function execute(Observer $observer): void
    {
        /** @var $shipment Shipment */
        $shipment = $observer->getShipment();
        $allShipped = true;
        $order = $shipment->getOrder();
        $this->logger->info("[INFO] [OBSERVER] OnSaveShipment triggered: ", [
            'order_id' => $order->getId(),
            'shipment_id' => $shipment->getId()
        ]);
        foreach ($order->getAllItems() as $item) {
            $this->logger->info("[INFO] [OBSERVER] Item shippable check: ", [
                'order_item_id' => $item->getId(),
                'qty_to_ship' => $item->getQtyToShip(),
                'is_virtual' => $item->getIsVirtual()
            ]);
            $allShipped &= $item->getQtyToShip() == 0;
        }
        if ($allShipped && strpos($order->getPayment()->getMethod(), 'threebyfour_') !== false) {
            try {
            $this->logger->info("[SUCCESS] [OBSERVER] All items shipped, confirming payment: ", [
                'order_id' => $order->getId()
            ]);
            if ($this->confirm->confirm($order)) {
                $history = $this->orderStatusHistory
                    ->setStatus($order->getStatus())
                    ->setComment('Payment Captured, status : FUNDED')
                    ->setEntityName(Order::ENTITY)
                    ->setIsCustomerNotified(false)
                    ->setCreatedAt(date('Y-m-d H:i:s'));
                $order->addStatusHistory($history);
                $this->createInvoiceForOrder($order, $order->getIncrementId());
                $this->logger->info("[SUCCESS] [OBSERVER] Payment captured: ", [
                    'order_id' => $order->getId(),
                    'status' => 'FUNDED'
                ]);
            }
            } catch(\Exception $e) {
                $this->logger->info("[ERROR] [OBSERVER] Error while creating shipment and invoice: ", [
                    'order_id' => $order->getId(),
                    'error' => $e->getMessage()
                ]);
            }
        }
        $this->logger->info("[INFO] [OBSERVER] Not all items shipped, nothing to do: ", [
            'order_id' => $order->getId()
        ]);
    }

    /**
     * Create invoice for order
     * 
     * Creates and processes invoice for the order after payment confirmation.
     * 
     * @param Order $order Order instance
     * @param string $order_reference Order reference
     * @return void
     * @version 1.0.0
     */
    protected function createInvoiceForOrder(Order $order, string $order_reference): void
    {
        $this->addTransactionToOrder($order, $order_reference);
        $invoice = $order->prepareInvoice();
        $invoice->setRequestedCaptureCase(Invoice::CAPTURE_ONLINE);
        $invoice->setState(Invoice::STATE_PAID);
        $invoice->setBaseGrandTotal($order->getBaseGrandTotal());
        $invoice->register();
        $invoice->getOrder()->setIsInProcess(true);
        $invoice->pay();

        $transactionSave = $this->transaction
            ->addObject($invoice)
            ->addObject($order);
        $transactionSave->save();

        $order->setTotalPaid($order->getTotalPaid());
        $order->setBaseTotalPaid($order->getBaseGrandTotal());
        $this->_invoiceRepository->save($invoice);

        $order->setStatus(Order::STATE_COMPLETE);
        $this->_orderRepository->save($order);
    }

    /**
     * Add transaction to order
     * 
     * Creates and saves payment transaction for the order.
     * 
     * @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);
    }
}
