<?php
/**
 * Webkul Software.
 *
 * @category  Webkul
 * @package   Webkul_MpPayPal
 * @author    Webkul
 * @copyright Copyright (c) Webkul Software Private Limited (https://webkul.com)
 * @license   https://store.webkul.com/license.html
 */

namespace Webkul\MpPayPal\Model;

use Magento\Customer\Model\Session;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Payment\Transaction;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
use Webkul\MpPayPal\Logger\Logger;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Webkul\Marketplace\Helper\Orders as MarketplaceOrders;
use Webkul\Marketplace\Model\SaleslistFactory;

class PayPalOrderManagement implements \Webkul\MpPayPal\Api\PayPalOrderManagementInterface
{
    /**
     * @var Magento\Customer\Model\Session
     */
    protected $_customerSession;

    /**
     * $_scopeConfig.
     *
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $_scopeConfig;

    /**
     * $_helper.
     *
     * @var \Webkul\MpPayPal\Helper\Data
     */
    protected $_helper;

    /**
     * $_orderRepository.
     *
     * @var OrderRepositoryInterface
     */
    protected $_orderRepository;

    /**
     * $_transactionBuilder.
     *
     * @var Transaction
     */
    protected $_transactionBuilder;

    /**
     * $_transactionFactory.
     *
     * @var Transaction
     */
    protected $_transactionFactory;

    /**
     * @var InvoiceSender
     */
    protected $_invoiceSender;

    /**
     * $_checkoutSession.
     *
     * @var \Magento\Checkout\Model\Session
     */
    protected $_checkoutSession;

    /**
     * $_logger.
     *
     * @var Webkul\MpPayPal\Logger\Logger
     */
    protected $_logger;

    /**
     * @var PriceCurrencyInterface
     */
    protected $_priceCurrency;

    /**
     * paypal configuration
     *
     * @var Webkul\MpPayPal\Gateway\Config\Config
     */
    protected $config;
    
    /**
     * paypal payment adapter
     *
     * @var Webkul\MpPayPal\Model\PayPal\Adapter
     */
    protected $adapter;

    /**
     * marketplace order helper
     *
     * @var MarketplaceOrders
     */
    protected $marketplaceOrders;

    /**
     * order management interface
     *
     * @var \Magento\Sales\Api\OrderManagementInterface
     */
    protected $orderManagement;

    /**
     * @var \Webkul\MpPayPal\Model\MpPayPalFactory
     */
    protected $transactionData;

    /**
     * @var \Magento\Sales\Model\OrderFactory
     */
    protected $_orderFactory;

    public function __construct(
        Session $customerSession,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Webkul\MpPayPal\Helper\Data $helper,
        OrderRepositoryInterface $orderRepository,
        \Magento\Checkout\Model\Session $checkoutSession,
        Transaction\BuilderInterface $transactionBuilder,
        \Magento\Sales\Model\Order\Payment\TransactionFactory $transactionFactory,
        InvoiceSender $invoiceSender,
        PriceCurrencyInterface $priceCurrency,
        Logger $logger,
        \Webkul\MpPayPal\Gateway\Config\Config $config,
        \Webkul\MpPayPal\Model\PayPal\Adapter $adapter,
        MarketplaceOrders $marketplaceOrders,
        \Magento\Sales\Api\OrderManagementInterface $orderManagement,
        \Webkul\MpPayPal\Model\MpPayPalFactory $transactionData,
        \Magento\Sales\Model\OrderFactory $orderFactory,
        \Magento\Sales\Model\Service\InvoiceServiceFactory $invoiceService,
        \Magento\Quote\Model\QuoteManagement $quoteManagement,
        \Magento\Quote\Model\QuoteRepository $quoteRepository,
        SaleslistFactory $saleslistFactory,
        \Magento\Framework\DB\TransactionFactory $transactions
    ) {
        $this->_helper = $helper;
        $this->_customerSession = $customerSession;
        $this->_scopeConfig = $scopeConfig;
        $this->_orderRepository = $orderRepository;
        $this->_checkoutSession = $checkoutSession;
        $this->_transactionBuilder = $transactionBuilder;
        $this->_transactionFactory = $transactionFactory;
        $this->_invoiceSender = $invoiceSender;
        $this->_logger = $logger;
        $this->_priceCurrency = $priceCurrency;
        $this->config = $config;
        $this->adapter = $adapter;
        $this->marketplaceOrders = $marketplaceOrders;
        $this->orderManagement = $orderManagement;
        $this->transactionData = $transactionData;
        $this->_orderFactory = $orderFactory;
        $this->invoiceService = $invoiceService;
        $this->quoteManagement = $quoteManagement;
        $this->quoteRepository = $quoteRepository;
        $this->saleslistFactory = $saleslistFactory;
        $this->transactions = $transactions;
    }

