<?php

namespace Leadlion\AutogedalOrderFlow\Cron;

use Leadlion\Web2Sms\Helper\Data;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\ResourceModel\Order\CollectionFactory;
use Eadesigndev\Awb\Model\Sources\CarrierType;
use Eadesigndev\Urgent\Model\GenerateAwb as GenerateAwbUrgent;
use Eadesigndev\FanCourier\Model\GenerateAwb as GenerateAwbFan;
use Eadesigndev\DPD\Model\GenerateAwb as GenerateAwbDPD;
use Eadesigndev\GLS\Model\GenerateAwb as GenerateAwbGLS;
use Eadesigndev\DragonStar\Model\GenerateAwb as GenerateAwbDragonStar;
use Eadesigndev\NemoExpress\Model\GenerateAwb as GenerateAwbNemoExpress;
use Eadesigndev\Sameday\Model\GenerateAwb as GenerateAwbSameday;
use Leadlion\AutogedalOrderFlow\Model\Packeta\GenerateAwb as GenerateAwbPacketa;
use Eadesigndev\Awb\Model\AwbRepository;
use Eadesigndev\Awb\Model\AwbFactory;
use Magento\Framework\App\Request\DataPersistorInterface;
use Eadesigndev\Urgent\Model\ShipmentData;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Message\ManagerInterface;
use Eadesigndev\Urgent\Model\Sources\PickupId as UrgentPickupId;
use Eadesigndev\FanCourier\Model\Sources\PickupId as FanCourierPickupId;
use Eadesigndev\DPD\Model\Sources\PickupId as DPDPickupId;
use Eadesigndev\GLS\Model\Sources\PickupId as GLSPickupId;
use Eadesigndev\DragonStar\Model\Sources\PickupId as DragonStarPickupId;
use Eadesigndev\NemoExpress\Model\Sources\PickupId as NemoExpressPickupId;
use Eadesigndev\Sameday\Model\Sources\PickupId as SamedayPickupId;
use Eadesigndev\Awb\Model\ResourceModel\Collection\Collection;
use Magento\Sales\Model\Order as OrderModel;
use Magento\Sales\Api\ShipmentRepositoryInterface;
use Magento\Sales\Api\Data\ShipmentTrackInterfaceFactory;
use Magento\Sales\Model\Order\ShipmentFactory;
use Leadlion\AutogedalOrderFlow\Helper\Data as OrderFlowData;
use Magento\Sales\Model\Order\Status\HistoryFactory;
use Monolog\Logger as MonoLogger;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Magento\Directory\Model\RegionFactory;
use Eadesigndev\GLS\Helper\PostalCodeLookup;

/**
 * Cron to create shipment based on custom ERP Orders with custom tracking number
 */
class GetAwb
{
    const AWB_NUMBER_FIELD = "awb_number";
    const PICKUP_NAME_FIELD = "pickup_name";
    const PICKUP_ID_FIELD = "pickup_id";
    const CARRIER_ID_FIELD = "carrier_id";
    const CARRIER_NAME_FIELD = "carrier_name";
    const STREET_TYPE = "bul.";
    const STREET_NO = "3";
    const DPD_PAYMENT = "SENDER";
    const DPD_CONTENT = "1x,";
    const PENDING_STATUS = "pending";
    const NEMOEXPRESS_PAYMENT = "client";

    private Collection $_awbCollection;
    private $logger;

    /**
     * @var GenerateAwbPacketa|GenerateAwbSameday|GenerateAwbFan|GenerateAwbUrgent|GenerateAwbGLS|GenerateAwbDPD|GenerateAwbDragonStar|GenerateAwbNemoExpress
     */
    private GenerateAwbPacketa|GenerateAwbSameday|GenerateAwbFan|GenerateAwbUrgent|GenerateAwbGLS|GenerateAwbDPD|GenerateAwbDragonStar|GenerateAwbNemoExpress $generateAwb;

