<?php

namespace Leadlion\AutogedalOrderFlow\Cron;

use Magento\Sales\Model\ResourceModel\Order\CollectionFactory;
use Magento\Store\Model\App\Emulation;
use Psr\Log\LoggerInterface;
use SmartBill\Integration\Helper\Data;
use SmartBill\Integration\Helper\DataEmail;
use SmartBill\Integration\Model\InvoiceFactory;
use SmartBill\Integration\Model\ResourceModel\Invoice\CollectionFactory as InvoiceCollectionFactory;
use SmartBill\Integration\Helper\Settings;
use Magento\Sales\Model\Order;
use Leadlion\AutogedalOrderFlow\Helper\Data as OrderFlowData;
use Magento\Sales\Api\OrderRepositoryInterface;
use Leadlion\AutogedalWarehouses\Model\ResourceModel\Warehouse\CollectionFactory as WarehouseCollection;
use Monolog\Logger as MonoLogger;
use Monolog\Handler\StreamHandler;
use Monolog\Level;

class GenerateInvoices
{
    protected $helper;
    protected $emailHelper;
    protected $invoiceCollectionFactory;
    protected $invoiceFactory;
    protected $orderCollectionFactory;
    protected $orderFlowHelper;
    protected $orderRepository;
    protected $warehouseCollection;
    /**
     * @var Emulation
     */
    protected $appEmulation;

    /**
     * @var LoggerInterface
     */
    private $logger;

    private CollectionFactory $_orderCollectionFactory;

    public function __construct(
        Data $helper,
        DataEmail $emailHelper,
        InvoiceCollectionFactory $invoiceCollectionFactory,
        CollectionFactory $orderCollectionFactory,
        InvoiceFactory $invoiceFactory,
        OrderFlowData $orderFlowHelper,
        OrderRepositoryInterface $orderRepository,
        WarehouseCollection $warehouseCollection,
        Emulation $appEmulation,
        LoggerInterface $logger
    ) {
        $this->helper = $helper;
        $this->emailHelper = $emailHelper;
        $this->invoiceFactory = $invoiceFactory;
        $this->invoiceCollectionFactory = $invoiceCollectionFactory;
        $this->_orderCollectionFactory = $orderCollectionFactory;
        $this->orderFlowHelper = $orderFlowHelper;
        $this->orderRepository = $orderRepository;
        $this->warehouseCollection = $warehouseCollection;
        $this->appEmulation = $appEmulation;
    }

