<?php
/**
 * Copyright © 2018 EaDesign by Eco Active S.R.L. All rights reserved.
 * See LICENSE for license details.
 */

namespace Eadesigndev\GLS\Model;

use Eadesigndev\Urgent\Api\Data\GenerateAwbInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\AbstractModel;
use Eadesigndev\Awb\Model\AwbRepository;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Sales\Api\OrderRepositoryInterface;
use Eadesigndev\GLS\Model\API\GLS;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\HTTP\Client\CurlFactory;
use Magento\Framework\Serialize\Serializer\Json;
use Leadlion\AutogedalWarehouses\Api\WarehouseRepositoryInterface;
use Magento\Framework\App\Filesystem\DirectoryList;

#[\AllowDynamicProperties]
class GenerateAwb extends AbstractModel implements GenerateAwbInterface
{
    const GLS_SAVE_AWB_PATH = 'awbs/gls/';

    const CONFIG_KEY_ACTIVE     = "carriers/gls/active";
    const CONFIG_KEY_API_MODE   = "carriers/gls/api_mode";
    const CONFIG_KEY_USERNAME   = "carriers/gls/user_account_urgent";
    const CONFIG_KEY_PASSWORD   = "carriers/gls/user_password_urgent";
    const CONFIG_KEY_CLIENTID   = "carriers/gls/clientid";
    const RECIPIENT_POSTCODE    = "070000";

    const CONFIG_KEY_PREFIX     = "carriers/gls/";

    const API_URL_PRINT_TEST            = "https://api.test.mygls.ro/ParcelService.svc/json/PrintLabels";
    const API_URL_PRINT_PRODUCTION      = "https://api.mygls.ro/ParcelService.svc/json/PrintLabels";

    protected $_awbRepository;
    protected $_orderRepository;
    protected $_connectorGLS;
    protected $_glsApi;
    protected $_scopeConfig;
    protected $curlFactory;
    protected $json;
    protected $warehouseRepository;
    protected $directoryList;

    public function __construct(
        Context $context,
        \Magento\Framework\Registry $registry,
        AwbRepository $awbRepository,
        OrderRepositoryInterface $orderRepository,
        GLS $glsApi,
        ScopeConfigInterface $scopeConfig,
        CurlFactory $curlFactory,
        Json $json,
        WarehouseRepositoryInterface $warehouseRepository,
        DirectoryList $directoryList,
        ?AbstractResource $resource = null,
        ?AbstractDb $resourceCollection = null,
        array $data = []
    )
    {
        $this->_glsApi = $glsApi;
        $this->_orderRepository = $orderRepository;
        $this->_awbRepository = $awbRepository;
        $this->_scopeConfig = $scopeConfig;
        $this->curlFactory = $curlFactory;
        $this->json = $json;
        $this->warehouseRepository = $warehouseRepository;
        $this->directoryList = $directoryList;
        parent::__construct($context, $registry, $resource, $resourceCollection, $data);
    }

    /**
     * Generate AWB using new GLS REST API (MyGLS)
     * If url_feed is set, uses REST API, otherwise falls back to old SOAP method
     *
     * @param int $awbId
     * @return string Parcel ID
     * @throws \Exception
     */
    public function generateAwb($awbId)
    {
        $model = $this->_awbRepository->getById($awbId);

        return $this->generateAwbRestApi($model);
    }

    /**
     * Generate AWB using REST API with PrintLabels
     * This combines PrepareLabels + GetPrintedLabels in one call
     *
     * @param \Eadesigndev\Awb\Model\Awb $model
     * @return string Parcel Number (AWB number)
     * @throws \Exception
     */
    protected function generateAwbRestApi($model)
    {
        // Determine printer type based on pickup_id
        $pickupId = $model->getData('awb_pickup_id');
        $printerType = ($pickupId == '666') ? 'Connect' : 'A4_4x1';

        $params = $this->buildPrintLabelsParams($model, $printerType);
        $response = $this->callPrintLabelsApi($params);

        if (!empty($response['PrintLabelsErrorList'])) {
            $errorDesc = $response['PrintLabelsErrorList'][0]['ErrorDescription'] ?? 'Unknown error';
            throw new \Exception($errorDesc);
        }

        if (empty($response['PrintLabelsInfoList'])) {
            throw new \Exception(__('Could not create label - no parcel info returned'));
        }

        $parcelInfo = $response['PrintLabelsInfoList'][0];
        $parcelNumber = $parcelInfo['ParcelNumber'] ?? null;

        if (!$parcelNumber) {
            throw new \Exception(__('ParcelNumber not returned from API'));
        }

        if (empty($response['Labels'])) {
            throw new \Exception(__('PDF label not returned from API'));
        }

        $pdfContent = implode(array_map('chr', $response['Labels']));
        $this->savePdf($parcelNumber, $pdfContent);

        return (string)$parcelNumber;
    }