    /**
     * @param OrderRepositoryInterface $orderRepository
     * @param CollectionFactory $orderCollectionFactory
     * @param DataPersistorInterface $dataPersistor
     * @param AwbRepository $awbRepository
     * @param AwbFactory $awbFactory
     * @param GenerateAwbUrgent $generateAwbUrgent
     * @param GenerateAwbFan $generateAwbFanCourier
     * @param GenerateAwbDPD $generateAwbDPD
     * @param GenerateAwbGLS $generateAwbGLS
     * @param GenerateAwbDragonStar $generateAwbDragonStar
     * @param GenerateAwbNemoExpress $generateAwbNemoExpress
     * @param GenerateAwbSameday $generateAwbSameday
     * @param GenerateAwbPacketa $generateAwbPacketa
     * @param ShipmentData $shipmentData
     * @param ScopeConfigInterface $scopeConfig
     * @param ManagerInterface $messageManager
     * @param UrgentPickupId $urgentPickupId
     * @param FanCourierPickupId $fancourierPickupId
     * @param DPDPickupId $dpdPickupId
     * @param GLSPickupId $glsPickupId
     * @param DragonStarPickupId $dragonstarPickupId
     * @param NemoExpressPickupId $nemoexpressPickupId
     * @param SamedayPickupId $samedayPickupId
     * @param Collection $awbCollection
     * @param CarrierType $awbCarrierType
     * @param ShipmentRepositoryInterface $shipmentRepository
     * @param ShipmentTrackInterfaceFactory $trackFactory
     * @param ShipmentFactory $shipmentFactory
     * @param Data $helper
     * @param OrderFlowData $orderFlowHelper
     * @param HistoryFactory $orderHistoryFactory
     * @param RegionFactory $regionFactory
     * @param PostalCodeLookup $glsPostalCodeLookup
     */
    public function __construct(
        private OrderRepositoryInterface $orderRepository,
        private CollectionFactory $orderCollectionFactory,
        private DataPersistorInterface $dataPersistor,
        private AwbRepository $awbRepository,
        private AwbFactory $awbFactory,
        private GenerateAwbUrgent $generateAwbUrgent,
        private GenerateAwbFan $generateAwbFanCourier,
        private GenerateAwbDPD $generateAwbDPD,
        private GenerateAwbGLS $generateAwbGLS,
        private GenerateAwbDragonStar $generateAwbDragonStar,
        private GenerateAwbNemoExpress $generateAwbNemoExpress,
        private GenerateAwbSameday $generateAwbSameday,
        private GenerateAwbPacketa $generateAwbPacketa,
        private ShipmentData $shipmentData,
        private ScopeConfigInterface $scopeConfig,
        private ManagerInterface $messageManager,
        private UrgentPickupId $urgentPickupId,
        private FanCourierPickupId $fancourierPickupId,
        private DPDPickupId $dpdPickupId,
        private GLSPickupId $glsPickupId,
        private DragonStarPickupId $dragonstarPickupId,
        private NemoExpressPickupId $nemoexpressPickupId,
        private SamedayPickupId $samedayPickupId,
        private Collection $awbCollection,
        private CarrierType $awbCarrierType,
        private ShipmentRepositoryInterface $shipmentRepository,
        private ShipmentTrackInterfaceFactory $trackFactory,
        private ShipmentFactory $shipmentFactory,
        private Data $helper,
        private OrderFlowData $orderFlowHelper,
        private HistoryFactory $orderHistoryFactory,
        private RegionFactory $regionFactory,
        private PostalCodeLookup $glsPostalCodeLookup
    ) {

        $this->_awbCollection = $awbCollection;

        $monolog = new MonoLogger('getawbc');
        $monolog->pushHandler(new StreamHandler(BP . '/var/log/getawb.log', Level::Debug));
        $this->logger = $monolog;
    }