    public function execute()
    {
        $monolog = new MonoLogger('invoices');
        $monolog->pushHandler(new StreamHandler(BP . '/var/log/invoices.log', Level::Debug));
        $logger = $monolog;

        $batchSize = (int)$this->helper->getSettings()->getSettingsValue(Settings::BATCH_SIZE);
        $batchSize = $batchSize !== 0 ? $batchSize : 10;
        $failedInvoiceOrderStatus = $this->helper->getSettings()->getSettingsValue(Settings::FAILED_INVOICE_STATUS_ORDER);
        $failedInvoiceOrderStatus = $failedInvoiceOrderStatus ?? 'lipsastoc';
        $testMode = $this->helper->getSettings()->getSettingsValue(Settings::TEST_MODE_KEY);
        $testModeOrderIds = $this->helper->getSettings()->getSettingsValue(Settings::TEST_MODE_ORDER_IDS);
        $testModeDisabledForOrderIds = $this->helper->getSettings()->getSettingsValue(Settings::TEST_MODE_DISABLED_FOR_ORDER_IDS);

        if (!empty($testModeOrderIds)) {
            $testModeOrderIds = array_map( static function($orderid) {return trim($orderid);}, explode(',', $testModeOrderIds));
        } else {
            $testModeOrderIds = ['NO_INCREMENT_IDS']; // added this to avoid collection failure if test mode orders ids is not configured.
        }

        if (!empty($testModeDisabledForOrderIds)) {
            $testModeDisabledForOrderIds = array_map( static function($orderid) {return trim($orderid);}, explode(',', $testModeDisabledForOrderIds));
        }

        $return = []; $smartbillError = false;
        if ($testMode && empty($testModeDisabledForOrderIds)) {
            $orderCollection = $this->_orderCollectionFactory->create()
                ->addAttributeToSelect("*")
                ->addFieldToFilter('increment_id', ['in' => $testModeOrderIds])
                ->addFieldToFilter('status', $this->orderFlowHelper->getAwbStatusCode())
                ->setPageSize($batchSize);
        } else if ($testMode && !empty($testModeDisabledForOrderIds) && count($testModeDisabledForOrderIds)) {
            $orderCollection = $this->_orderCollectionFactory->create()
                ->addAttributeToSelect("*")
                ->addFieldToFilter('increment_id', ['in' => $testModeDisabledForOrderIds])
                ->addFieldToFilter('status', $this->orderFlowHelper->getAwbStatusCode())
                ->setPageSize($batchSize);
        } else {
            $orderCollection = $this->_orderCollectionFactory->create()
                ->addAttributeToSelect("*")
                ->addFieldToFilter('status', $this->orderFlowHelper->getAwbStatusCode())
                ->setPageSize($batchSize);
        }

        foreach ($orderCollection as $order) {
            $orderId = $order->getEntityId();
            $logger->info('$orderId'.$orderId);
            try{
                $order = $this->orderRepository->get($orderId);
                $this->appEmulation->startEnvironmentEmulation(
                    $order->getStoreId(),
                    \Magento\Framework\App\Area::AREA_FRONTEND,
                    true
                );

                $settingsData = $this->helper->getSettings()->buildSettingsData();
                $invoiceCollectionFactoryResults = $this->invoiceCollectionFactory->create()->getItemByColumnValue('order_id', $orderId);
                if ($invoiceCollectionFactoryResults != null && $invoiceCollectionFactoryResults->getData('order_id') == $orderId){
                    $invoiceFactory = $invoiceCollectionFactoryResults;
                }
                else {
                    $invoiceFactory = $this->invoiceFactory->create();
                }
                $smartbillInvoice = $this->helper->buildSmartBillDocumentFromOrder( $orderId);
                $allocatedWarehouse = $order->getData('autogedal_allocated_warehouse');
                if (!empty($allocatedWarehouse)) {
                    $smartbillInvoice['seriesName'] = $this->getWarehouseSeriesNumber($allocatedWarehouse);
                    $smartbillInvoice['useStock'] = $this->getWarehouseAutomaticStockManagement($allocatedWarehouse);
                }
                if ($testMode) {
                    $smartbillInvoice['seriesName'] = 'TEST';
                    unset($smartbillInvoice['useStock']);
                }
                $logger->info(print_r($smartbillInvoice,true));
                /*
                if (isset($smartbillInvoice['useStock']) && $smartbillInvoice['useStock']) {
                    foreach ($smartbillInvoice['products'] as &$product) {

                        $product['warehouseName'] = $settingsData['INVOICE_WHICH_STOCK_SETTINGS_KEY'];
                    }
                } else {
                    foreach ($smartbillInvoice['products'] as &$product) {
                        $product['warehouseName'] = "";
                    }
                }
*/
                /** @var \SmartBill\Integration\SmartBillCloudRestClient $connector */
                $connector = $this->helper->getSmartBillConnector();

                $connector->setMagentoOrderId($orderId);
                $connector->setDataLogger($invoiceFactory);

                $documentType = $this->helper->getSettings()->getSettingsValue(Settings::INVOICE_TYPE_KEY);

                if (!$testMode) {
                    if ($documentType == Settings::SMARTBILL_INVOICE_TYPE) {
                        $serverCall = $connector->createInvoiceWithDocumentAddress($smartbillInvoice);
                    } else {
                        $serverCall = $connector->createProformaWithDocumentAddress($smartbillInvoice);
                    }
                } elseif (!empty($testModeDisabledForOrderIds) && count($testModeDisabledForOrderIds) && $testMode) {
                    $serverCall = $connector->createProformaDraft($smartbillInvoice);
                }

                if (!empty($serverCall) && $serverCall['errorText'] && (!$testMode || ($testMode && !empty($testModeDisabledForOrderIds) && count($testModeDisabledForOrderIds)))){
                    $return['status'] = false;
                    $return['message'] = $serverCall['message'];
                    $return['error'] = $serverCall['errorText'];
                    $order->addStatusToHistory($order->getStatus(), 'Smartbill error: '. $return['error'].' '. $serverCall['message']);
                    $this->orderRepository->save($order);
                } else {
                    if (!$testMode || ($testMode && !empty($testModeDisabledForOrderIds) && count($testModeDisabledForOrderIds))) {
                        $return['status'] = true;
                        if (isset($serverCall['number']) && ($serverCall['number'])) {
                            $return['message'] = 'Factura a fost emisa cu succes: ' . $serverCall['message'] . $serverCall['series'] . ' ' . $serverCall['number'] . '.';
                            $invoiceFactory->setData('smartbill_invoice_id', $serverCall['number'])
                                ->setData('smartbill_series', $serverCall['series'])
                                ->setData('smartbill_document_url', $serverCall['documentUrl'] ?? '')
                                ->setData('settings_data', json_encode($settingsData))
                                ->setData('smartbill_status', Settings::SMARTBILL_DATABASE_INVOICE_STATUS_FINAL)
                                ->save();
                            $order->setState(Order::STATE_PROCESSING)
                                ->setStatus($this->orderFlowHelper->getInvoicedStatusCode())
                                ->addStatusToHistory($order->getStatus(), 'Factura a fost emisa cu succes: ' . $serverCall['message'] . ' ' . $serverCall['series'] . ' ' . $serverCall['number']);
                            $logger->info($serverCall['number']);
                            $logger->info($serverCall['series']);
                            $this->orderRepository->save($order);
                        } else {
                            if (isset($serverCall['series'])) {
                                $invoiceFactory->setData('smartbill_series', $serverCall['series']);
                            }
                            $invoiceFactory->setData('settings_data', json_encode($settingsData));
                            $invoiceFactory->setData('smartbill_document_url', $serverCall['documentUrl'] ?? '');
                            $invoiceFactory->setData('smartbill_status', Settings::SMARTBILL_DATABASE_INVOICE_STATUS_DRAFT)->save();
                            $return['message'] = 'Operatiunea s-a desfasurat cu succes:  ' . $serverCall['message'];

                            $order->addStatusToHistory($order->getStatus(), 'Operatiunea s-a desfasurat cu succes: ' . $serverCall['message'] . ' ' . $serverCall['series']);
                            $this->orderRepository->save($order);
                        }

                        if ($testMode && !empty($testModeDisabledForOrderIds) && count($testModeDisabledForOrderIds)) {
                            // don't try to generate pdf for "proforma ciorna"
                        } else {
                            $return['number'] = $serverCall['number'];
                            $return['series'] = $serverCall['series'];
                            $smartbillNumber = $this->orderFlowHelper->getSmartbillPdfSeries();
                            // Get the PDF invoice
                            $pdfInvoiceResponse = $connector->PDFInvoice($smartbillNumber, $serverCall['series'], $serverCall['number']);
                            if (!empty($pdfInvoiceResponse)) {
                                $pdfFileName = $this->helper->generateInvoicePDFFilename($serverCall['series'], $serverCall['number'], $orderId);
                                $invoiceUrl = $this->helper->generateInvoiceUrl($serverCall['series']);

                                // Save the invoice to the server
                                $savedDocumentOnServerFlag = $this->helper->saveInvoiceFileOnServer($serverCall['series'], $serverCall['number'], $orderId, $pdfInvoiceResponse, $invoiceUrl);
                                if ($savedDocumentOnServerFlag) {
                                    // Save the invoice url to the database
                                    $invoiceFactory->setSmartbillDocumentUrl($savedDocumentOnServerFlag)
                                        ->save();

                                    //$order->addStatusToHistory($order->getStatus(), 'PDF invoice was generated successfully: '. $serverCall['series'] . ' ' . $serverCall['number']);
                                    $this->orderRepository->save($order);
                                }
                            } else {
                                $smartbillError = true;
                                $errorMessage = "NO PDF invoice was generated. Check in SmartBill Cloud for the document " . $serverCall['series'] . $serverCall['number'];
                                $logger->info($errorMessage);
                                $this->orderRepository->save($order);
                            }
                        }
                    } elseif ($testMode && empty($testModeDisabledForOrderIds)) {
                        $request = json_encode($smartbillInvoice);
                        $order->addStatusToHistory($order->getStatus(), "Request data to be sent to SmartBill:" . $request);
                        $this->orderRepository->save($order);
                    }
                }
            }
            catch(\Exception $e){
                $logger->info($e->getTraceAsString());
                $logger->info($e->getMessage());
                if (!$testMode) {
                    $smartbillError = true;
                    if (isset($invoiceFactory)) {
                        $invoiceFactory->setData('order_id', $orderId)
                            ->setData('settings_data', json_encode($settingsData))
                            ->setData('smartbill_status', Settings::SMARTBILL_DATABASE_INVOICE_STATUS_DRAFT)
                            ->save();
                    }
                    $order->setState(Order::STATE_PROCESSING)
                        ->setStatus($failedInvoiceOrderStatus);
                    $order->addStatusToHistory($order->getStatus(), $e->getMessage());
                    try {
                        $this->orderRepository->save($order);
                    } catch (\Exception $e) {
                        $logger->info($e->getMessage());
                    }
                } elseif (!empty($testModeDisabledForOrderIds) && count($testModeDisabledForOrderIds)) {
                    $order->setState(Order::STATE_PROCESSING)
                        ->setStatus($failedInvoiceOrderStatus);
                    $order->addStatusToHistory($order->getStatus(), $e->getMessage());
                    try {
                        $this->orderRepository->save($order);
                    } catch (\Exception $e) {
                        $logger->info($e->getMessage());
                    }
                }
            }
            if ($smartbillError) {
                continue;
            }
            $this->appEmulation->stopEnvironmentEmulation();
        }
    }

    public function getWarehouseSeriesNumber($warehouse)
    {
        $warehouseCollection = $this->warehouseCollection->create()
            ->addFieldToFilter('warehouse_id', $warehouse);
        return $warehouseCollection->getFirstItem()->getData('smartbill_invoice_series');
    }

    /**
     * Get the allocated warehouse for a specific order
     * @param $order
     * @return mixed
     */
    private function getWarehouseAutomaticStockManagement($allocatedWarehouse) {
        try {
            $warehouseCollection = $this->warehouseCollection->create()
                ->addFieldToFilter('warehouse_id', $allocatedWarehouse);
            return $warehouseCollection->getFirstItem()->getData('automatic_stock_management');
        } catch (\Exception $e) {
            return null;
        }
    }
}
