<?php
/**
 * Copyright © 2016 Filoblu S.r.l. All rights reserved.
 */

namespace FiloBlu\Esb\Model\Rest;

use FiloBlu\Esb\Api\Data\AcknowledgeInterface;
use FiloBlu\Esb\Api\ExternalInvoiceInterface;
use FiloBlu\Esb\Helper\LoggerProvider;
use FiloBlu\OrderAdditionalData\Model\AdditionalDataFactory;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\File\Write;
use Magento\Framework\Filesystem\File\WriteInterface;
use Magento\Framework\Phrase;
use Magento\Sales\Api\Data\InvoiceInterface;
use Magento\Sales\Api\Data\OrderExtensionFactory;
use Magento\Sales\Exception\CouldNotInvoiceException;
use Magento\Sales\Model\InvoiceOrder;
use Magento\Sales\Model\OrderRepository;
use Magento\Sales\Model\Service\InvoiceService;
use Magento\Sales\Api\Data\InvoiceItemCreationInterfaceFactory;
use Monolog\Logger;
use \Magento\Framework\DB\TransactionFactory;

if (!defined('DS')):
    define('DS', DIRECTORY_SEPARATOR);
endif;

/**
 * REST Api to save external invoices
 */
class ExternalInvoice implements ExternalInvoiceInterface
{

    /**
     * @var OrderExtensionFactory
     */
    protected $_orderRepository;

    /**
     * @var SearchCriteriaBuilder
     */
    protected $_searchCriteriaBuilder;

    /**
     * @var ScopeConfigInterface
     */
    protected $_scopeConfig;

    /**
     * @var OrderExtensionFactory
     */
    protected $_orderExtensionFactory;

    /**
     * @var InvoiceService
     */
    protected $_invoiceService;

    /**
     * @var Filesystem
     */
    protected $_filesystem;

    /**
     * @var Logger
     */
    protected $_logger;

    /**
     * @var AcknowledgeInterface
     */
    protected $_response;

    /**
     * @var AdditionalDataFactory
     */
    protected $_additionalDataFactory;

    /**
     * @var TransactionFactory
     */
    protected $_transactionFactory;

    /**
     * Init plugin
     *
     * @param ScopeConfigInterface $scopeConfig
     * @param OrderRepository $orderRepository
     * @param OrderExtensionFactory $orderExtensionFactory
     * @param InvoiceOrder $invoiceOrder
     * @param InvoiceService $invoiceService
     * @param Filesystem $filesystem
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param LoggerProvider $loggerProvider
     * @param AcknowledgeInterface $response
     * @param AdditionalDataFactory $additionalDataFactory
     */
    public function __construct(
        ScopeConfigInterface $scopeConfig,
        OrderRepository $orderRepository,
        OrderExtensionFactory $orderExtensionFactory,
        Filesystem $filesystem,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        LoggerProvider $loggerProvider,
        AcknowledgeInterface $response,
        AdditionalDataFactory $additionalDataFactory,
        InvoiceService $invoiceService,  
        TransactionFactory $transactionFactory
    )
    {
        $this->_scopeConfig = $scopeConfig;
        $this->_orderRepository = $orderRepository;
        $this->_orderExtensionFactory = $orderExtensionFactory;
        $this->_filesystem = $filesystem;
        $this->_searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->_logger = $loggerProvider->getLogger();
        $this->_response = $response;
        $this->_additionalDataFactory = $additionalDataFactory;
        $this->_invoiceService = $invoiceService;        
        $this->_transactionFactory=$transactionFactory;
    }