    /**
     * Generate AWB using old SOAP API (backward compatibility)
     *
     * @param \Eadesigndev\Awb\Model\Awb $model
     * @return string Parcel ID
     * @throws \Exception
     */
    protected function generateAwbSoap($model)
    {
        $config = array(
            'username'       => $this->_scopeConfig->getValue(self::CONFIG_KEY_USERNAME),
            'password'       => $this->_scopeConfig->getValue(self::CONFIG_KEY_PASSWORD),
            'senderid'       => $this->_scopeConfig->getValue(self::CONFIG_KEY_CLIENTID),

            'sender_name'    => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_name"),
            'sender_address' => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_address"),
            'sender_city'    => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_city"),
            'sender_zipcode' => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_zipcode"),
            'sender_country' => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_country"),
            'sender_contact' => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_contact"),
            'sender_phone'   => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_phone"),
            'sender_email'   => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_email"),

            'consig_name' => $model->getData('recipient'),
            'consig_address' => $model->getData('street'),
            'consig_city' => $model->getData('city'),
            'consig_zipcode' => self::RECIPIENT_POSTCODE,
            'consig_contact' => $model->getData('recipient'),
            'consig_phone' => $model->getData('telephone'),
            'consig_email' => $model->getData('customer_email'),
            'consig_country' => 'RO',
            'pcount' => $model->getData('packages') + $model->getData('envelopes'),
            'pickupdate' => date("Y-m-d"),
            'content' => $model->getData('content'),
            'clientref' => 'Comanda #' . $model->getData('order_id'),
            'codamount' => $model->getData('repayment_value'),
            'codref' => 'Comanda #' . $model->getData('order_id'),
            'timestamp'  => date('YmdHis', time()),
        );

        $this->_glsApi->setData($config);
        $this->_setOrderServices([$model->getData('tariff_plan') => $model->getData('tariff_plan')]);
        $response = $this->_glsApi->generateParcel();

        if(isset($response['successfull']) && $response['successfull'] == true) {
            if (!empty($response['pcls'][0])) {
                $parcelId = $response['pcls'][0];
                if (isset($response2)) {
                    $this->_generatePdf($response, $response2);
                } else {
                    $this->_generatePdf($response);
                }
                return $parcelId;
            } else {
                throw new \Exception(__('Something bad happened'));
            }
        }

        if (!empty($response['errdesc'])) {
            throw new \Exception($response['errdesc']);
        }

        throw new \Exception(__('Something bad happened'));
    }

