<?php

namespace KupShop\CheckAppBundle\Util;

use KupShop\CheckAppBundle\Exception\OrderCheckException;
use KupShop\ProductsBatchesBundle\Util\ProductsBatchesUtil;
use KupShop\ProductsSerialNumbersBundle\Util\ProductsSerialNumbersUtil;
use KupShop\ReturnsBundle\Util\ReturnsUtil;
use Query\Operator;

class ReturnCheckoutWorker
{
    /** @var ReturnsUtil|null */
    private $returns;

    /** @var ProductsSerialNumbersUtil|null */
    private $productsSerialNumbersUtil;

    /** @var ProductsBatchesUtil|null */
    private $productsBatchesUtil;

    /** @var WorkerUtil */
    protected $workerUtil;

    /**
     * @required
     */
    public function setWorkerUtil(WorkerUtil $workerUtil): void
    {
        $this->workerUtil = $workerUtil;
    }

    /**
     * @required
     */
    public function setReturnsUtil(?ReturnsUtil $returns = null)
    {
        $this->returns = $returns;
    }

    /**
     * @required
     */
    public function setProductsSerialNumbersUtil(?ProductsSerialNumbersUtil $productsSerialNumbersUtil = null)
    {
        $this->productsSerialNumbersUtil = $productsSerialNumbersUtil;
    }

    /**
     * @required
     */
    public function setProductsBatchesUtil(?ProductsBatchesUtil $productsBatchesUtil)
    {
        $this->productsBatchesUtil = $productsBatchesUtil;
    }

    public function getReturnCheckData($id_return)
    {
        $data = [];
        foreach ($this->returns->getReturnItems($id_return) as $item) {
            $product = new \Product();
            $product->createFromDB($item['id_product']);

            $additionalData = $this->getAdditionalData($item['id_product'], $item['id_variation']);

            $productData = sqlQueryBuilder()
                ->select('COALESCE(pv.ean, p.ean) ean, COALESCE(pv.code, p.code) code, pv.title title')
                ->from('products', 'p')
                ->joinVariationsOnProducts()
                ->where(Operator::equalsNullable([
                    'p.id' => $item['id_product'],
                    'pv.id' => $item['id_variation'],
                ]))
                ->addSelect(\Query\Product::withProductPhotoId(true))
                ->execute()->fetch();

            $data[] = [
                'id' => $item['id'],
                'id_item' => $item['id_item'],
                'title' => $item['descr'].' '.$productData['title'],
                'quantity' => (int) $item['pieces'],
                'code' => ($productData['code'] ?? ''),
                'ean' => formatEAN($productData['ean']),
                'id_product' => $item['id_product'],
                'id_variation' => $item['id_variation'],
                'note' => $product->note_ ?? '',
                'serial_number_require' => $additionalData['serial_number_require'] ?? '',
                'batch_number_require' => $additionalData['batch_number_require'] ?? '',
                'batches_numbers' => $additionalData['batches_numbers'] ?? [],
                'image' => getImage($productData['id_photo'], null, null, 3)['src'] ?? '',
                'positions' => $additionalData['positions'] ?? '',
                'supplier_eans' => explode(';', $additionalData['supplier_eans'] ?? ''),
                'supplier_codes' => explode(';', $additionalData['supplier_codes'] ?? ''),
                'figure' => $product->visible,
            ];
        }

        usort($data, function ($a, $b) {
            return empty($b['ean']) <=> empty($a['ean']);
        });

        $index = 0;
        foreach ($data as &$item) {
            $item['index'] = $index++;
        }

        return $data;
    }

    public function checkoutReturn($id_return, $items): array
    {
        if ($error = $this->checkReturn((int) $id_return, $items)) {
            return [
                'result' => false,
                'keepState' => true,
                'message' => $error,
            ];
        }

        sqlGetConnection()->transactional(function () use ($id_return, $items) {
            // update return_items by checked items
            foreach ($items as $item) {
                if (!($item->checked ?? false)) {
                    // remove item
                    sqlQueryBuilder()->delete('return_items')
                        ->where(Operator::equals(['id' => $item->id]))
                        ->execute();
                }
            }

            $this->returns->logHistory($id_return, 'Proběhla kontrola vratky skrze vstupní kontrolu ', false, 1);
            $this->returns->changeStatus($id_return, 1);
        });

        return [
            'result' => true,
            'message' => 'Vratka zkontrolována',
        ];
    }

