<?php

namespace KupShop\OrderingBundle\Util\Order;

use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\OrderingBundle\Event\OrderEvent;
use Query\Operator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class OrderImporter
{
    /** @required */
    public EventDispatcherInterface $eventDispatcher;

    public function importOrders($xml, $edit, $insert = true)
    {
        if ($xml instanceof \DOMDocument) {
            $orders = simplexml_import_dom($xml);
        } else {
            $orders = simplexml_load_string($xml);
        }
        unset($xml);

        foreach ($orders as $orderElement) {
            $this->importOrder($orderElement, $edit, $insert);
        }
    }

    protected function getProductVariationByCode($code)
    {
        return sqlQueryBuilder()
            ->select('pv.id as id_variation, pv.id_product as id_product, pv.title as title_variation, p.title as title_product')
            ->from('products_variations', 'pv')
            ->leftJoin('pv', 'products', 'p', 'p.id=pv.id_product')
            ->andWhere('pv.code=:product_code')
            ->setParameter('product_code', $code)
            ->execute()
            ->fetch();
    }

    protected function getProductByCode($code)
    {
        return sqlQueryBuilder()
            ->select('id as id_product, title as title_product')
            ->from('products')
            ->andWhere('code=:product_code')
            ->setParameter('product_code', $code)
            ->execute()
            ->fetch();
    }

    protected function addOrderAdditionalFields(&$orderRow, $orderElement)
    {
    }

    protected function addInsertFieldItem(&$itemData, $item)
    {
    }

    protected function searchOrder($order)
    {
        return sqlQueryBuilder()
            ->select('id')
            ->from('orders')
            ->andWhere('id=:orderId OR order_no=:order_code')
            ->setParameter('orderId', (string) $order->ID)
            ->setParameter('order_code', (string) $order->ORDER_NO)
            ->execute()
            ->fetchColumn();
    }

    protected function searchUser($order)
    {
        return sqlQueryBuilder()
            ->select('id')
            ->from('users')
            ->andWhere(Operator::equals(['id' => (string) $order->USER->ID]))
            ->execute()
            ->fetchColumn();
    }

    protected function getVariationAndProductIds($item)
    {
        if (findModule(\Modules::PRODUCTS_VARIATIONS)) {
            $qbVariation = $this->getProductVariationByCode((string) $item->CODE);
            if ($qbVariation) {
                return $qbVariation;
            }
        }

        $qbProduct = $this->getProductByCode((string) $item->CODE);
        if ($qbProduct) {
            $qbProduct['id_variation'] = null;
            $qbProduct['title_variation'] = null;

            return $qbProduct;
        }

        return null;
    }

    protected function searchDelivery($order)
    {
        $delData = sqlFetch(
            sqlQuery('
                SELECT dt.id as id, dtp.name as payment_name, dtd.name as delivery_name FROM delivery_type dt
                    LEFT JOIN delivery_type_delivery dtd ON dt.id_delivery = dtd.id
                    LEFT JOIN delivery_type_payment dtp on dt.id_payment = dtp.id
                    WHERE dtd.id=:deliveryId AND dtp.id=:paymentId',
                [
                    'deliveryId' => (string) $order->DELIVERY->attributes()['id'],
                    'paymentId' => (string) $order->PAYMENT->attributes()['id'],
                ]
            )
        );

        return [$delData['delivery_name'], $delData['id'], $delData['payment_name']];
    }

    protected function updateItem($itemData, $item)
    {
        sqlQueryBuilder()
            ->update('order_items')
            ->directValues($itemData)
            ->andWhere('id=:id')
            ->setParameter('id', (string) $item->attributes()['id'])
            ->execute();
    }

    protected function getExistsItems($orderId)
    {
        return sqlFetchAll(
            sqlQueryBuilder()
                ->select('*')
                ->from('order_items')
                ->andWhere('id_order=:id')
                ->setParameter('id', $orderId)
                ->execute(),
            'id'
        );
    }

    protected function existsItemInArray($selectedItems, $item)
    {
        return sqlQueryBuilder()
            ->select('id')
            ->from('order_items')
            ->andWhere(Operator::equals(['id' => $item->attributes()['id']]))
            ->execute()
            ->fetchColumn();
    }

    protected function afterInsertAction($orderElement, $order)
    {
    }

    protected function afterOrderUpdateAction($orderElement, $order)
    {
    }

    /**
     * @param \SimpleXMLElement $orderElement
     *
     * @return array[]|void
     */
    public function importOrder($orderElement, $edit, $insert)
    {
        if ($orderElement instanceof \DOMDocument) {
            $orderElement = simplexml_import_dom($orderElement);
        }

        $new_order = false;
        $orderID = $this->searchOrder($orderElement);
        if (!empty($orderID) && !$edit) {
            $this->resolveOrder($orderElement, $orderID);

            return;
        }

        if (empty($orderID) && !$insert) {
            return;
        }

        if (!$orderID) {
            $new_order = true;
        }

        $orderRow = $this->getOrderBaseData($orderElement, $new_order);

        $idUser = $this->searchUser($orderElement);
        if ($idUser) {
            $orderRow['id_user'] = $idUser;
        }

        if (!$insert && $orderElement->NOTE_ADMIN) {
            unset($orderRow['note_admin']);
        }

        list($delName, $delId, $payName) = $this->searchDelivery($orderElement);
        if (!empty($delName) && !empty($delId) && !empty($payName)) {
            $orderRow['delivery_type'] = $payName.' - '.$delName;
            $orderRow['id_delivery'] = $delId;
        }

        $this->addOrderAdditionalFields($orderRow, $orderElement);

        if (!empty($orderID)) {
            sqlQueryBuilder()
                ->update('orders')
                ->directValues($orderRow)
                ->andWhere('id=:orderID')
                ->setParameter('orderID', $orderID)
                ->execute();
        } else {
            sqlQueryBuilder()
                ->insert('orders')
                ->directValues($orderRow)
                ->execute();
            $orderID = sqlInsertId();
        }

        $order = new \Order();
        $order->createFromDB($orderID);

        if ($new_order) {
            $this->eventDispatcher->dispatch(new OrderEvent($order), OrderEvent::ORDER_CREATED);
        }

        $selectedItems = $this->getExistsItems($orderID);

        foreach ($orderElement->ITEMS->ITEM ?? [] as $item) {
            $itemData = [];
            $itemData['pieces'] = (string) $item->PIECES;
            $itemData['piece_price'] = (string) $item->PIECE_PRICE;
            $itemData['total_price'] = (string) $item->TOTAL_PRICE;
            $itemData['tax'] = (string) $item->VAT;

            $itemId = ($edit) ? $this->existsItemInArray($selectedItems, $item) : false;

            if (!empty($itemId)) {
                unset($selectedItems[$itemId]);
                $this->updateItem($itemData, $item);
            } else {
                $data = $this->getVariationAndProductIds($item);
                $itemData['id_variation'] = ($data) ? $data['id_variation'] : null;
                $itemData['id_product'] = ($data) ? $data['id_product'] : null;

                $itemData['id_order'] = $orderID;

                if ($data && $data['title_product'] && ($data['title_variation'] ?? false)) {
                    $itemData['descr'] = $data['title_product'].' - '.$data['title_variation'];
                } elseif ($data && $data['title_product']) {
                    $itemData['descr'] = $data['title_product'];
                } else {
                    $itemData['descr'] = (string) $item->NAME;
                }

                $this->addInsertFieldItem($itemData, $item);
                sqlQueryBuilder()
                    ->insert('order_items')
                    ->directValues($itemData)
                    ->execute();
            }
        }

        foreach ($selectedItems as $selectedItem) {
            sqlQueryBuilder()
                ->delete('order_items')
                ->andWhere('id=:notFoundID')
                ->setParameter('notFoundID', $selectedItem['id'])
                ->execute();
        }

        $contextManager = ServiceContainer::getService(ContextManager::class);
        $contextManager->activateOrder($order, function () use ($order, $new_order, $orderElement) {
            $order->recalculate();
            if ($new_order) {
                $this->eventDispatcher->dispatch(new OrderEvent($order), OrderEvent::ORDER_COMPLETE);

                $this->afterInsertAction($orderElement, $order);
            } else {
                $this->afterOrderUpdateAction($orderElement, $order);
            }
        });
    }

    public function resolveOrder($orderElement, $qbOrder)
    {
    }

    public function getOrderBaseData(\SimpleXMLElement $orderElement, bool $isNewOrder = true): array
    {
        $data = [];

        if ($isNewOrder) {
            $data['date_created'] = !empty((string) $orderElement->DATE_CREATED) ? (string) $orderElement->DATE_CREATED : (new \DateTime())->format('Y-m-d H:i:s');
            if ((string) $orderElement->ORDER_NO) {
                $data['order_no'] = (string) $orderElement->ORDER_NO;
            }
            if ((string) $orderElement->DATE_UPDATE) {
                $data['date_updated'] = (string) $orderElement->DATE_UPDATE;
            }
            if ((string) $orderElement->DATE_HANDLE) {
                $data['date_handle'] = (string) $orderElement->DATE_HANDLE;
            }
            if ((string) $orderElement->DATE_DELIVERED) {
                $data['date_delivered'] = (string) $orderElement->DATE_DELIVERED;
            }
            if ((string) $orderElement->CURRENCY_RATE) {
                $data['currency_rate'] = (string) $orderElement->CURRENCY_RATE;
            }
            if ((string) $orderElement->CURRENCY) {
                $data['currency'] = (string) $orderElement->CURRENCY;
            }

            $data['status'] = 0;
        }

        if ((string) $orderElement->ID_LANGUAGE) {
            $data['id_language'] = (string) $orderElement->ID_LANGUAGE;
        }

        if ((string) $orderElement->STATUSES->STATUS) {
            $data['status'] = (string) $orderElement->STATUSES->STATUS;
        }

        $data['status_payed'] = 0;
        if ((string) $orderElement->STATUS_PAID) {
            $data['status_payed'] = 1;
        }

        // TOTAL_PRICE=0 => RECALCULATE
        $data['total_price'] = 0.0000;

        // INVOICE
        $data['invoice_name'] = (string) $orderElement->INVOICE_ADDRESS->NAME;
        $data['invoice_surname'] = (string) $orderElement->INVOICE_ADDRESS->SURNAME;
        $data['invoice_firm'] = (string) $orderElement->INVOICE_ADDRESS->FIRM;
        $data['invoice_ico'] = (string) $orderElement->INVOICE_ADDRESS->ICO;
        $data['invoice_dic'] = (string) $orderElement->INVOICE_ADDRESS->DIC;
        $data['invoice_street'] = (string) $orderElement->INVOICE_ADDRESS->STREET;
        $data['invoice_city'] = (string) $orderElement->INVOICE_ADDRESS->CITY;
        $data['invoice_zip'] = (string) $orderElement->INVOICE_ADDRESS->ZIP;
        $data['invoice_country'] = (string) $orderElement->INVOICE_ADDRESS->COUNTRY;
        $data['invoice_phone'] = (string) $orderElement->INVOICE_ADDRESS->PHONE;
        $data['invoice_email'] = (string) $orderElement->INVOICE_ADDRESS->EMAIL;

        // DELIVERY
        $data['delivery_name'] = (string) $orderElement->DELIVERY_ADDRESS->NAME;
        $data['delivery_surname'] = (string) $orderElement->DELIVERY_ADDRESS->SURNAME;
        $data['delivery_firm'] = (string) $orderElement->DELIVERY_ADDRESS->FIRM;
        $data['delivery_street'] = (string) $orderElement->DELIVERY_ADDRESS->STREET;
        $data['delivery_city'] = (string) $orderElement->DELIVERY_ADDRESS->CITY;
        $data['delivery_zip'] = (string) $orderElement->DELIVERY_ADDRESS->ZIP;
        $data['delivery_country'] = (string) $orderElement->DELIVERY_ADDRESS->COUNTRY;
        $data['delivery_phone'] = (string) $orderElement->DELIVERY_ADDRESS->PHONE;

        if ($isNewOrder && $orderElement->NOTE_ADMIN) {
            $data['note_admin'] = json_encode($orderElement->NOTE_ADMIN);
        }

        return $data;
    }
}