    /**
     * Build parameters for PrintLabels API call
     *
     * @param \Eadesigndev\Awb\Model\Awb $model
     * @param string $printerType 'Connect' or 'A4_4x1'
     * @param int $printPosition Print position (1-4 for A4)
     * @return array
     */
    protected function buildPrintLabelsParams($model, string $printerType = 'A4_4x1', int $printPosition = 1): array
    {
        $clientId = $this->_scopeConfig->getValue(self::CONFIG_KEY_CLIENTID);

        // Format phone number
        $recipientPhone = $model->getData('telephone');
        if (strlen($recipientPhone) < 11) {
            $recipientPhone = '004' . $recipientPhone;
        }

        // Load warehouse to get pickup address data
        $pickupId = $model->getData('awb_pickup_id');
        $warehouse = null;
        $warehouseName = '';
        $senderPhone = $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_phone");
        $senderStreet = $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_address");
        $senderCity = $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_city");
        $senderZipCode = $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_zipcode");

        if ($pickupId) {
            try {
                // Load warehouse by pickup_id using ObjectManager (collection approach)
                $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
                $warehouseCollection = $objectManager->create(
                    'Leadlion\AutogedalWarehouses\Model\ResourceModel\Warehouse\CollectionFactory'
                )->create();

                $warehouse = $warehouseCollection
                    ->addFieldToFilter('pickup_id', $pickupId)
                    ->getFirstItem();

                // Use warehouse data if available and warehouse was found
                if ($warehouse && $warehouse->getId()) {
                    if ($warehouse->getName()) {
                        $warehouseName = $warehouse->getName();
                    }
                    if ($warehouse->getPhone()) {
                        $senderPhone = $warehouse->getPhone();
                    }
                    if ($warehouse->getAddress()) {
                        $senderStreet = $warehouse->getAddress();
                    }
                    if ($warehouse->getCity()) {
                        $senderCity = $warehouse->getCity();
                    }
                    if ($warehouse->getZipCode()) {
                        $senderZipCode = $warehouse->getZipCode();
                    }
                }
            } catch (\Exception $e) {
                // If warehouse not found, use config values as fallback
            }
        }

        // Format sender phone number
        if (strlen($senderPhone) < 11) {
            $senderPhone = '004' . $senderPhone;
        }

        // Calculate parcel count and weight per parcel
        $parcelCount = (int)($model->getData('packages') + $model->getData('envelopes')) ?: 1;
        $totalWeight = (float)$model->getData('weight') ?: 1;

        $parcelPropertyList = [];
        $parcelPropertyList[] = [
                'Content' => $model->getData('content') ?: 'Parcel',
                'PackageType' => 2,
                'Height' => '1',
                'Length' => '1',
                'Width' => '1',
                'Weight' => $totalWeight
            ];

        // Build the parcel data
        $parcel = [
            'ClientNumber' => (int)$clientId,
            'ClientReference' => $warehouseName,
            'Content' => $model->getData('content') ?: 'Parcel',
            'Count' => $parcelCount,
            'PickupAddress' => [
                'Name' => $this->_scopeConfig->getValue(self::CONFIG_KEY_PREFIX . "sender_name"),
                'Street' => $senderStreet,
                'City' => $senderCity,
                'ZipCode' => $senderZipCode,
                'CountryIsoCode' => 'RO',
                'ContactPhone' => $senderPhone
            ],
            'DeliveryAddress' => [
                'Name' => $model->getData('recipient'),
                'Street' => $model->getData('street'),
                'City' => $model->getData('city'),
                'ZipCode' => $model->getData('postcode') ?: self::RECIPIENT_POSTCODE,
                'CountryIsoCode' => 'RO',
                'ContactName' => $model->getData('recipient'),
                'ContactPhone' => $recipientPhone,
                'ContactEmail' => $model->getData('customer_email')
            ],
            'ParcelPropertyList' => $parcelPropertyList
        ];

        // Add COD if applicable
        if ($codAmount = $model->getData('repayment_value')) {
            $parcel['CODAmount'] = (float)$codAmount;
            $parcel['CODReference'] = 'Comanda #' . $model->getData('order_id');
            $parcel['ServiceList'][] = ['Code' => 'COD'];
        }

        // Add other services based on tariff plan
        $this->addServicesFromTariff($parcel, $model, $recipientPhone);

        return [
            'ParcelList' => [$parcel],
            'TypeOfPrinter' => $printerType,
            'PrintPosition' => $printPosition,
            'ShowPrintDialog' => false,
            'WebshopEngine' => 'Magento2'
        ];
    }

    /**
     * Add services to parcel based on tariff plan
     *
     * @param array &$parcel
     * @param \Eadesigndev\Awb\Model\Awb $model
     * @param string $recipientPhone
     * @return void
     */
    protected function addServicesFromTariff(array &$parcel, $model, string $recipientPhone): void
    {
        if (!isset($parcel['ServiceList'])) {
            $parcel['ServiceList'] = [];
        }

        // FDS - FlexDeliveryService (Email notification)
        $customerEmail = $model->getData('customer_email');
        if (!empty($customerEmail)) {
            $parcel['ServiceList'][] = [
                "Code" => "FDS",
                "FDSParameter" => [
                    "StringValue" => $customerEmail
                ]
            ];
        }

        // FSS - FlexDeliveryService SMS (Phone notification)
        if (!empty($recipientPhone)) {
            $parcel['ServiceList'][] = [
                "Code" => "FSS",
                "FSSParameter" => [
                    "StringValue" => $recipientPhone
                ]
            ];
        }
    }