    public function checkReturn(int $id_return, array $items): ?string
    {
        return $this->workerUtil->checkIfAnyItemChecked($items);
    }

    protected function getAdditionalData($id_product, $id_variation)
    {
        return $this->workerUtil->getProductAdditionalData($id_product, $id_variation);
    }

    public function checkoutReturnSerialNumbers($items)
    {
        $items = array_filter($items ?? [], function ($item) {
            return $item->serial_number_require === 'Y';
        });

        sqlGetConnection()->transactional(
            function () use ($items) {
                $serialNumbers = [];
                foreach ($items as $item) {
                    if (count($item->serialNumbers) < $item->checked) {
                        throw new OrderCheckException('Produkt s id '.$item->id_product.' má méně SN než je nakliknuto.');
                    }
                    if (count($item->serialNumbers) > $item->checked) {
                        throw new OrderCheckException('Produkt s id '.$item->id_product.' má více SN než je nakliknuto.');
                    }
                    if (count($item->serialNumbers) > count(array_unique($item->serialNumbers))) {
                        throw new OrderCheckException('Produkt s ID '.$item->id_product.' má duplicitní SN.');
                    }

                    $orderItemSerialNumbers = sqlFetchAll(
                        sqlQueryBuilder()->select('psn.*, oi.id_order')
                            ->from('products_serial_numbers', 'psn')
                            ->join('psn', 'order_items', 'oi', 'psn.id_order_item = oi.id')
                            ->where(Operator::equals(['oi.id' => $item->id_item]))
                            ->execute(), 'serial_number');

                    if (empty($orderItemSerialNumbers)) {
                        // u polozky v puvodni objednavce nebyly zadane seriove cisla => neni co kontrolovat a reaktivovat
                        continue;
                    }

                    // u polozky v puvodni objednavce byly zadane seriove cisla => musi vratit jedno z nich
                    foreach ($item->serialNumbers as $serialNumber) {
                        if (array_key_exists($serialNumber, $orderItemSerialNumbers)) {
                            $serialNumbers[] = $orderItemSerialNumbers[$serialNumber];
                        } else {
                            $SN = reset($orderItemSerialNumbers);
                            $orderID = $SN['id_order'];
                            throw new OrderCheckException(
                                "Špatné sériové číslo: <br>v původní objednávce ID {$orderID} nebyl produkt s ID {$item->id_product} a SN \"{$serialNumber}\"."
                            );
                        }
                    }
                }

                $this->productsSerialNumbersUtil->stornoProductsSerialNumbers($serialNumbers, 'return');
            });
    }

    // insert batch numbers into return_items custom_data, create batch number if not already exists
    public function checkoutReturnBatchNumbers($items)
    {
        foreach ($items as $item) {
            if (($item->batch_number_require ?? false) != 'Y') {
                continue;
            }

            $idBatch = $item->batchNumber->id ?? false;

            if (!$idBatch) {
                if (empty($item->batchNumber->code)) {
                    continue;
                }

                $batchNumber = $this->productsBatchesUtil->createBatchNumber([
                    'id_product' => $item->id_product,
                    'id_variation' => $item->id_variation,
                    'code' => $item->batchNumber->code,
                    'date_expiry' => $item->batchNumber->date_expiry,
                ]);
                $idBatch = $batchNumber['id'];
            }
            $this->productsBatchesUtil->clearBatchesCustomData('return_items', ['id' => $item->id], 'data');
            $this->productsBatchesUtil->setBatchesToCustomData('return_items',
                ['id' => $item->id],
                [$idBatch => (int) $item->checked], 'data');
        }
    }
}