    /**
     * Creates shipment programmatically with custom tracking number
     */
    public function execute()
    {
        //only order in "PRELUATA" status  - client request, do not change
        $orderStatus = [self::PENDING_STATUS];

        $orderCollFactory = $this->orderCollectionFactory->create();
         $orderCollection = $orderCollFactory
            ->addAttributeToSelect("*")
            ->addFieldToFilter('status', ['IN' => $orderStatus])
            ->addFieldToFilter('autogedal_allocated_carrier', array('notnull' => true))
            ->addFieldToFilter('awb_number', array('null' => true))
            ->addFieldToFilter('created_at', ['lt' => new \Zend_Db_Expr("DATE_SUB(NOW(), INTERVAL 30 SECOND)")])
            ->setPageSize(100);

        if (count($orderCollection) <= 0){
            return;
        }

        $awdId = ''; $id = ''; $orderId = ''; $samModel = '';$awbModel = ''; $shipmentId = '';
        foreach ($orderCollection as $order) {
            $carrierType = $order->getData('autogedal_allocated_carrier');
            if ($carrierType == CarrierType::PACKETA_FAN_COURIER) {
                continue;
            }

            if ($carrierType == CarrierType::GLS) {
                $shippingAddress = $order->getShippingAddress();
                if ($shippingAddress && empty($shippingAddress->getData('postcode'))) {
                    $city = $shippingAddress->getData('city');
                    $street = $shippingAddress->getData('street');
                    $region = $shippingAddress->getData('region');
                    $countyAbbr = $this->getCountyAbbreviation($region);
                    $postalCode = $this->glsPostalCodeLookup->getPostalCode($city, $street, $countyAbbr);

                    if ($postalCode) {
                        $shippingAddress->setData('postcode', $postalCode);
                        $shippingAddress->save();
                    } else {
                        $order->addCommentToStatusHistory("Codul postal nu a putut fi detectat automat. Va rugam sa-l completati manual.");
                        $order->setState(self::PENDING_STATUS);
                        $order->setStatus('carrier_failed');
                        $this->orderRepository->save($order);
                        continue;
                    }
                }
            }

            $awbCollection = clone $this->_awbCollection;

            /*********** Generate shipment begin **********/
            $orderId = $order->getData('entity_id');

            $shipmentCollection = $order->getShipmentsCollection()->getFirstItem();
            $shipmentId = $shipmentCollection->getEntityId();

            if (empty($shipmentId)) {
                $orderItems = $order->getAllItems();
                $itemArr=[];
                foreach ($orderItems as $item) {
                    $itemArr[$item->getId()] = (int)$item->getQtyOrdered();
                }

                $shipment = $this->shipmentFactory->create($order,$itemArr);
                try {
                    $this->shipmentRepository->save($shipment);
                    $shipmentId = $shipment->getEntityId();
                } catch (\Exception $e) {}
                $this->orderRepository->save($order);

            }

            $awbModel =  $awbCollection->addFieldToFilter('order_id', $orderId)->getFirstItem();
            $awdId = $awbModel->getData('entity_id');
            $awbNumberExists = $awbModel->getAwbNumber();
            /*********** Generate shipment ends **********/

            try {
                if (($carrierType != '') && (empty($awbNumberExists)) && $awbModel->getData('status')!=1 ) {
                    switch ($carrierType)
                    {
                        case CarrierType::URGENT_CURRIER:
                            $awbData = $this->saveAWBData($carrierType, 'eadesigndevurgent', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbUrgent;
                            $currentCarrierPickupPointsRepository = $this->urgentPickupId->getAvailable();
                            break;
                        case CarrierType::FAN_CURRIER:
                            $awbData = $this->saveAWBData($carrierType, 'fancourier_customshipping', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbFanCourier;
                            $currentCarrierPickupPointsRepository = $this->fancourierPickupId->getAvailable();
                            break;
                        case CarrierType::DPD:
                            $awbData = $this->saveAWBData($carrierType, 'dpd', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbDPD;
                            $currentCarrierPickupPointsRepository = $this->dpdPickupId->getAvailable();
                            break;
                        case CarrierType::GLS:
                            $awbData = $this->saveAWBData($carrierType, 'gls', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbGLS;
                            $currentCarrierPickupPointsRepository = $this->glsPickupId->getAvailable();
                            break;
                        case CarrierType::DRAGON_STAR:
                            $awbData = $this->saveAWBData($carrierType, 'dragon', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbDragonStar;
                            $currentCarrierPickupPointsRepository = $this->dragonstarPickupId->getAvailable();
                            break;
                        case CarrierType::NEMO_EXPRESS:
                            $awbData = $this->saveAWBData($carrierType, 'nemoexpress', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbNemoExpress;
                            $currentCarrierPickupPointsRepository = $this->nemoexpressPickupId->getAvailable();
                            break;
                        case CarrierType::SAMEDAY:
                            $awbData = $this->saveAWBData($carrierType, 'sameday', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbSameday;
                            $currentCarrierPickupPointsRepository = $this->samedayPickupId->getAvailable();
                            break;
                        case CarrierType::PACKETA_SAMEDAY:
                            $awbData = $this->saveAWBData($carrierType, 'packeta', $order, $shipmentId, $awdId);
                            $this->generateAwb = $this->generateAwbPacketa;
                            $currentCarrierPickupPointsRepository = 1;

                    }

                    $availableCarriers = $this->awbCarrierType->getAvailable();

                    if (!empty($awdId)) {
                        $id = $awdId;
                        $awbData = $awbModel;
                    } else {
                        $id = $awbData->getEntityId();
                    }

                    $this->generateAwb->setGenerateAwb($id);
                    $awbNumber = $this->generateAwb->generateAwb($id);

                    if (!empty($awbNumber)) {
                        $awbData->setData('awb_number', $awbNumber);

                        $this->messageManager->addSuccessMessage(__('You awb ' . var_export($awbNumber, true) . ' number is generated.'));

                        $awbData->setData('status', 1);
                        $this->awbRepository->save($awbData);

                        if (!empty($awbData->getOrderId())) {
                            $autogedalCarrierText = $availableCarriers[$carrierType]->getText();
                            $orderObject = $this->saveOrderExtraData($awbData->getOrderId(), [
                                self::AWB_NUMBER_FIELD => $awbNumber,
                                self::PICKUP_ID_FIELD => $awbData->getData("awb_pickup_id"),
                                self::CARRIER_ID_FIELD => $carrierType,
                                self::CARRIER_NAME_FIELD => $autogedalCarrierText ?? "",
                                self::PICKUP_NAME_FIELD => $currentCarrierPickupPointsRepository[$awbModel->getData("awb_pickup_id")] ?? ""
                            ]);
                        }
                        $shipment = $this->shipmentRepository->get($shipmentId);
                        $track = $this->trackFactory->create()->setNumber(
                            $awbNumber
                        )->setCarrierCode(
                            $autogedalCarrierText
                        )->setTitle(
                            $autogedalCarrierText
                        );
                        $shipment->addTrack($track);
                        $this->shipmentRepository->save($shipment);
                        $this->helper->callAfterShipmentSaveByCron($shipment, $awbNumber, $autogedalCarrierText, $autogedalCarrierText);
                    } else {
                        $this->messageManager->addErrorMessage(__('You awb was not generated.'));
                    }
                }
            } catch (LocalizedException $e) {
                $this->messageManager->addErrorMessage($e->getMessage());
                $order->addCommentToStatusHistory($e->getMessage());
                $order->setState(self::PENDING_STATUS);
                $order->setStatus('carrier_failed');
                $this->orderRepository->save($order);
            } catch (\Exception $e) {
                $this->messageManager->addExceptionMessage(
                    $e,
                    __('Something went wrong while aprove the awb.') . ':' . $e->getMessage()
                );
                $order->addCommentToStatusHistory($e->getMessage());
                $order->setState(self::PENDING_STATUS);
                $order->setStatus('carrier_failed');
                $this->orderRepository->save($order);
            }
        }
    }

    public function saveAWBData($carrierType, $carrierTypeText, $order, $shipmentId, $awdId)
    {
        $data = [];
        $model = $this->awbFactory->create();
        $shippingAddress = $order->getShippingAddress();
        $warehouse = $order->getData('autogedal_allocated_warehouse');
        $payment = $order->getPayment();
        $paymentMethod = $payment->getData('method');

        $data['tariff_plan'] = $this->scopeConfig->getValue('carriers/'.$carrierTypeText.'/tariff_plan',\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
        $this->logger->info('pickup_id'.$this->getWarehousePickupId($warehouse));
        $data['awb_pickup_id'] = $this->getWarehousePickupId($warehouse);
        $data['delivery_saturday'] = $this->scopeConfig->getValue('carriers/'.$carrierTypeText.'/delivery_saturday',\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
        $data['open_package'] = $this->scopeConfig->getValue('carriers/'.$carrierTypeText.'/open_package',\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
        $data['delivery_payment'] = $this->scopeConfig->getValue('carriers/'.$carrierTypeText.'/delivery_payment',\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
        $data['payment_method'] = $this->scopeConfig->getValue('carriers/'.$carrierTypeText.'/payment_method',\Magento\Store\Model\ScopeInterface::SCOPE_STORE);

        $data['carrier_id'] = $carrierType;
        $data['weight'] = $this->orderFlowHelper->getProductWeight($order);
        $data['order_id'] = $order->getData('entity_id');
        $data['shipment_id'] = $shipmentId;
        if ($order->getData('packages_number') != 1) {
            $data['packages'] = (int)$order->getData('packages_number');
        } else {
            $data['packages'] = ceil($data['weight']/31);
        }
        
        $data['envelopes'] = 0;
        $data['order_value'] = '';
        $grandTotal = $order->getData('grand_total');
        $totalDue = $order->getData('total_due');

        if ($paymentMethod == 'banktransfer') {
            $data['repayment_value'] = 0;
        } elseif (in_array($paymentMethod, ['cardcc', 'net_card']) && $order->getData('autogedal_operator_id')) {
            if ($totalDue < $grandTotal) {
                $data['repayment_value'] = $totalDue;
            } else {
                $data['repayment_value'] = 0;
            }
        } else {
            if ($totalDue < $grandTotal) {
                $data['repayment_value'] = $totalDue;
            } else {
                $data['repayment_value'] = $grandTotal;
            }
        }

        $data['comments'] = '';
        if($shippingAddress->getData('company')){
            $recipient = $shippingAddress->getData('company');
        } else {
            $recipient = $shippingAddress->getData('firstname').' '.$shippingAddress->getData('lastname');
        }

        if ($carrierTypeText == 'eadesigndevurgent' &&
            strlen($recipient) > 40
        ) {
            $recipient = substr($recipient, 0, 40);
        }
        $data['recipient'] = $recipient;
        $data['city'] = $shippingAddress->getData("city");
        $data['street'] = $shippingAddress->getData("street");
        $data['country_id'] = $shippingAddress->getData("country_id");
        $data['region_id'] = $shippingAddress->getData("region");
        if ($carrierType == CarrierType::DPD) {
            $data['street_no'] = self::STREET_NO;
            $data['street_type'] = self::STREET_TYPE;
            $data['delivery_payment'] = self::DPD_PAYMENT;
            $data['content'] = self::DPD_CONTENT;
            $data['comments'] = substr($this->orderFlowHelper->getOrderItemsDpd($order), 0, 200);
        } else if ($carrierType == CarrierType::DRAGON_STAR) {
            $data['comments'] = $this->orderFlowHelper->getOrderItems($order);
        } else if ($carrierType == CarrierType::NEMO_EXPRESS) {
            $data['delivery_payment'] = self::NEMOEXPRESS_PAYMENT;
            $data['content'] =  $this->orderFlowHelper->getOrderItems($order);
        } else if ($carrierType == CarrierType::PACKETA_SAMEDAY) {
            $data['content'] =  substr($this->orderFlowHelper->getOrderItemsDpd($order), 0, 128);
        } else if ($carrierType == CarrierType::GLS) {
            $data['content'] =  substr($this->orderFlowHelper->getOrderItemsDpd($order), 0, 200);
        } else {
          $data['content'] =  $this->orderFlowHelper->getOrderItems($order);
        }
        $data['telephone'] = $shippingAddress->getData("telephone");
        $data['customer_email'] = $order->getData('customer_email');
        $data['postcode'] = $shippingAddress->getData("postcode");
        $data['contact'] = $shippingAddress->getData('firstname').' '.$shippingAddress->getData('lastname');

        if ($awdId) {
            $model = $this->awbRepository->getById($awdId);
            $data['update_time'] = time();
            $model->addData($data);
            $this->awbRepository->save($model);
        } else {
            $model->setData($data);
            $model->setData('update_time', time());
            $this->awbRepository->save($model);
        }
        $this->logger->info('contact: '.$model->getData('contact'));
        return $model;
    }

    protected function saveOrderExtraData($orderId, $extraData = [])
    {
        if (empty($extraData)) {
            return false;
        }

        $orderObject = $this->orderRepository->get($orderId);
        foreach ($extraData as $dataName => $dataValue) {
            $orderObject->setData($dataName, $dataValue);
        }

		$getAwbOrderStatus = $this->orderFlowHelper->getAwbStatusCode();

		$autogedalCarrierText = $extraData[self::CARRIER_NAME_FIELD];
		$awbNumber = $extraData[self::AWB_NUMBER_FIELD];

		$comment = "S-a generat AWB $autogedalCarrierText nr $awbNumber";

		$history = $this->orderHistoryFactory->create()
            ->setStatus(!empty(trim($getAwbOrderStatus)) ? trim($getAwbOrderStatus) : $orderObject->getStatus())
            ->setEntityName(\Magento\Sales\Model\Order::ENTITY)
            ->setComment(
                $comment
            );

        $history->setIsCustomerNotified(false)->setIsVisibleOnFront(false);
        $orderObject->addStatusHistory($history);

		$orderObject->setData('state', OrderModel::STATE_PROCESSING);
		$orderObject->setData('status', !empty(trim($getAwbOrderStatus)) ? trim($getAwbOrderStatus) : $orderObject->getStatus());
        $orderObject->save();

        return $orderObject;
    }

    public function getWarehousePickupId($warehouse)
    {
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $warehouseCollectionFactory=$objectManager->get('Leadlion\AutogedalWarehouses\Model\ResourceModel\Warehouse\CollectionFactory');
        $warehouseCollection = $warehouseCollectionFactory->create()
            ->addFieldToFilter('warehouse_id', $warehouse);
        $pickup_id=$warehouseCollection->getFirstItem()->getData('pickup_id');
        $this->logger->info('$pickup_id'.$pickup_id);
        return $pickup_id;
    }

    /**
     * Get county abbreviation from region name
     * Loads region code from directory_country_region table
     *
     * @param string $region Region name
     * @return string County abbreviation code
     */
    protected function getCountyAbbreviation($region)
    {
        if (empty($region)) {
            return '';
        }

        try {
            // Load region by name for Romania (RO)
            $regionModel = $this->regionFactory->create();
            $regionModel->loadByName($region, 'RO');

            if ($regionModel && $regionModel->getId()) {
                // Return the code field (e.g., "HD", "IS", "CJ")
                return $regionModel->getCode();
            }
        } catch (\Exception $e) {
            $this->logger->warning("Failed to load region code for: {$region}, Error: " . $e->getMessage());
        }

        return '';
    }
}