<?php

namespace YSquare\Shipcloud\Application\Controller\Admin;

use OxidEsales\Eshop\Application\Model\Country;
use OxidEsales\Eshop\Application\Model\CountryList;
use OxidEsales\Eshop\Application\Model\Orderarticle;
use OxidEsales\Eshop\Application\Model\State;
use OxidEsales\Eshop\Core\Email;
use OxidEsales\Eshop\Core\Model\ListModel;
use OxidEsales\Eshop\Core\Registry;
use YSquare\Shipcloud\Application\Model\Shipcloud;
use YSquare\Shipcloud\Application\Model\Shipcloud2Orderarticle;
use YSquare\Shipcloud\Core\Exception\ShipcloudApiException;

class ShipcloudFormController extends AdminController
{
    protected $_sThisTemplate = 'shipcloud_order_form.tpl';// phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
    protected $packageTemplates;
    /** @var Shipcloud */
    protected $shipcloud;
    protected $order;
    protected $returnAddresses;
    protected $message = [];

    public function __construct()
    {
        parent::__construct('shipcloud_order');
    }

    public function getCarrierPackages($carrierKey)
    {
        if ($this->active_carriers and $this->active_carriers[$carrierKey]) {
            return $this->active_carriers[$carrierKey]['packages'];
        } else {
            return [];
        }
    }