    /**
     * Call the GLS PrintLabels REST API
     * This combines PrepareLabels + GetPrintedLabels in one call
     *
     * @param array $params
     * @return array
     * @throws \Exception
     */
    protected function callPrintLabelsApi(array $params): array
    {
        $apiUrl = $this->getApiUrl();
        $username = $this->_scopeConfig->getValue(self::CONFIG_KEY_USERNAME);
        $password = $this->_scopeConfig->getValue(self::CONFIG_KEY_PASSWORD);

        if (!$username || !$password) {
            throw new \Exception('GLS API credentials are not configured');
        }

        $passwordBytes = unpack('C*', hash('sha512', $password, true)) ?: [];
        $params['Username'] = $username;
        $params['Password'] = array_values($passwordBytes);

        $curl = $this->curlFactory->create();
        $curl->setHeaders(['Content-Type' => 'application/json']);
        $curl->setTimeout(30);

        try {
            $curl->post($apiUrl, $this->json->serialize($params));

            $status = $curl->getStatus();
            $body = $curl->getBody();

            if ($status !== 200) {
                throw new \Exception("GLS API returned status {$status}: {$body}");
            }

            $response = $this->json->unserialize($body);
            return $response;

        } catch (\Exception $e) {
            throw new \Exception('GLS API request failed: ' . $e->getMessage());
        }
    }


    /**
     * Get the GLS API URL for PrintLabels endpoint
     * Switches between test and production based on admin configuration
     *
     * @return string
     */
    protected function getApiUrl(): string
    {
        $apiMode = $this->_scopeConfig->getValue(self::CONFIG_KEY_API_MODE);
        $isProduction = ($apiMode === 'production');

        return $isProduction ? self::API_URL_PRINT_PRODUCTION : self::API_URL_PRINT_TEST;
    }

    /**
     * Get printed label PDF from server
     * This method is called when user wants to print the label
     *
     * @param string $parcelNumber Parcel Number (AWB number)
     * @param string $printerType Printer type (ignored - for compatibility with Helper interface)
     * @param int $printPosition Print position (ignored - for compatibility with Helper interface)
     * @return string PDF content as binary string
     * @throws \Exception
     */
    public function getPrintedLabel(string $parcelNumber, string $printerType = 'A4_4x1', int $printPosition = 1): string
    {
        // Note: $printerType and $printPosition are ignored because we read the already-generated PDF from disk
        // These parameters exist only for compatibility with the Helper::getAwbDocument() interface

        $varPath = $this->directoryList->getPath(DirectoryList::VAR_DIR);
        $awbPath = $varPath . '/' . self::GLS_SAVE_AWB_PATH;
        $pdfFilePath = $awbPath . $parcelNumber . '.pdf';

        if (!file_exists($pdfFilePath)) {
            throw new \Exception("PDF label not found on server for AWB: {$parcelNumber}");
        }

        $pdfContent = file_get_contents($pdfFilePath);

        if ($pdfContent === false) {
            throw new \Exception("Failed to read PDF label from server for AWB: {$parcelNumber}");
        }

        return $pdfContent;
    }

    /**
     * Save PDF label to file in var/awbs/gls/ directory
     * Called from generateAwbRestApi() after PrintLabels API returns the PDF
     * This directory is NOT publicly accessible for security
     *
     * @param string $parcelNumber
     * @param string $pdfContent
     * @return void
     */
    protected function savePdf(string $parcelNumber, string $pdfContent): void
    {
        $varPath = $this->directoryList->getPath(DirectoryList::VAR_DIR);
        $awbPath = $varPath . '/' . self::GLS_SAVE_AWB_PATH;

        if (!file_exists($awbPath)) {
            mkdir($awbPath, 0755, true);
        }

        $filename = $awbPath . $parcelNumber . '.pdf';
        file_put_contents($filename, $pdfContent);
    }