    /**
     * {@inheritdoc}
     */
    public function doSellerOrderInvoice($order, $sellerData, $transactionId, $sellerCount, $shippingArray)
    {
        $order = $this->validateOrder($order);
        if ($order && $order->getTotalDue() > 0) {
            try {
                $orderId = $order->getId();
                $sellerId = $sellerData['seller'];
                $eachSellerShipping = 0;
                if (!empty($shippingArray)) {
                    $eachSellerShipping = $shippingArray['price']/$sellerCount;
                }

                /**
                 * $itemsarray get item data for invoice.
                 *
                 * @var array
                 */
                $itemsarray['data'] = [];

                if (isset($sellerData['products']) && $sellerData['products']) {
                    $itemsarray = $this->_getItemQtys($order, explode(',', $sellerData['products']));
                }
                $invoice = $this->invoiceService->create()
                ->prepareInvoice(
                    $order,
                    $itemsarray['data']
                );
                if (count($itemsarray['data']) > 0) {
                    $invoice->setTransactionId($transactionId);
                    $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_ONLINE);
                    
                    $invoice->setShippingAmount((float) $sellerData['shippingprice'] + (float) $eachSellerShipping);
                    $invoice->setBaseShippingAmount((float) $sellerData['shippingprice'] + (float) $eachSellerShipping);
                    $invoice->setTaxAmount($sellerData['taxamount']);
                    $invoice->setBaseTaxAmount($sellerData['taxamount']);
                    $invoice->setSubtotal(
                        $this->_priceCurrency->round(
                            $sellerData['price'] - $sellerData['taxamount'] + (float) $eachSellerShipping
                        )
                    );
                    $invoice->setBaseSubtotal(
                        $this->_priceCurrency->round(
                            $sellerData['price'] - $sellerData['taxamount'] + (float) $eachSellerShipping
                        )
                    );
                    $price = $sellerData['price'];
                    if (isset($sellerData['discount'])) {
                        $price = $sellerData['price'] - $sellerData['discount'];
                    }
                    $invoice->setGrandTotal(
                        $this->_priceCurrency->round(
                            $price + (float) $eachSellerShipping
                        )
                    );
                    $invoice->setBaseGrandTotal(
                        $this->_priceCurrency->round(
                            $price + (float) $eachSellerShipping
                        )
                    );
                } else {
                    $invoice->setTransactionId($transactionId);
                    $invoice->setRequestedCaptureCase(\Magento\Sales\Model\Order\Invoice::CAPTURE_ONLINE);
                    $price = $sellerData['price'];
                    if (isset($sellerData['discount'])) {
                        $price = $sellerData['price'] - $sellerData['discount'];
                    }
                    $invoice->setSubtotal($sellerData['price'] - $order->getShippingTaxAmount());
                    $invoice->setTaxAmount($order->getShippingTaxAmount());
                    $invoice->setBaseTaxAmount($order->getShippingTaxAmount());
                    $invoice->setShippingAmount(0);
                    $invoice->setBaseShippingAmount(0);
                    $invoice->setGrandTotal($price);
                    $invoice->setBaseGrandTotal($price);
                }
                
                    $invoice->register();
                    $invoice->save();
                    $invoice->getOrder()->setIsInProcess(true);
                    $transactionSave = $this->transactions->create()
                    ->addObject($invoice)
                    ->addObject($invoice->getOrder());
                    $transactionSave->save();
                    $this->_invoiceSender->send($invoice);
                    //send notification code
                    $order->addStatusHistoryComment(
                        __(
                            'Notified customer about invoice #%1.',
                            $invoice->getId()
                        )
                    )
                    ->setIsCustomerNotified(true)
                    ->setState(\Magento\Sales\Model\Order::STATE_PROCESSING)
                    ->setStatus(\Magento\Sales\Model\Order::STATE_PROCESSING)
                    ->save();
                    $invoiceId = $invoice->getId();
                    return $invoice->getId();
            } catch (\Exception $e) {
                $this->_logger->critical($e);
                
                throw new \Magento\Framework\Exception\LocalizedException(
                    __("something went wrong while creating the order invoice -- ".$e->getMessage())
                );
            }
        } else {
            $this->_logger->critical("something went wrong not able to create seller wise invoice");
        }
    }

    /**
     * {@inheritdoc}
     */
    public function doTransaction($order, $sellerData)
    {
        try {
            $order = $this->validateOrder($order);
            if ($order === null) {
                return null;
            }
            $transactionId = $this->getTransactionId($order->getPayment(), $sellerData);
            $this->_logger->critical('debug transaction with id: '.$transactionId, $sellerData);
            $payment = $order->getPayment();
            //$lastTransactionId = $payment->getLastTransId();
            $payment->setLastTransId($transactionId);
            $payment->setTransactionId($transactionId);
            $formatedPrice = $order->getBaseCurrency()->formatTxt(
                $order->getGrandTotal()
            );

            $message = __('The authorized amount is %1.', $formatedPrice);

            $trans = $this->_transactionBuilder;
            $isClosed = 0;
            $transaction = $trans->setPayment($payment)
                ->setOrder($order)
                ->setTransactionId($transactionId)
                ->setAdditionalInformation(
                    [\Magento\Sales\Model\Order\Payment\Transaction::RAW_DETAILS => (array) $sellerData]
                )
                ->setFailSafe(true)
                ->build(\Magento\Sales\Model\Order\Payment\Transaction::TYPE_CAPTURE);
            $payment->addTransactionCommentsToOrder(
                $transaction,
                $message
            );
            $payment->save();
            $order->save();
            if ($transaction) {
                $transaction->setIsClosed($isClosed);
                return  $transaction->save()->getTransactionId();
            } else {
                return $transactionId;
            }
        } catch (\Exception $e) {
            $this->_logger->critical($e);

            throw new \Magento\Framework\Exception\LocalizedException(
                __("something went wrong while creating the transaction-- ".$e->getMessage())
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    public function createOrder($orderData)
    {
        $quote = $this->_checkoutSession->getQuote();
                
        if (!$this->getCustomerSession()->isLoggedIn()) {
            $this->_helper->setGuestQuote($quote);
        }
        $payment = $quote->getPayment();
        $additionalInfo = [];
        $additionalInfo['orderID'] = $orderData['id'];
        $additionalInfo['payerID'] = $orderData['payer']['payer_id'];
        $additionalInfo['quoteID'] = $quote->getId();
        $additionalInfo['paymentToken'] = '';
        $payment->setAdditionalInformation($additionalInfo);
        $quote->collectTotals();
        // Create Order From Quote
        $order = $this->quoteManagement->placeOrder($quote->getId());

        $this->_checkoutSession->setQuoteId(null);
        $quote->setIsActive(false);
        $this->quoteRepository->save($quote);
    }

    /**
     * Get customer session object
     *
     * @return \Magento\Customer\Model\Session
     */
    public function getCustomerSession()
    {
        return $this->_customerSession;
    }

    /**
     * check if invoice creation allowed
     *
     * @param Magento\Sales\Model\Order $order
     * @return bool
     */
    public function canInvoice($order)
    {
        return $order->canInvoice();
    }

    /**
     * check transaction status
     *
     * @param string $transactionId
     * @return bool
     */
    public function transactionStatus($transactionId)
    {
        try {
            $transaction = $this->adapter->findTransaction($transactionId);
            return $transaction->status;
        } catch (\Exception $e) {
            return false;
        }
    }
    /**
     * validate order
     *
     * @param Magento\Sales\Model\Order $order
     * @return Magento\Sales\Model\Order | NULL
     */
    public function validateOrder($order)
    {
        if ($order && $order->getId()) {
            return $order;
        }

        return null;
    }

    /**
     * get transaction id for seller from payment additional info
     *
     * @param Magento\Sales\Model\Order\Payment $payment
     * @param array $charge
     * @return void
     */
    public function getTransactionId($payment, $charge)
    {
        $additionalInfo = $payment->getAdditionalInformation();
        
        $transData = [];
        foreach ($additionalInfo as $key => $info) {
            $data = explode("sellerId_", $key);
            $this->_logger->critical('debug transaction with additionalInfo: '.json_encode($data));
            if (is_numeric($data[1])) {
                if ($charge['seller'] == $data[1]) {
                    return $info['payments']['captures'][0]['id'];
                }
            }
        }
    }

    /**
     * get seller id from transaction id
     *
     * @param string $transactionId
     * @return int
     */
    public function getSellerFromTransactionId($transactionId, $order)
    {
        if (is_numeric($order)) {
            $order = $this->_orderFactory->create()->load($order);
        }
        $additionalInfo = $order->getPayment()->getAdditionalInformation();
        $transData = [];
        foreach ($additionalInfo as $key => $info) {
            $data = explode("_seller_", $key);
            if (count($data) == 2) {
                if (is_numeric($data[1])) {
                    $transData[$data[1]][$data[0]] = $info;
                }
            }
        }
        
        foreach ($transData as $t) {
            if ($t['id'] == $transactionId) {
                return $t['sellerId'];
            }
        }
        return 0;
    }

    /**
     * get real transaction id generated by payment gateway
     * @param int $transactionId
     *
     * @return string
     */
    public function getRealTransactionId($transactionId)
    {
        return $this->_transactionFactory->create()->load($transactionId)->getTxnId();
    }

    /**
     * get all transactions by order id
     * @param int $orderId
     *
     * @return string
     */
    public function getAllTransactionsOfOrder($orderId)
    {
        $transactions = [];
        $tranCollection = $this->_transactionFactory->create()->getCollection()
                                ->addFieldToFilter("order_id", ['eq' => $orderId]);

        if ($tranCollection->getSize() > 0) {
            foreach ($tranCollection as $tc) {
                $transactions[] = $tc->getTxnId();
            }
        }
        return $transactions;
    }

    /**
     * get all marketplace hold transactions
     *
     * @return Magento\Sales\Model\ResouceModel\Order\Payment\Collection
     */
    public function getTransactionList()
    {
        return $this->_transactionFactory->create()->getCollection()
        ->addFieldToFilter("is_closed", ['eq' => 0]);
    }

    /**
     * get transaction object
     *
     * @param string $txnId
     *
     * @return Magento\Sales\Model\Order\Payment\Transaction | bool
     */
    public function getTransactionByTxnId($txnId)
    {
        $collection = $this->_transactionFactory->create()->getCollection()
        ->addFieldToFilter("txn_id", ['eq' => $txnId]);
        if ($collection->getSize() > 0) {
            return $collection->getLastItem();
        } else {
            return false;
        }
    }

        /**
         * get cart data from payment additional info
         *
         * @param Magento\Sales\Model\Order\Payment $payment
         * @return array
         */
    public function getCartData($payment)
    {
        $additionalInfo = $payment->getAdditionalInformation();
        $cartData = [];
        foreach ($additionalInfo as $key => $info) {
            $data = explode("sellerId_", $key);
            if (count($data) == 2) {
                if (is_numeric($data[1])) {
                    $cartData[$data[1]] = $info;
                }
            }
        }

        return $cartData;
    }

    /**
     * getIfSellerInCart function to check if seller present in the cart.
     *
     * @param array $paymentDetails
     *
     * @return bool true | false
     */
    public function getIfSellerInCart($paymentDetails = [])
    {
        foreach ($paymentDetails as $sellerData) {
            if ($sellerData['seller']) {
                return true;
            }
        }

        return false;
    }

    /**
     * Get Order item array data to create invoice.
     *
     * @param $order
     * @param $items
     *
     * @return array
     */
    protected function _getItemQtys($order, $items)
    {
        $data = [];
        $subtotal = 0;
        $baseSubtotal = 0;
        foreach ($order->getAllItems() as $item) {
            if (in_array($item->getProductId(), $items)) {
                $data[$item->getItemId()] = (float)(
                    $item->getQtyOrdered() - $item->getQtyInvoiced()
                );

                $_item = $item;

                // for bundle product
                $bundleitems = $this->mergeData(
                    $_item
                );

                if ($_item->getParentItem()) {
                    continue;
                }

                if ($_item->getProductType() == 'bundle') {
                    foreach ($bundleitems as $_bundleitem) {
                        if ($_bundleitem->getParentItem()) {
                            $data[$_bundleitem->getItemId()] = (float)(
                                $_bundleitem->getQtyOrdered() - $item->getQtyInvoiced()
                            );
                        }
                    }
                }
                $subtotal += $_item->getRowTotal();
                $baseSubtotal += $_item->getBaseRowTotal();
            } else {
                if (!$item->getParentItemId()) {
                    $data[$item->getItemId()] = 0;
                }
            }
        }

        return [
            'data' => $data,
            'subtotal' => $subtotal,
            'baseSubtotal' => $baseSubtotal,
        ];
    }

    /**
     * pay amount to seller
     *
     * @param Magento\Sales\Model\Order $order
     * @param int $sellerId
     * @param string $transactionId
     * @return void
     */
    public function payToSeller($order, $sellerId, $transactionId)
    {
        if (is_numeric($order)) {
            $order = $this->_orderFactory->create()->load($order);
        }
        $transactionData = $this->transactionData->create();
        $payPalOrder = $transactionData->getCollection()
            ->addFieldToFilter('order_id', $order->getIncrementId())
            ->addFieldToFilter('seller_id', $sellerId)
            ->getFirstItem();
        $payPalOrder->setPaymentStatus(1)->save();
        $collection = $this->saleslistFactory->create()
            ->getCollection()
            ->addFieldToFilter(
                'seller_id',
                $sellerId
            )
            ->addFieldToFilter(
                'order_id',
                $order->getId()
            )
            ->addFieldToFilter(
                'paid_status',
                0
            );
        foreach ($collection as $item) {
            $item->setCpprostatus(1)->save();
        }
        $this->marketplaceOrders->paysellerpayment($order, $sellerId, $transactionId);
    }

    public function mergeData($_item)
    {
        return array_merge(
            [$_item],
            $_item->getChildrenItems()
        );
    }
}