    public function createShipment()
    {
        $address_data = $this->adminSettings->getSettings('from', 'main_settings');
        $create_shipping_label = Registry::getRequest()->getRequestEscapedParameter('createShippingLabel');

        $parameters = [];
        parse_str(Registry::getRequest()->getRequestEscapedParameter('parameters'), $parameters);
        $editval = $parameters['editval'];
        $orderval = $parameters['orderval'];

        if ($parameters['deliveryAddressActive']) {
            $street = $orderval['oxorder__oxdelstreet'];
            $street_no = $orderval['oxorder__oxdelstreetnr'];
            $zip_code = $orderval['oxorder__oxdelzip'];
            $city = $orderval['oxorder__oxdelcity'];
            $country_id = $orderval['oxorder__oxdelcountryid'];
            $mail = $orderval['oxorder__oxdelemail'];
            $company = $orderval['oxorder__oxdelcompany'];
            $first_name = $orderval['oxorder__oxdelfname'];
            $last_name = $orderval['oxorder__oxdellname'];
            $care_of = $orderval['oxorder__oxdeladdinfo'];
            $state_id = $orderval['oxorder__oxdelstateid'];
            $phone = $orderval['oxorder__oxdelfon'];
        } else {
            $street = $orderval['oxorder__oxbillstreet'];
            $street_no = $orderval['oxorder__oxbillstreetnr'];
            $zip_code = $orderval['oxorder__oxbillzip'];
            $city = $orderval['oxorder__oxbillcity'];
            $country_id = $orderval['oxorder__oxbillcountryid'];
            $mail = $orderval['oxorder__oxbillemail'];
            $company = $orderval['oxorder__oxbillcompany'];
            $first_name = $orderval['oxorder__oxbillfname'];
            $last_name = $orderval['oxorder__oxbilllname'];
            $care_of = $orderval['oxorder__oxbilladdinfo'];
            $state_id = $orderval['oxorder__oxdelstateid'];
            $phone = $orderval['oxorder__oxbillfon'];
        }

        if ($create_shipping_label == "true") {
            $create_shipping_label = true;
        } else {
            $create_shipping_label = false;
        }

        $oCountry = oxNew(Country::class);
        $oCountry->loadInLang(0, $country_id);
        $country = $oCountry->oxcountry__oxisoalpha2->value;

        $oState = oxNew(State::class);
        $oState->loadInLang(0, $state_id);
        $state = $oState->oxstates__oxisoalpha2->value;

        $data = [
            "from"                  => [
                "company"    => $address_data['company'],
                "first_name" => $address_data['fname'],
                "last_name"  => $address_data['lname'],
                "care_of"    => $address_data['addinfo'],
                "street"     => $address_data['street'],
                "street_no"  => $address_data['streetnr'],
                "zip_code"   => $address_data['zip'],
                "city"       => $address_data['city'],
                "country"    => $address_data['country'],
            ],
            "to"                    => [
                "company"    => $company,
                "first_name" => $first_name,
                "last_name"  => $last_name,
                "care_of"    => $care_of,
                "street"     => $street,
                "street_no"  => $street_no,
                "zip_code"   => $zip_code,
                "city"       => $city,
                "state"      => $state,
                "country"    => $country,
                "phone"      => $phone,
            ],
            "package"               => [
                "weight" => number_format($editval['ysshipcloud__yspackageweight'], 2),
                "length" => number_format($editval['ysshipcloud__yspackagelength'], 2),
                "width"  => number_format($editval['ysshipcloud__yspackagewidth'], 2),
                "height" => number_format($editval['ysshipcloud__yspackageheight'], 2),
                "type"   => $editval['ysshipcloud__yspackagetype'],
            ],
            "carrier"               => $editval['ysshipcloud__yscarrier'],
            "service"               => $editval['ysshipcloud__yscarrierservice'],
            "reference_number"      => $editval['ysshipcloud__ysreferencenumber'],
            "notification_email"    => $mail,
            "description"           => $editval['ysshipcloud__yspackagecontentdescription'],
            "create_shipping_label" => $create_shipping_label,
        ];

        if ($create_shipping_label && $editval['ysshipcloud__ysshipcloudadvancenotice'] == "on") {
            $data["additional_services"] = [
                [
                    "name"       => "advance_notice",
                    "properties" => [
                        "phone" => $phone,
                    ],
                ],
            ];
        }

        $shipcloud_id = $editval['ysshipcloud__ysshipcloudid'];

        try {
            $data = $this->adminRest->createShipment($data, $shipcloud_id);

            $soxId = $parameters['oxid'];
            $shipcloud = oxNew(Shipcloud::class);
            $shipcloud->load($soxId);

            $params = [];
            $params['ysshipcloud__ysshipcloudid'] = $data['id'];
            $params['ysshipcloud__ysshipcloudtrackingnumber'] = $data['carrier_tracking_no'];
            $params['ysshipcloud__ysshipclouddeliverycosts'] = $data['price'];
            $params['ysshipcloud__ysshipcloudlabelurl'] = $data['label_url'];
            $params['ysshipcloud__ystrackingurl'] = $data['tracking_url'];

            $shipcloud->assign($params);
            $result = $shipcloud->save();

            if ($result) {
                $oShipcloud = oxNew(Shipcloud::class);
                $oShipcloud->load($result);
            }

            header('Content-Type: application/json; charset=UTF-8');
            echo json_encode(["exception" => null, "result" => $data, "log" => json_encode($data), "save" => $result]);

            if (
                $editval['ysshipcloud__yscarrierservice'] != "returns" and
                $create_shipping_label and $editval["send_order_mail"]
            ) {
                $this->sendOrderMail();
            }
        } catch (ShipcloudApiException $e) {
            header('Content-Type: application/json; charset=UTF-8');
            echo json_encode(
                ["exception" => ["code" => $e->getCode(), "log" => json_encode($data), "message" => $e->getMessage()]]
            );
        }
        exit();
    }

