<?php

use KupShop\AdminBundle\AdminList\BaseList;
use KupShop\AdminBundle\AdminList\FiltersStorage;
use KupShop\AdminBundle\Query\Invert;
use KupShop\AdminBundle\Util\ListExporter;
use KupShop\CatalogBundle\Query\Search;
use KupShop\CatalogBundle\Util\ProductsFilterSpecs;
use KupShop\KupShopBundle\Query\JsonOperator;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\HtmlBuilder\HTML;
use KupShop\ReturnsBundle\Util\ReturnsUtil;
use Query\Operator;

class ReturnsList extends BaseList
{
    use FiltersStorage;

    protected $showMassEdit = true;
    protected $tableName = 'returns';
    protected bool $useLazyNumberOfPages = true;

    protected ?string $tableAlias = 'r';

    protected $orderParam = [
        'sort' => 'number',
        'direction' => 'DESC',
    ];
    protected $tableDef = [
        'id' => 'r.id',
        'fields' => [
            'number' => ['field' => 'r.id', 'translate' => true, 'render' => 'renderCode'],
            'date_created' => ['field' => 'r.date_created', 'translate' => true, 'render' => 'renderDateTime'],
            'date_accepted' => ['field' => 'r.date_accepted', 'translate' => true, 'render' => 'renderDateTime'],
            'handled' => ['field' => 'r.date_handle', 'translate' => true, 'render' => 'renderDateTime', 'visible' => 'N'],
            'status' => ['field' => 'r.status', 'translate' => true, 'render' => 'renderStatus', 'spec' => 'r.status', 'fieldType' => ReturnsList::TYPE_LIST],
            'customer' => ['field' => 'customer', 'spec' => null, 'translate' => true, 'render' => 'renderCustomer'],
            'order_number' => ['field' => 'orders_nos', 'spec' => null, 'translate' => true, 'render' => 'renderOrderId', 'class' => 'tiny-list-items', 'export' => 'exportOrderNo'],
            'return_items' => ['translate' => true, 'field' => 'items', 'render' => 'renderItems', 'class' => 'left tiny-list-items', 'size' => 2, 'visible' => 'N'],
            'return_reason' => ['translate' => true, 'field' => 'reason_ids', 'render' => 'renderReason', 'class' => 'left tiny-list-items', 'size' => 2, 'visible' => 'N'],
            'flags' => ['translate' => true, 'field' => 'r.flags', 'render' => 'renderFlags', 'class' => 'columnCampaigns product-campaigns', 'size' => 0.7, 'fieldType' => ReturnsList::TYPE_LIST_MULTISELECT],
            'return_label' => ['translate' => true, 'field' => 'return_label', 'render' => 'renderReturnLabel', 'visible' => 'Y', 'size' => 1],
            'label_url' => ['translate' => true, 'field' => 'label_url', 'render' => 'renderLabelUrl', 'visible' => 'Y', 'size' => 1],
            'user_note' => ['translate' => true, 'field' => 'user_note', 'render' => 'renderBoolean', 'visible' => 'Y', 'size' => 1],
            'total_price' => ['translate' => true, 'field' => 'total_price', 'render' => 'renderPrice', 'spec' => [], 'visible' => 'N', 'size' => 1],
            'do_not_stock_in' => ['translate' => true, 'field' => 'do_not_stock_in', 'render' => 'renderBoolean', 'spec' => [], 'visible' => 'N', 'size' => 1],
        ],
        'class' => 'getRowClass',
    ];

    protected $template = 'list/returnsList.tpl';

    /** @var ReturnsUtil */
    protected $returns;

    public function __construct()
    {
        $this->returns = ServiceContainer::getService(ReturnsUtil::class);
    }

    public function getSQLOrdering(&$var, &$orderParam)
    {
        parent::getSQLOrdering($var, $orderParam);

        if (isset($var['orderField']) && ($var['orderField'] == 'items')) {
            $var['orderField'] = '';
        }
    }

