<?php

use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\OrderingBundle\Entity\Purchase\PurchaseState;
use KupShop\OrderingBundle\Event\OrderItemEvent;
use KupShop\OrderingBundle\Util\Order\OrderInfo;

/**
 * Class PosCart.
 */
class PosCartBase extends Cart
{
    /** @var \Symfony\Component\HttpFoundation\Session\Session */
    private $session;

    /**
     * Fetching functions.
     */
    protected function fetchProducts()
    {
        global $dbcfg, $cfg;

        $this->session = ServiceContainer::getService('session');

        $this->totalPriceNoVat = DecimalConstants::zero();
        $this->totalPriceWithVat = DecimalConstants::zero();

        $where = queryCreate($this->getSelectParams(), true);

        $fields = 'c.id, c.pieces, c.id_variation, c.note, p.id as id_product, p.title, p.price, p.producer as id_producer, pr.name as producer, p.discount, COALESCE(pv.in_store, p.in_store) as in_store, p.vat,
						FIND_IN_SET("Z", p.campaign)>0 free_shipping, COALESCE(pv.delivery_time, p.delivery_time) as delivery_time, COALESCE(pv.ean, p.ean) as ean ';
        $from = 'cart AS c
					LEFT JOIN products AS p ON c.id_product=p.id
					LEFT JOIN producers AS pr ON pr.id=p.producer
					LEFT JOIN products_variations AS pv ON c.id_variation=pv.id
               LEFT JOIN photos_products_relation ppr ON p.id=ppr.id_product AND ppr.show_in_lead="Y"';

        if (findModule('products_variations', 'variationCode')) {
            $fields .= ', pv.code as variation_code';
        }

        $SQL = sqlQuery("SELECT {$fields}
					FROM {$from}
					WHERE {$where}
					GROUP BY c.id
					ORDER BY c.id");

        foreach ($SQL as $row) {
            $product = new Product($row['id_product']);

            $product->createFromDB($product->id);

            $product->cart_item_id = $row['id'];

            $dec_Pieces = Decimal::fromString("{$row['pieces']}");
            $product->pieces = $row['pieces'];

            $product->note = $product->parseNote($row['note']);
            $product->inStore = $row['in_store'];

            $IDvariation = $product->id_variation = $row['id_variation'];

            if (!is_null($IDvariation)) {
                $product->title = Variations::fillInProductTitle(intval($IDvariation), $product->title);
            }

            if ($IDvariation) {
                $product->ean = returnSQLResult('SELECT ean FROM products_variations WHERE id=:id', ['id' => $IDvariation]);
                if (findModule(Modules::PRODUCTS_VARIATIONS, Modules::SUB_CODE)) {
                    $product->code = returnSQLResult('SELECT code FROM products_variations WHERE id=:id', ['id' => $IDvariation]);
                }
            }

            if (empty($row['id_product'])) {
                if (!empty($product->note['title'])) {
                    $product['title'] = $product->note['title'];
                }

                if (!empty($product->note['vat'])) {
                    $product['vat'] = getVal('vat', $product->note, 0);
                }

                if (!empty($product->note['piece_price'])) {
                    $product->priceRaw = toDecimal($product->note['piece_price']);
                }
            }
            $price = $product->getPrice($IDvariation, $product->note, $dec_Pieces);

            $product->discount = getVal('discount', $product->note, $row['discount']);

            // nove promenne s cenou
            $priceArray = formatCustomerPrice($price, (float) $product->discount, getVat($row['vat']), $product->id);

            $price_with_vat_rounded = roundPrice($priceArray['value_with_vat']);
            $price_without_vat_rounded = calcPrice($price_with_vat_rounded, -getVat($row['vat']));
            $item_price_with_vat = $price_with_vat_rounded->mul($dec_Pieces);
            $item_price_without_vat = calcPrice($item_price_with_vat, -getVat($row['vat']));
            $totalPriceArray = formatPrice($item_price_without_vat, getVat($row['vat']));

            /* TODO rozbije to automotovelo - zaokrouhlovani na cely cisla, 122 * 3pcs -10% sleva */
            $priceArray['value_without_vat'] = $price_without_vat_rounded;

            $product->price = $priceArray;
            $product->totalPrice = $totalPriceArray;
            // celkova cena bez DPH
            $this->totalPriceNoVat = $this->totalPriceNoVat->add($price_without_vat_rounded->mul($dec_Pieces));

            // celkova cena s DPH
            $this->totalPriceWithVat = $this->totalPriceWithVat->add($price_with_vat_rounded->mul($dec_Pieces));

            $vat = $row['vat'];

            // ceny podle DPH, vlozit cenu s DPH
            if (!isset($this->priceByVat[$vat])) {
                $this->priceByVat[$vat] = DecimalConstants::zero();
            }

            $this->priceByVat[$vat] = $this->priceByVat[$vat]->add($price_without_vat_rounded->mul($dec_Pieces));

            // zapocitat celkovy pocet kusu
            $this->totalPieces += $row['pieces'];

            $this->products[$product->cart_item_id] = $product;
        }
        sqlFreeResult($SQL);
    }

    public static function findDeliveryTypeByPayMethod($id_method)
    {
        switch ($id_method) {
            case Payment::METHOD_CASH:
                $payment_class = 'Hotovost';
                break;
            case Payment::METHOD_CARD:
                $payment_class = 'PlatebniKarta';
                break;
            case Payment::METHOD_INVOICE:
                $payment_class = 'Prevodem';
                break;
            default:
                throw new Exception('POS: undefined pay method !!');
        }

        $delivery_type = sqlQueryBuilder()
            ->select("dt.id as id, CONCAT_WS(' - ', dtp.name, dtd.name) as name")
            ->from('delivery_type', 'dt')
            ->join('dt', 'delivery_type_delivery', 'dtd', 'dt.id_delivery = dtd.id AND dtd.class = :delivery_class')
            ->join('dt', 'delivery_type_payment', 'dtp', 'dt.id_payment = dtp.id  AND dtp.class = :payment_class')
            ->setParameters([
                'payment_class' => $payment_class,
                'delivery_class' => 'OsobniOdber',
            ])
            ->execute()
            ->fetchAll();

        if (!$delivery_type) {
            throw new Exception('POS: id_delivery_type not found!!');
        }

        return $delivery_type[0];
    }

    // Order Submission
    public function submitOrder($languageID = null)
    {
        global $cfg, $ctrl, $dbcfg, $adminID;

        $error = $this->checkCart();

        if (empty($error)) {
            $order = new Order();

            sqlStartTransaction();

            $fields = [
                'id_user' => null,
                'order_no' => '_'.rand(0, 999999),
                'status' => 0,
                'note_user' => $this->note,
                'invoice_name' => 'Zákazník',
                'delivery_type' => 'Kamenný obchod',
                'date_created' => date('Y-m-d H:i'),
                'date_updated' => date('Y-m-d H:i'),
                'source' => OrderInfo::ORDER_SOURCE_POS,
                'id_delivery' => $this->transport,
            ];

            $SQL = $this->insertSQL('orders', $fields);
            // TODO: non null - ["invoice_firm", "invoice_ico", "invoice_dic", "delivery_firm", "note_user", "invoice_country", "delivery_country"]

            if ($SQL) {
                // ID nove ulozene objednavky
                $IDorder = sqlInsertId();

                // Generate order ID
                $orderNo = Orders::createOrderNumber($IDorder);

                if (empty($orderNo)) {
                    logError(__FILE__, __LINE__, 'Empty orderNo!');
                }

                // Store generated order ID
                $this->updateSQL('orders', [
                    'order_no' => $orderNo,
                    'admin' => $adminID ? $adminID : null,
                ], ['id' => $IDorder]);

                // Create Order object
                $order->createFromDB($IDorder, true, true, true);

                // Set status indicating order creation
                $order->status = -1;

                // ulozit vybrane zbozi do objednavky
                $products = [];

                $cfg['Order']['hideCode'] = true;
                foreach ($this->products as $product) {
                    $products[] = $product;

                    $discount = getVal('discount', $product->note, 0);

                    $fields = [
                        'discount' => $discount,
                    ];

                    if (empty($product->id)) {
                        $res = $order->insertNonItem(
                            $order->id,
                            $product->price['value_without_vat']->mul(toDecimal($product->pieces)),
                            $product->price['value_without_vat'],
                            $product->pieces,
                            $product->price['vat'],
                            $product['title']
                        );
                    } else {
                        $res = $order->insertItem($product->id, $product->id_variation, $product->pieces, '', false);
                        $fields['piece_price'] = $product->price['value_without_vat'];
                        $fields['total_price'] = $product->price['value_without_vat']->mul(toDecimal($product->pieces));
                        $dispatcher = ServiceContainer::getService('event_dispatcher');
                        $event = $dispatcher->dispatch(
                            new OrderItemEvent($product, $product->id_variation, null, $product->pieces, null, $order),
                            OrderItemEvent::ITEM_UPDATED
                        );
                    }

                    $this->updateSQL('order_items',
                        $fields, ['id' => $res]);

                    if (false === $res) {
                        logError(__FILE__, __LINE__, "Order->insertItem failed: insertItem({$product->id}, {$product->id_variation}, {$product->pieces}, {$product->note}) cart item: {$product->cart_item_id}");

                        $_REQUEST['IDp'] = $product->id;

                        return 31;
                    }

                    // vymazat zbozi z kosiku
                    $this->deleteSQL('cart', ['id' => $product->cart_item_id]);
                }

                $this->clear();

                $order->recalculate(true);

                sqlFinishTransaction();

                $order->logHistory('Vytvořeno v pokladně '.date(Settings::getDateFormat().' '.Settings::getTimeFormat(), time()));

                return $order;
            } else {
                $error = 2;
            }
        }

        return $error;
    }

    public function addNonItem($params)
    {
        return $this->insertSQL('cart', $params);
    }

    public function updateNonItem($params)
    {
        return $this->updateSQL('cart', ['pieces' => $params['pieces'], 'note' => $params['note']], ['id' => $params['id']]);
    }

    public static function getVats()
    {
        if (!empty($vats)) {
            return $vats;
        }

        $SQL = sqlQuery('SELECT id, descr, vat, is_default
						FROM '.getTableName('vats').'
						ORDER BY vat DESC ');
        static $vats = [];

        foreach ($SQL as $key => $row) {
            $vats[$row['id']] = $row;
        }

        return $vats;
    }

    public static function getVatByID($id_vat)
    {
        $vats = self::getVats();

        if (!empty($vats[$id_vat]['vat'])) {
            return $vats[$id_vat]['vat'];
        } else {
            return 0;
        }
    }

    public function getSelectParams($alias = '')
    {
        $userKey = self::getCartID();

        return ['user_key' => $userKey];
    }

    protected function checkCart()
    {
        // Check cart is not empty
        $where = queryCreate($this->getSelectParams(), true);
        $products = returnSQLResult('SELECT COUNT(c.id)
               FROM '.getTableName('cart').' AS c
               LEFT JOIN '.getTableName('products')." AS p ON c.id_product=p.id
               WHERE {$where}");

        if (0 == intval($products)) {
            return 12;
        }

        // Check all items are in store if required
        return $this->checkCartItems();
    }

    public function checkCartItems(&$errors = null)
    {
        global $dbcfg, $cfg;

        $where = queryCreate($this->getSelectParams(), true);

        $SQL = sqlQuery("SELECT p.id as id_product
               FROM cart AS c
               LEFT JOIN products AS p ON c.id_product=p.id
               LEFT JOIN products_variations pv ON pv.id_product = p.id
               WHERE {$where} AND c.id_variation IS NULL AND pv.id IS NOT NULL
               GROUP BY c.id");
        if (sqlNumRows($SQL) > 0) {
            logError(__FILE__, __LINE__, 'WARN: Není vybrana varianta');
            $this->errors[self::$ERR_NOT_SELECTED_VARIATION] = $errors = sqlFetchAll($SQL, ['id_product' => 'id_product']);

            return 31;
        }

        // Check for products not on stock
        if (\Settings::ORDER_AVAILABILITY_ALL !== $dbcfg->order_availability) {
            $where = queryCreate($this->getSelectParams(), true);

            if (findModule('products', 'showMax')) {
                $where .= ' AND (COALESCE(pv.in_store, p.in_store) < c.pieces OR c.pieces > COALESCE(pv.in_store_show_max, p.in_store_show_max, '.$cfg['Modules']['products']['showMax'].'))';
            } else {
                $where .= ' AND COALESCE(pv.in_store, p.in_store) < c.pieces';
            }

            $failedProducts = sqlFetchAll(sqlQuery("SELECT p.id, pv.id as id_variation
		         FROM cart AS c
		         LEFT JOIN products AS p ON c.id_product=p.id
		         LEFT JOIN products_variations AS pv ON c.id_variation=pv.id
					WHERE {$where} AND p.id IS NOT NULL"),
                ['id' => 'id_variation']);

            if (count($failedProducts) > 0) {
                $this->errors[self::$ERR_OUT_OF_STOCK] = $errors = $failedProducts;

                return 30;
            }
        }

        return null;
    }

    public function getPurchaseState(): PurchaseState
    {
        $cart = clone $this;
        $cart->products = array_filter($cart->products, function ($product) {
            return !empty($product['product']);
        });

        return $this->purchaseUtil->createStateFromCart($cart)->setSource(OrderInfo::ORDER_SOURCE_POS);
    }
}

class POSInternalException extends Exception
{
}

if (empty($subclass)) {
    class PosCart extends PosCartBase
    {
    }
}