    public function save()
    {
        $soxId = $this->getEditObjectId();

        $parameter = [];
        parse_str(Registry::getRequest()->getRequestEscapedParameter('parameters'), $parameter);
        $editval = $parameter['editval'];
        $orderval = $parameter['orderval'];
        $order_articles = $parameter['order_articles'];

        if ($order_articles) {
            foreach ($order_articles as $oxid => $order_article) {
                if ($oxid != 'oa') {
                    $s2oa = oxNew(Shipcloud2Orderarticle::class);
                    $s2oa->load($oxid);
                    if (!$order_article['active'] and $s2oa->ysshipcloud2orderarticles__oxamount->value > 0) {
                        $s2oa->assign(['ysshipcloud2orderarticles__oxamount' => 0]);
                        $s2oa->save();
                    } elseif ($s2oa->ysshipcloud2orderarticles__oxamount->value != $order_article['amount']) {
                        $s2oa->assign(['ysshipcloud2orderarticles__oxamount' => $order_article['amount']]);
                        $s2oa->save();
                    }
                } else {
                    foreach ($order_article as $oa_id => $oa) {
                        $order_article = oxNew('oxorderarticle');
                        $order_article->load($oa_id);
                        if ($order_article->oxorderarticles__oxweight->value != $oa['weight']) {
                            $order_article->assign(['oxorderarticles__oxweight' => $oa['weight']]);
                            $oa_id = $order_article->save();
                            $order_article = oxNew('oxorderarticle');
                            $order_article->load($oa_id);
                        }
                    }
                }
            }
        }

        if ($editval['ysshipcloud__ysshipcloudsendmail'] == 'on') {
            $editval['ysshipcloud__ysshipcloudsendmail'] = 1;
        } else {
            $editval['ysshipcloud__ysshipcloudsendmail'] = 0;
        }

        if ($editval['ysshipcloud__ysshipcloudadvancenotice'] == 'on') {
            $editval['ysshipcloud__ysshipcloudadvancenotice'] = 1;
        } else {
            $editval['ysshipcloud__ysshipcloudadvancenotice'] = 0;
        }

        if ($editval['ysshipcloud__ysshipcloudsendshipcloudmail'] == 'on') {
            $editval['ysshipcloud__ysshipcloudsendshipcloudmail'] = 1;
        } else {
            $editval['ysshipcloud__ysshipcloudsendshipcloudmail'] = 0;
        }

        $carrier = $editval['ysshipcloud__yscarrier'];
        $carrierService = $editval['ysshipcloud__yscarrierservice'];

        // Remove carrier service if not available for carrier
        if ($carrier and $carrierService) {
            $carrierServices = $this->getCarrierServices($carrier);
            if (!array_key_exists($carrierService, $carrierServices)) {
                $parameters['ysshipcloud__yscarrierservice'] = "";
            }
        }

        $this->shipcloud = oxNew(Shipcloud::class);
        if ($soxId != "-1") {
            $this->shipcloud->load($soxId);
        } else {
            $editval['ysshipcloud__oxid'] = null;
        }

        $order = $this->getOrder();
        $order->assign($orderval);
        $order->save();

        // Reset Package Template values if changed
        if ($this->shipcloud->ysshipcloud__yspackagetemplate->value != $editval['ysshipcloud__yspackagetemplate']) {
            $editval['ysshipcloud__yspackagewidth'] = "";
            $editval['ysshipcloud__yspackagelength'] = "";
            $editval['ysshipcloud__yspackageheight'] = "";
            $editval['ysshipcloud__yspackageadditionalweight'] = "";
            $editval['ysshipcloud__yscarrier'] = "";
            $editval['ysshipcloud__yscarrierservice'] = "";
            $editval['ysshipcloud__yspackagetype'] = "";
        }

        $oldCarrierService = $this->shipcloud->ysshipcloud__yscarrierservice->value;

        if ($oldCarrierService != $editval['ysshipcloud__yscarrierservice'] and $oldCarrierService == 'returns') {
            $failed = false;
            if ($order_articles) {
                foreach ($order_articles as $shipcloud_oxid => $order_article) {
                    if ($oxid != 'oa') {
                        if (!$order_article['active']) {
                            $amount = 0;
                        } else {
                            $amount = $order_article['amount'];
                        }

                        $order_article = oxNew(Orderarticle::class);
                        $order_article->load($order_article['oa']);

                        $sum = $order_article->oxorderarticles__oxamount->value;

                        $queryBuilder = $this->queryBuilderFactory->create();
                        $sum_send = $queryBuilder
                            ->select('sum(s2oa.oxamount) as sm')
                            ->from('ysshipcloud2orderarticles', 's2oa')
                            ->innerJoin('s2oa', 'oxorderarticles', 'oa', 's2oa.OXOARTID = oa.OXID')
                            ->innerJoin('s2oa', 'ysshipcloud', 's', 's2oa.YSSHCLID = s.OXID')
                            ->andWhere("s2oa.YSCARRIERSERVICE != 'returns'")
                            ->andWhere('oa.OXID = :oa_oxid')
                            ->setParameter("oa_oxid", $order_article->oxorderarticles__oxid->value)
                            ->execute()->fetch()["sm"];

                        if (!$sum_send) {
                            $sum_send = 0;
                        }
                        $max_amount_available = $sum - $sum_send;

                        if ($amount > $max_amount_available) {
                            $failed = true;
                        }
                    }
                }

                if ($failed) {
                    $editval['ysshipcloud__yscarrierservice'] = 'returns';
                    $this->message[] = [
                        "type" => "danger",
                        "text" => "Service konnte nicht geändert werden da nicht genug Artikel verfügbar sind.",
                        "info" => "",
                        "time" => 3000,
                    ];
                }
            }
        }
        if ($editval['ysshipcloud__ysshipcloudsendmail']) {
            $editval['ysshipcloud__ysshipcloudsendmail'] = 1;
        } else {
            $editval['ysshipcloud__ysshipcloudsendmail'] = 0;
        }

        $this->shipcloud->assign($editval);
        $this->shipcloud->save();
    }