    /**
     * @param InvoiceInterface $invoice_data
     * @return AcknowledgeInterface
     * @throws CouldNotInvoiceException
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    public function save(InvoiceInterface $invoice_data)
    {
        $invoiceExtensionAttributes = $invoice_data->getExtensionAttributes();

        if (null == $invoiceExtensionAttributes) {
            $message = __("Cannot find order to save external document: missing extension attributes");
            $this->_logger->critical($message->render());
            throw new CouldNotInvoiceException(new Phrase($message->render()));
        }
        if (!$invoiceExtensionAttributes->getOrderIncrementId()) {
            $message = __("Cannot find order to save external document: missing order increment id");
            $this->_logger->critical($message->render());
            throw new CouldNotInvoiceException(new Phrase($message->render()));
        }
        if (!$invoiceExtensionAttributes->getDocumentType()) {
            $message = __("Cannot find order to save external document: missing document type");
            $this->_logger->critical($message->render());
            throw new CouldNotInvoiceException(new Phrase($message->render()));
        }
        if (null == $invoice_data->getIncrementId()) {
            $message = __("Cannot find order to save external document: missing document increment id");
            $this->_logger->critical($message->render());
            throw new CouldNotInvoiceException(new Phrase($message->render()));
        }
        $orderIncrementId = $invoiceExtensionAttributes->getOrderIncrementId();
        $invoiceIncrementId = $invoice_data->getIncrementId();
        $documentType = $invoiceExtensionAttributes->getDocumentType();
        $filename = "order_" . $orderIncrementId . "_" . $documentType . "_" . $invoiceIncrementId . ".pdf";
        $filename = preg_replace("/[^0-9A-Za-z.]+/", "_", $filename);
        $documentFolder = $this->_scopeConfig->getValue(
            'esb/print_documents/document_folder'
        );
        $writer = $this->_filesystem->getDirectoryWrite(DirectoryList::VAR_DIR);
        /** @var WriteInterface|Write $file */

        $file = $writer->openFile($documentFolder . DS . $filename, 'w');
        try {
            $file->lock();
            try {
                $file->write(base64_decode($invoiceExtensionAttributes->getDocumentPdf()));
            } finally {
                $file->unlock();
            }
        } finally {
            $file->close();
        }

        $invoice = array(
            "created_at" => $invoice_data->getCreatedAt(),
            "increment_id" => $invoice_data->getIncrementId(),
            "document_type" => $invoiceExtensionAttributes->getDocumentType(),
            "grand_total" => $invoice_data->getGrandTotal(),
            "document_pdf" => $filename
        );       
        