    public function customizeTableDef($tableDef)
    {
        global $cfg;

        $tableDef = parent::customizeTableDef($tableDef);

        $tableDef['fields']['Vratková objednávka'] = [
            'field' => 'r.id_order',
            'render' => 'renderReturnOrder',
            'spec' => function (Query\QueryBuilder $qb) {
                $qb->addSelect('ro.order_no as return_order_no')
                    ->leftJoin('r', 'orders', 'ro', 'ro.id = r.id_order');
            },
            'visible' => 'N',
        ];

        $tableDef['fields']['customer']['spec'] = function (Query\QueryBuilder $qb) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id')
                ->leftJoin('ri', 'order_items', 'oi', 'oi.id = ri.id_item')
                ->leftJoin('oi', 'orders', 'o', 'o.id = oi.id_order');
            $qb->addSelect('CONCAT(o.invoice_surname, " ", o.invoice_name) as customer');
        };

        $tableDef['fields']['order_number']['spec'] = function (Query\QueryBuilder $qb) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id')
                ->leftJoin('ri', 'order_items', 'oi', 'oi.id = ri.id_item')
                ->leftJoin('oi', 'orders', 'o', 'o.id = oi.id_order');
            $qb->addSelect('GROUP_CONCAT(o.order_no ORDER BY o.id) orders_nos, GROUP_CONCAT(o.id ORDER BY o.id) orders_ids');
        };

        $tableDef['fields']['return_reason']['spec'] = function (Query\QueryBuilder $qb) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id');
            $qb->addSelect('GROUP_CONCAT(ri.reason ORDER BY ri.id) reason_ids');
        };

        $tableDef['fields']['return_label']['spec'] = function (Query\QueryBuilder $qb) {
            $qb->addSelect(JsonOperator::value('r.data', 'returnDeliveryLabel.*.carrier_id').' as return_label');
            $qb->addSelect(JsonOperator::value('r.data', 'returnDeliveryLabel.*.track_url').' as track_url');
        };

        $tableDef['fields']['label_url']['spec'] = function (Query\QueryBuilder $qb) {
            $qb->addSelect(JsonOperator::value('r.data', 'returnDeliveryLabel.*.label_url').' as label_url');
        };

        $tableDef['fields']['user_note']['spec'] = function (Query\QueryBuilder $qb) {
            $qb->addSelect(JsonOperator::extract('r.history', '$[0].comment').' != "" as user_note');
        };

        $tableDef['fields']['total_price']['spec'] = function (Query\QueryBuilder $qb) {
            $qb->addSelect('r.total_price, o.currency')
                ->leftJoin('oi', 'orders', 'o', 'o.id = oi.id_order');
        };

        $tableDef['fields']['status']['fieldOptions'] = [
            2 => translate('handled', 'Returns'),
            1 => translate('accepted', 'Returns'),
            0 => translate('new', 'Returns'),
        ];

        if (!empty($cfg['Returns']['Flags'])) {
            foreach ($cfg['Returns']['Flags'] as $flag => $value) {
                $tableDef['fields']['flags']['fieldOptions'][$flag] = $value['name'];
            }
        }

        $tableDef['fields']['do_not_stock_in']['spec'] = function (\Query\QueryBuilder $qb) {
            $qb->addSelect("IF(JSON_VALUE(r.data, '$.doNotStockIn'), 'Y', 'N') as do_not_stock_in");
        };

        if (findModule(Modules::STORES)) {
            $tableDef['fields']['store'] = [
                'translate' => true,
                'field' => 'id_store',
                'render' => 'renderStore',
                'spec' => function (Query\QueryBuilder $qb) {
                    $qb->addSelect('JSON_VALUE(r.data, "$.id_store") as id_store');
                },
                'visible' => 'N',
            ];
        }

        return $tableDef;
    }

    public function getRowClass($values)
    {
        $class = '';
        if (in_array($values['status'], $this->returns->getHandleStatuses())) {
            $class = ' row-green';
        }

        if (in_array($values['status'], $this->returns->getAcceptedStatuses())) {
            $class = ' row-blue';
        }

        return $class;
    }

    public function renderStore(array $values)
    {
        $storesInStore = ServiceContainer::getService(\KupShop\StoresBundle\Utils\StoresInStore::class);

        $storeId = $values['id_store'] ?? $storesInStore->getMainStoreId();

        return HTML::create('a')
            ->attr('href', 'javascript:nw(\'stores\', '.$storeId.')')
            ->text($this->getStoreName((int) $storeId))
            ->end();
    }

    public function renderReturnOrder($values)
    {
        return HTML::create('a')
            ->attr('href', 'javascript:nw(\'orders\', '.$values['id_order'].')')
            ->text($values['return_order_no']);
    }

    public function renderReturnLabel($values)
    {
        if (!$values['return_label']) {
            return '';
        }

        if ($values['track_url']) {
            return HTML::create('a')
                ->attr('href', $values['track_url'])
                ->attr('target', '_blank')
                ->text($values['return_label']);
        }

        return $values['return_label'];
    }

    public function renderLabelUrl($values, $column)
    {
        $value = $this->getListRowValue($values, $column['field']);

        if (!$value) {
            return '';
        }

        return HTML::create('a')
            ->attr('href', $value)
            ->attr('target', '_blank')
            ->attr('class', 'btn-sm btn btn-default btn-xs')
            ->attr('title', 'Náhled')
            ->tag('span')
            ->attr('class', 'bi bi-image')
            ->end();
    }

    public function renderOrderId($values)
    {
        $orderIDs = explode(',', $values['orders_ids'] ?? '');
        $orderNos = explode(',', $values['orders_nos'] ?? '');

        $orderIDs = array_unique($orderIDs);
        $orderNos = array_unique($orderNos);

        $output = HTML::create('span');

        foreach ($orderIDs as $key => $id) {
            $output
                ->tag('div')
                ->attr('style', 'font-size:12px')
                ->tag('a')
                ->attr('href', 'javascript:nw(\'orders\', '.$id.')')
                ->text($orderNos[$key])
                ->end()
                ->end();
        }

        return $output;
    }

    public function exportOrderNo($values)
    {
        $array = explode(',', $values['orders_nos']);

        $array = array_unique($array);

        return $this->exportArray($array);
    }

    public function renderCustomer($values)
    {
        if (empty($values['id_user'])) {
            return $values['customer'];
        }

        return HTML::create('a')
            ->attr('href', 'javascript:nw(\'users\', '.$values['id_user'].')')
            ->text($values['customer']);
    }

    public function renderCheckbox($values)
    {
        return HTML::create('input')
            ->attr('name', 'data[return_ids][]')
            ->attr('checked', 'checked')
            ->attr('value', $values['id'])
            ->attr('type', 'checkbox');
    }

    public function renderStatus($values)
    {
        return $this->returns->getStatuses()[$values['status']] ?? '';
    }

    public function renderCode($values)
    {
        return $values['code'];
    }

    public function renderFlags($values, $column)
    {
        $cfg = \KupShop\KupShopBundle\Config::get();

        $flags = $this->getListRowValue($values, $column['field']);
        if (empty($flags)) {
            return '';
        }

        $flags = preg_split('/,/', $flags);

        $ret = [];
        foreach (array_intersect($flags, array_keys($cfg['Returns']['Flags'])) as $flag) {
            $ret[] = HTML::create('span')
                ->class('badge badge-pastel-default')
                ->tag('span')->class('tooltip-text')->text($cfg['Order']['Flags'][$flag]['name'] ?? $flag)->end()
                ->tag('span')->class('badge-text')->text($flag)->end();
        }

        return $ret;
    }

    public function getQuery()
    {
        return parent::getQuery()->addSelect('ri.id as return_item_id')
            ->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id');
    }

    public function getFilterQuery(): Query\QueryBuilder
    {
        $qb = parent::getFilterQuery();

        if ($this->getTableIdColumn() == 'ri.id') {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id');
        }

        if ($currency = getVal('currency')) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id')
                ->leftJoin('ri', 'order_items', 'oi', 'oi.id = ri.id_item')
                ->leftJoin('oi', 'orders', 'o', 'o.id = oi.id_order');
            $qb->andWhere(
                Invert::checkInvert(Operator::inStringArray($currency, 'o.currency'), getVal('currency_invert'))
            );
        }

        if ($customer = getVal('customer')) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id')
                ->leftJoin('ri', 'order_items', 'oi', 'oi.id = ri.id_item')
                ->leftJoin('oi', 'orders', 'o', 'o.id = oi.id_order');
            $search_fields = [
                ['field' => 'o.invoice_name', 'match' => 'both'],
                ['field' => 'o.invoice_surname', 'match' => 'both'],
            ];
            $search = new Search();
            $qb->andWhere(Invert::checkInvert($search->searchFields($customer, $search_fields), getVal('customer_invert')));
        }

        if ($status = getVal('status')) {
            $qb->andWhere(
                Invert::checkInvert(Operator::inIntArray($status, 'r.status'), getVal('status_invert'))
            );
        }

        if ($id_flag_or = getVal('id_flag_or')) {
            $qb->andWhere(Invert::checkInvert(Operator::findInSet((array) $id_flag_or, 'r.flags', 'OR'), getVal('id_flag_or_invert')));
        }

        if ($reasons = getVal('reasons')) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id');
            $qb->andWhere(
                Invert::checkInvert(Operator::inIntArray($reasons, 'ri.reason'), getVal('reasons_invert'))
            );
        }

        if ($code = getVal('code')) {
            $qb->andWhere(
                Invert::checkInvert(
                    Operator::orX(
                        [
                            Operator::equals(['r.id' => $code]),
                            Operator::equals(['r.code' => $code]),
                        ]
                    ), getVal('code_invert')
                )
            );
        }

        if ($doNotStockIn = getVal('do_not_stock_in')) {
            $qb->andWhere(
                Operator::equals(["IF(JSON_VALUE(r.data, '$.doNotStockIn'), 'Y', 'N')" => $doNotStockIn])
            );
        }

        if ($stores = getVal('stores')) {
            $qb->andWhere(
                Invert::checkInvert(
                    Operator::inIntArray($stores, JsonOperator::value('r.data', 'id_store')),
                    getVal('stores_invert')
                )
            );
        }

        if ($returnLabel = getVal('returnLabel')) {
            $returnLabel = ltrim($returnLabel, 'Z');
            $qb->andWhere(
                Invert::checkInvert(
                    Operator::equals([
                        JsonOperator::value('r.data', 'returnDeliveryLabel.*.carrier_id') => $returnLabel,
                        JsonOperator::value('r.data', 'returnDeliveryLabel.carrier_id') => $returnLabel,
                    ], 'OR'),
                    getVal('returnLabel_invert')
                )
            );
        }

        if ($productId = getVal('id_product')) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id')
                ->leftJoin('ri', 'order_items', 'oi', 'oi.id = ri.id_item');
            $qb->andWhere('oi.id_product = :id_product')
                ->setParameter('id_product', $productId);
            if ($variationId = getVal('id_variation')) {
                if (intval($variationId) > 0) {
                    $qb->andWhere('oi.id_variation = :id_variation')
                        ->setParameter('id_variation', $variationId);
                }
            }
        }

        $dateRange = getVal('date_range');

        if ($from = $dateRange['from'] ?? null) {
            $from = DateTime::createFromFormat(Settings::getDateFormat(), $from)->format('Y-m-d 00:00:00');
        }
        if ($to = $dateRange['to'] ?? null) {
            $to = DateTime::createFromFormat(Settings::getDateFormat(), $to)->format('Y-m-d 23:59:59');
        }
        if ($from || $to) {
            $qb->andWhere(Operator::between('r.date_created', new \Range($from, $to)));
        }

        if ($accepted_from = $dateRange['accepted_from'] ?? null) {
            $accepted_from = DateTime::createFromFormat(Settings::getDateFormat(), $accepted_from)->format('Y-m-d 00:00:00');
        }
        if ($accepted_to = $dateRange['accepted_to'] ?? null) {
            $accepted_to = DateTime::createFromFormat(Settings::getDateFormat(), $accepted_to)->format('Y-m-d 23:59:59');
        }
        if ($accepted_from || $accepted_to) {
            $qb->andWhere(Operator::between('r.date_accepted', new \Range($accepted_from, $accepted_to)));
        }

        if ($filter = getVal('filter')) {
            $qb->leftJoin('r', 'return_items', 'ri', 'ri.id_return = r.id')
                ->leftJoin('ri', 'order_items', 'oi', 'oi.id = ri.id_item');
            $qb->innerJoin('oi', 'products', 'p', 'oi.id_product = p.id')
                ->andWhere(ServiceContainer::getService(ProductsFilterSpecs::class)->getSpecs($filter));

            // Product filter filtruje produkt na základě jmenovky, tady potřebuju vyfiltrovat variantu
            // products_variations tabulka joinuta je jen kdyz se podle ni hleda
            if (isset($filter['variations'])) {
                $qb->andWhere('oi.id_variation = pv.id');
            }
        }

        if ($admins = getVal('id_admin')) {
            $qb->andWhere(
                Invert::checkInvert(Operator::inIntArray($admins, 'r.id_admin'), getVal('id_admin_invert'))
            );
        }

        return $qb;
    }

    public function renderItems($values, $column)
    {
        $qb = sqlQueryBuilder()->select('oi.descr, ri.pieces, oi.id_product')
            ->from('return_items', 'ri')
            ->leftJoin('ri', 'order_items', 'oi', 'oi.id = ri.id_item')
            ->where(Operator::equals(['id_return' => $values['id']]));
        if ($this->getTableIdColumn() == 'ri.id') {
            $qb->andWhere(Operator::equals(['ri.id' => $values['return_item_id']]));
        }

        $output = HTML::create('span');

        foreach ($qb->execute() as $item) {
            if ($item['id_product'] ?? false) {
                $output
                    ->tag('div')
                    ->text(floatval($item['pieces']).'x ')
                    ->tag('a')
                    ->attr('href', 'javascript:nw(\'product\', \''.$item['id_product'].'\')')->text($item['descr'])
                    ->end()
                    ->end();
            } else {
                if (!empty($item['pieces']) || !empty($item['descr'])) {
                    $output
                        ->tag('div')
                        ->text(floatval($item['pieces']).'x '.$item['descr'])
                        ->end();
                }
            }
        }

        return $output;
    }

    public function renderReason($values, $column)
    {
        $reasonIDs = explode(',', $values['reason_ids'] ?? '');
        if (($this->getColumns()['fields']['return_items']['visible'] ?? 'N') == 'N') {
            $reasonIDs = array_unique($reasonIDs);
        }

        $output = HTML::create('span');

        foreach ($reasonIDs as $id) {
            $output
                ->tag('div')
                ->text($this->returns->getReasons()[$id] ?? '')
                ->end()
                ->end();
        }

        return $output;
    }

    public function handleReturnItemsExport()
    {
        $listExporter = ServiceContainer::getService(ListExporter::class);
        $listObject = $this;
        $listObject->getTableDef();
        $listObject->tableDef['id'] = 'ri.id';
        $listObject->setPageDivide(0);
        $listExporter->setListObject($listObject);
        $listExporter->listExport();
        exit;
    }

    protected function getStoreName(int $storeId): string
    {
        $storesInStore = ServiceContainer::getService(\KupShop\StoresBundle\Utils\StoresInStore::class);

        return $storesInStore->getStores()[$storeId]['name'] ?? '';
    }
}

return ReturnsList::class;