    protected function getOrder($shipcloud_id = null)
    {
        if (isset($this->order)) {
            return $this->order;
        }
        if (is_null($shipcloud_id)) {
            $shipcloud_id = $this->getEditObjectId();
        }
        /** @var Shipcloud $shipcloud */
        $shipcloud = oxNew(Shipcloud::class);
        $shipcloud->load($shipcloud_id);

        $this->order = $shipcloud->getOrder();
        return $this->order;
    }

    public function sendOrderMail()
    {
        $parameters = [];
        parse_str(Registry::getRequest()->getRequestEscapedParameter('parameters'), $parameters);

        $soxId = $parameters['oxid'];
        if (isset($soxId) && $soxId != "-1") {
            // load object
            $this->shipcloud = oxNew(Shipcloud::class);
            $this->shipcloud->load($soxId);

            $this->_aViewData["edit"] = $this->shipcloud;
        }

        $this->_aViewData["order"] = $this->getOrder($soxId);

        $oEmail = oxNew(Email::class);
        $order = $this->getOrder();
        $result = $oEmail->sendSendedShipcloudMail($order, $this->shipcloud);

        if ($result["send"]) {
            $this->shipcloud->assign([
                'yssendmaildate' => date("Y-m-d H:i:s", Registry::getUtilsDate()->getTime()),
            ]);
            $this->shipcloud->save();
        }

        echo json_encode(["exception" => null, "result" => $result]);
        exit();
    }

    public function getReturnAddresses()
    {
        if (isset($this->returnAddresses)) {
            return $this->returnAddresses;
        }

        $queryBuilder = $this->queryBuilderFactory->create();
        $queryBuilder
            ->select('*')
            ->from('ysshipcloudaddress')
            ->where('YS_RETURN_ADDRESS = 1');

        // order articles
        $this->returnAddresses = oxNew(ListModel::class);
        $this->returnAddresses->init(\YSquare\Shipcloud\Application\Model\ShipcloudAddress::class);
        $this->returnAddresses->selectString($queryBuilder->getSQL(), $queryBuilder->getParameters());

        return $this->returnAddresses;
    }

    public function getShippingCosts()
    {
        $parameters = [];
        parse_str(Registry::getRequest()->getRequestEscapedParameter('parameters'), $parameters);
        $editval = $parameters['editval'];
        $orderval = $parameters['orderval'];

        if ($parameters['deliveryAddressActive']) {
            $street = $orderval['oxorder__oxdelstreet'];
            $street_no = $orderval['oxorder__oxdelstreetnr'];
            $zip_code = $orderval['oxorder__oxdelzip'];
            $city = $orderval['oxorder__oxdelcity'];
            $country_id = $orderval['oxorder__oxdelcountryid'];
        } else {
            $street = $orderval['oxorder__oxbillstreet'];
            $street_no = $orderval['oxorder__oxbillstreetnr'];
            $zip_code = $orderval['oxorder__oxbillzip'];
            $city = $orderval['oxorder__oxbillcity'];
            $country_id = $orderval['oxorder__oxbillcountryid'];
        }

        $oCountry = oxNew(Country::class);
        $oCountry->loadInLang(0, $country_id);
        $country = $oCountry->oxcountry__oxisoalpha2->value;

        $data = [
            "carrier" => $editval['ysshipcloud__yscarrier'],
            "service" => $editval['ysshipcloud__yscarrierservice'],
            "to"      => [
                "street"    => $street,
                "street_no" => $street_no,
                "zip_code"  => $zip_code,
                "city"      => $city,
                "country"   => $country,
            ],
            "package" => [
                "weight" => number_format($editval['ysshipcloud__yspackageweight'], 2),
                "length" => number_format($editval['ysshipcloud__yspackagelength'], 2),
                "width"  => number_format($editval['ysshipcloud__yspackagewidth'], 2),
                "height" => number_format($editval['ysshipcloud__yspackageheight'], 2),
                "type"   => $editval['ysshipcloud__yspackagetype'],
            ],
        ];

        try {
            header('Content-Type: application/json; charset=UTF-8');
            echo json_encode(["exception" => null, "result" => $this->adminRest->getShippingCosts($data)]);
            exit();
        } catch (ShipcloudApiException $e) {
            header('Content-Type: application/json; charset=UTF-8');
            echo json_encode(["exception" => ["code" => $e->getCode(), "message" => $e->getMessage()]]);
            exit();
        }
    }