        /*if (count($orderList) <> 1) {
            $message = __("Cannot find order %1", $invoiceExtensionAttributes->getOrderIncrementId());
            $this->_logger->critical($message->render());
            throw new CouldNotInvoiceException(new Phrase($message->render()));
        }*/
        $create_invoice = $this->_scopeConfig->getValue(
            'esb/external_invoice/create_invoice'
        );
        try {

            //Sometimes the orderList has duplicates. Use raw loadByIncrementId in that case.
            try{
                $searchCriteria = $this->_searchCriteriaBuilder
                ->addFilter('increment_id', $invoiceExtensionAttributes->getOrderIncrementId())->create();
                $orderList = $this->_orderRepository->getList($searchCriteria);
                $items = $orderList->getItems();
                $order = array_shift($items);
            }
            catch(\Exception $e){
                $this->_logger->info($e->getMessage());
                $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
                $order = $objectManager->create('Magento\Sales\Model\Order')->loadByIncrementId($orderIncrementId);
            }

            if($create_invoice==1){

                if (!$order->canInvoice()) {
                    $message = __('Cannot create invoice for order %1', $invoiceExtensionAttributes->getOrderIncrementId());
                    $this->_logger->critical($message->render());
                    throw new CouldNotInvoiceException(new Phrase($message->render()));
                }
                $qtys = [];
                $invoice_items = $invoice_data['items'];
                foreach ($order->getItems() as $order_item) {
                    foreach ($invoice_items ?? [] as $key => $invoice_item) {
                        if ($order_item->getTypeId() != "configurable" && $order_item->getTypeId() != "bundle" && $invoice_item['sku'] === $order_item->getSku()) {
                            $qtys[$order_item->getId()] = $invoice_item['qty'];
                            break;
                            //unset($invoice_items[$key]);
                        }
                    }
                }

                $invoice_object = $this->_invoiceService->prepareInvoice($order, $qtys);
                if (!$invoice_object) {
                    throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t save the invoice right now.'));
                }
                if (!$invoice_object->getTotalQty()) {
                    throw new \Magento\Framework\Exception\LocalizedException(
                        __('You can\'t create an invoice without products.')
                    );
                }
                $shipping_amount = $invoice_data->getShippingInclTax();
                if (abs($shipping_amount - $order->getShippingInclTax()) < 0.5) {
                    $shipping_amount = $order->getShippingInclTax();
                }
                $invoice_object->setShippingInclTax($shipping_amount);
                $capture_case = $invoiceExtensionAttributes->getCaptureCase();
                if (!$capture_case) {
                    $capture_case=\Magento\Sales\Model\Order\Invoice::CAPTURE_OFFLINE;
                }
                $this->_logger->info("CAPTURE CASE FOR ORDER $orderIncrementId: $capture_case");
                $invoice_object->setRequestedCaptureCase($capture_case);
                $invoice_object->register();
                $invoice_object->getOrder()->setCustomerNoteNotify(false);
                $invoice_object->getOrder()->setIsInProcess(true);
                //$order->addStatusHistoryComment('Automatically INVOICED', false);
                $transactionSave = $this->_transactionFactory->create()->addObject($invoice_object)->addObject($invoice_object->getOrder());
                $transactionSave->save();

            }
            //$order = $orderList->getFirstItem();
            $original_status = $order->getStatus();
            $orderExtensionAttributes = $order->getExtensionAttributes();
            $additionalData = array("external_invoices" => array($invoice_data->getIncrementId() => $invoice));
            $additionalDataObject=null;
            if ($orderExtensionAttributes) {
                if ($orderExtensionAttributes->getFbOad()) {
                    $additionalDataObject = $orderExtensionAttributes->getFbOad();
                    $additionalData = array();
                    foreach ($additionalDataObject->getCustomAttributes() as $customAttribute) {
                        $additionalData[$customAttribute->getAttributeCode()] = $customAttribute->getValue();
                    }
                    if (array_key_exists("external_invoices", $additionalData)) {
                        $additionalData["external_invoices"][$invoice_data->getIncrementId()] = $invoice;
                    } else {
                        $additionalData["external_invoices"] = array($invoice_data->getIncrementId() => $invoice);
                    }
                }else{
                    $additionalDataObject = $this->_additionalDataFactory->create();
                }
            } else {
                $orderExtensionAttributes = $this->_orderExtensionFactory->create();
                $additionalDataObject = $this->_additionalDataFactory->create();
            }
            foreach ($additionalData as $field => $value) {
                $additionalDataObject->setCustomAttribute($field, $value);
            }
            $orderExtensionAttributes->setFbOad($additionalDataObject);
            $order->setExtensionAttributes($orderExtensionAttributes);
            $order->addStatusHistoryComment(__("External document has been saved: %1 %2", $documentType, $invoiceIncrementId), "invoiced");
            if ($original_status == "sent_to_logistics") {
                $order->addStatusHistoryComment(__("Order has been sent to logistics"), "sent_to_logistics");
            }
            if(($order->getState() == "processing" || $order->getState() == "complete") && !$order->canInvoice() && !$order->canShip()) {
                $order->setState("complete");
                $order->addStatusHistoryComment("", "complete");
            }
            $this->_orderRepository->save($order);
            $this->_response->setStatus("success");
            $this->_response->setMessage(__("External document has been saved"));
            $this->_response->setFlow("create_document");
            $this->_response->setOrderIncrementId($order->getIncrementId());
            return $this->_response;
        } catch (\Exception $e) {
            $this->_logger->critical($e->getMessage());
            throw $e;
        }
    }
}