    /**
     * Delete AWB - tries REST API first, falls back to SOAP
     *
     * @param string $awbNumber
     * @return bool
     */
    public function deleteAwb($awbNumber)
    {
        // For now, use old SOAP method
        // TODO: Implement REST API DeleteLabels method
        return !empty($this->_glsApi->deleteParcel([$awbNumber]));
    }

    /**
     * Legacy method for SOAP API
     */
    private function _setOrderServices($services)
    {
        $data = $this->_glsApi->toArray();
        $phone = isset($data['consig_phone']) ? $data['consig_phone'] : '';
        if (strlen($phone) < 11) {
            $phone = '004'.$phone;
        }
        $pickupdate = isset($data['pickupdate']) ? $data['pickupdate'] : '';
        foreach ($services as $key => $value) {
            if(!isset($value['code'])){
                unset($services[$key]);
            }
        }
        if(isset($services[3]) && $services[3]['code'] == 'PRS'){
            $services[3]['info'] = $data['pickupdate'];
            $data['printit'] = false;
        }
        if(isset($services[5]) && $services[5]['code'] == 'SM2'){
            $services[5]['info'] = $phone;
        }
        if(isset($services[4]['code']) && $services[4]['code'] == 'SM1' && isset($services[4]['info'])){
            $services[4]['info'] = $phone.'|'.$services[4]['info'];
        }elseif(isset($services[4]['code']) && $services[4]['code'] == 'SM1'){
            $services[4]['info'] = $phone;
        }else{
            unset($services[4]);
        }
        if(isset($services[8]) && $services[8]['code'] == 'INS'){
            $services[8]['info'] = (int) $services[8]['info'];
        }

        if(isset($services[3]) && $services[3]['code'] == 'PRS'){
            $data['printit'] = false;
            $this->_glsApi->setData($data);
            $this->_invertSender($data);
        }

        $this->_glsApi->setData(array('services' => $services));
    }

    /**
     * Legacy method for SOAP API
     */
    public function _invertSender($data)
    {
        $data_to_set['sender_name'] = $data['consig_name'];
        $data_to_set['sender_address'] = $data['consig_address'];
        $data_to_set['sender_city'] = $data['consig_city'];
        $data_to_set['sender_zipcode'] = $data['consig_zipcode'];
        $data_to_set['sender_contact'] = $data['consig_contact'];
        $data_to_set['sender_phone'] = $data['consig_phone'];
        $data_to_set['sender_email'] = $data['consig_email'];
        $data_to_set['consig_name'] = $data['sender_name'];
        $data_to_set['consig_address'] = $data['sender_address'];
        $data_to_set['consig_city'] = $data['sender_city'];
        $data_to_set['consig_zipcode'] = $data['sender_zipcode'];
        $data_to_set['consig_contact'] = $data['sender_contact'];
        $data_to_set['consig_phone'] = $data['sender_phone'];
        $data_to_set['consig_email'] = $data['sender_email'];

        $this->_glsApi->setData($data_to_set);
    }

    /**
     * Legacy PDF generation method for SOAP API
     */
    public function _generatePdf($response, $response2 = null)
    {
        $path = $_SERVER['DOCUMENT_ROOT'] . self::GLS_SAVE_AWB_PATH;
        if (!file_exists($path)) {
            mkdir($path, 0755, true);
        }

        if (isset($response['pdfdata'])) {
            $pdf_decoded = base64_decode($response['pdfdata']);
            if (!empty($response['pcls'][0])) {
                $filename = $path . ((int)$response['pcls'][0]) . '.pdf';
                file_put_contents($filename, $pdf_decoded);
            }
        }

        if ($response2 !== null && isset($response2['pdfdata'])) {
            $pdf_decoded2 = base64_decode($response2['pdfdata']);
            if (!empty($response['pcls'][0])) {
                $filename = $path . 'ex_' . ((int)$response['pcls'][0]) . '.pdf';
                file_put_contents($filename, $pdf_decoded2);
            }
        }
    }

    public function getPunctRidicare()
    {
        // TODO: Implement getPunctRidicare() method
    }

    public function setGenerateAwb($awbId)
    {
        // TODO: Implement setGenerateAwb() method.
    }
}