    public function deleteShipment()
    {
        $parameters = [];
        parse_str(Registry::getRequest()->getRequestEscapedParameter('parameters'), $parameters);
        $editval = $parameters['editval'];

        try {
            $this->adminRest->delete("/v1/shipments/" . $editval['ysshipcloud__ysshipcloudid']);

            $soxId = $parameters['oxid'];
            $shipcloud = oxNew(Shipcloud::class);
            $shipcloud->load($soxId);

            $params = [];
            $params['ysshipcloud__ysshipcloudid'] = '';
            $params['ysshipcloud__ysshipcloudtrackingnumber'] = '';
            $params['ysshipcloud__ysshipclouddeliverycosts'] = '';
            $params['ysshipcloud__ysshipcloudlabelurl'] = '';

            $shipcloud->assign($params);
            $result = $shipcloud->save();

            if ($result) {
                $oShipcloud = oxNew(Shipcloud::class);
                $oShipcloud->load($result);
            }

            header('Content-Type: application/json; charset=UTF-8');
            echo json_encode(["exception" => null, "save" => $result]);
        } catch (ShipcloudApiException $e) {
            header('Content-Type: application/json; charset=UTF-8');
            echo json_encode(["exception" => ["code" => $e->getCode(), "message" => $e->getMessage()]]);
        }
        exit();
    }

    public function render(): string
    {
        $ret = parent::render();

        $soxId = $this->_aViewData["oxid"] = $this->getEditObjectId();
        if (isset($soxId) && $soxId != "-1") {
            // load object
            $this->shipcloud = oxNew(Shipcloud::class);
            $this->shipcloud->load($soxId);

            $this->_aViewData["edit"] = $this->shipcloud;
        }

        $order = $this->getOrder($soxId);
        $this->_aViewData["order"] = $order;
        if ($order->getFieldData('oxdelfname')
            or $order->getFieldData('oxdelcompany')
            or $order->getFieldData('oxdellname')
        ) {
            $this->_aViewData["daActive"] = 1;
        }
        $this->_aViewData["order_form_message"] = json_encode($this->message);

        $this->_aViewData["main_settings"] = $this->adminSettings->getSettings(null, 'main_settings')['settings'];

        $oCountryList = oxNew(CountryList::class);
        $oCountryList->loadActiveCountries(Registry::getLang()->getObjectTplLanguage());
        $this->_aViewData["countrylist"] = $oCountryList;

        return $ret;
    }

    public function changeShipcloudPackage()
    {
    }

    public function getComputedPackageWeight($additional)
    {
        $order = $this->getOrder();
        $order_articles = $order->getOrderArticles();
        $weight = 0;
        foreach ($order_articles as $order_article) {
            $ysshipcloud2orderarticles = oxNew(Shipcloud2Orderarticle::class);
            $ysshipcloud2orderarticles->loadEnhanced(
                $order_article->oxorderarticles__oxid->value,
                $this->getEditObjectId()
            );

            $current_amount = $ysshipcloud2orderarticles->ysshipcloud2orderarticles__oxamount->value;
            $weight += $order_article->oxorderarticles__oxweight->value * $current_amount;
        }
        $weight += floatval($additional);

        return $weight;
    }

    /**
     * Add additional information to order articles∞
     * (ysshipcloud2orderarticles id, active, current_amount, available_amount, ...),
     *
     *
     * @return array
     */
    public function getOrderArticlesEnhanced(): array
    {
        $order = $this->getOrder();
        $order_articles = $order->getOrderArticles();

        $ysorderarticles = [];

        foreach ($order_articles as $order_article) {
            /** @var Shipcloud2Orderarticle $s2oa */
            $s2oa = oxNew(Shipcloud2Orderarticle::class);
            $s2oa->loadEnhanced($order_article->oxorderarticles__oxid->value, $this->getEditObjectId());

            $queryBuilder = $this->queryBuilderFactory->create();
            $sum_send = $queryBuilder
                ->select('sum(s2oa.oxamount) as sm')
                ->from('ysshipcloud2orderarticles', 's2oa')
                ->innerJoin('s2oa', 'ysshipcloud', 's', 's2oa.YSSHCLID = s.OXID')
                ->andWhere('s2oa.OXOARTID = :article_id')
                ->andWhere("s.YSCARRIERSERVICE != 'returns'")
                ->setParameter("article_id", $order_article->oxorderarticles__oxid->value)
                ->execute()->fetch()["sm"];
            if (!$sum_send) {
                $sum_send = 0;
            }

            $sum = $order_article->oxorderarticles__oxamount->value;
            $current_amount = $s2oa->ysshipcloud2orderarticles__oxamount->value;
            $max_amount_available = $sum - ($sum_send - $current_amount);
            $ysorderarticles[] = [
                'oxid'                 => $s2oa->ysshipcloud2orderarticles__oxid->value,
                'active'               => $current_amount > 0,
                'current_amount'       => $current_amount,
                'max_amount_available' => $max_amount_available,
                'aio'                  => $sum - $max_amount_available,
                'max_amount'           => $sum,
                'order_article'        => $order_article,
            ];
        }
        return $ysorderarticles;
    }

    public function getDefaultCarrier(): string
    {
        $order = $this->getOrder();
        $delivery_set = $order->oxorder__oxdeltype->value;

        $packageTemplateKey = $this->shipcloud->ysshipcloud__yspackagetemplate->value;
        if ($packageTemplateKey) {
            $packageTemplate = $this->getPackageTemplates($packageTemplateKey);
            if ($packageTemplate and $packageTemplate['delvset']) {
                $delvset = $packageTemplate['delvset'];
                if ($delvset[$delivery_set]['carrier']) {
                    return $delvset[$delivery_set]['carrier'];
                }
            }
        }

        $delvset = $this->adminSettings->getSettings('delvset', 'main_settings');
        if ($delvset[$delivery_set]['carrier']) {
            return $delvset[$delivery_set]['carrier'];
        }

        return "";
    }

    public function getPackageTemplates($packageTemplateKey = null)
    {
        if (!$this->packageTemplates) {
            $this->packageTemplates = $this->adminSettings->getSettings('package_templates', 'package_templates');
        }
        if ($packageTemplateKey and $this->packageTemplates) {
            foreach ($this->packageTemplates as $packageTemplate) {
                if ($packageTemplate['name'] == $packageTemplateKey) {
                    return $packageTemplate;
                }
            }
        }
        return $this->packageTemplates;
    }

    /**
     *
     * Prepares default service
     * 1 Priority package_template settings
     * 2 Priority default setting
     *
     * @return mixed
     */
    public function getDefaultService(): string
    {
        $order = $this->getOrder();
        $delivery_set = $order->oxorder__oxdeltype->value;
        $packageTemplateKey = $this->shipcloud->ysshipcloud__yspackagetemplate->value;
        if ($packageTemplateKey) {
            $packageTemplate = $this->getPackageTemplates($packageTemplateKey);
            if ($packageTemplate and $packageTemplate['delvset']) {
                $delvset = $packageTemplate['delvset'];
                if ($delvset[$delivery_set]['service']) {
                    return $delvset[$delivery_set]['service'];
                }
            }
        }

        $delvset = $this->adminSettings->getSettings('delvset', 'main_settings');
        if ($delvset[$delivery_set]['service']) {
            return $delvset[$delivery_set]['service'];
        }

        return "";
    }

    public function getDefaultReturnAddressId(): string
    {
        # $order = $this->getOrder();
        # if ($order->getFieldData('GN_UNITE_ORDER') === '...') {
        #    return "...";
        # }
        return "";
    }
}
