<?php

namespace KupShop\OrderingBundle\Util\Order;

use Doctrine\Common\Collections\ArrayCollection;
use KupShop\DropshipBundle\Util\DropshipmentUtil;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Context\LanguageContext;
use Query\Operator;

class OrderInfo
{
    public const ORDER_SOURCE_SHOP = 'shop';
    public const ORDER_SOURCE_IMPORT = 'import';
    public const ORDER_SOURCE_RESERVATION = 'reservation';
    public const ORDER_SOURCE_POS = 'pos';
    public const ORDER_SOURCE_RETURNS = 'returns';
    public const ORDER_SOURCE_DROPSHIP = 'dropship';
    public const ORDER_SOURCE_API = 'api';
    public const ORDER_SOURCE_ADMIN = 'admin';

    private $languageContext;

    private ?DropshipmentUtil $dropshipmentUtil;

    public function __construct(LanguageContext $languageContext, ?DropshipmentUtil $dropshipmentUtil)
    {
        $this->languageContext = $languageContext;
        $this->dropshipmentUtil = $dropshipmentUtil;
    }

    public function getOrderBankAccount(\Order $order): string
    {
        $dbcfg = \Settings::getFromCache($order->getLanguage());
        $account = "{$dbcfg->bank_account_number}/{$dbcfg->bank_code}";
        if (!empty($dbcfg->bank_account_prefix)) {
            $account = "{$dbcfg->bank_account_prefix}-{$account}";
        }

        return $account;
    }

    public function getOrderDropshipmentInfo(int $orderId): ?array
    {
        if (!findModule(\Modules::DROPSHIP)) {
            return null;
        }

        $dropshipmentOrder = sqlQueryBuilder()
            ->select('id_dropshipment, id_external, data')
            ->from('order_dropshipment')
            ->where(Operator::equals(['id_order' => $orderId]))
            ->execute()->fetchAssociative();

        if ($dropshipmentOrder) {
            return [
                'dropshipment' => $this->dropshipmentUtil->getDropshipment((int) $dropshipmentOrder['id_dropshipment'], true),
                'external' => [
                    'id' => $dropshipmentOrder['id_external'],
                    'data' => json_decode($dropshipmentOrder['data'] ?: '', true) ?: [],
                ],
            ];
        }

        return null;
    }

    /**
     * @return bool
     */
    public function isOrderInPerson($orderId)
    {
        $order = new \Order();
        $order->createFromDB($orderId);
        $delivery = $order->getDeliveryType()->getDelivery();
        if ($delivery instanceof \OsobniOdber) {
            return true;
        }

        return false;
    }

    public function getOrderStatus($statusId)
    {
        $cfg = Config::get();

        if ($statusId === 'storno') {
            $text = $cfg['Order']['Status']['storno'][1];
        } else {
            $text = $cfg['Order']['Status']['global'][$statusId];
        }

        if ($this->languageContext->translationActive()) {
            if (($translate = translate_shop($statusId, 'order_status', true)) !== false) {
                $text = $translate;
            }
        }

        return $text;
    }

    public function getNamedVats($vats)
    {
        $return = [];
        foreach ([0 => 'none', getVat() => 'default'] as $v => $name) {
            if (!empty($vats[$v])) {
                $vat = $vats[$v]['tax'];
                unset($vats[$v]);
            } else {
                $vat = formatPrice(0, $v);
            }
            $return[$name] = $vat;
        }

        foreach (['first', 'second'] as $key => $name) {
            $v = array_slice($vats, $key, $key + 1, true);
            $vatKey = array_keys($v);
            $vatKey = reset($vatKey);
            $v = reset($v);

            if (!empty($v)) {
                $return[$name] = $v['tax'];
            } else {
                $return[$name] = formatPrice(0, $vatKey ?: 0);
            }
        }

        return $return;
    }

    /**
     * @return array
     */
    public function getUsedDiscounts($id_order)
    {
        $used_order_discounts = sqlQueryBuilder()->select('od.id, od.name')
            ->from('order_discounts_orders', 'odo')
            ->join('odo', 'order_discounts', 'od', 'odo.id_order_discount = od.id')
            ->where(Operator::equals(['id_order' => $id_order]))
            ->execute();

        return sqlFetchAll($used_order_discounts, ['id' => 'name']);
    }

    public function getDiscounts(\Order $order): array
    {
        if (!isset($order->itemsDiscounts)) {
            $orders = new ArrayCollection();
            $orders->set((int) $order->id, $order);
            $this->fetchDiscountsForMultiple($orders);
        }

        return $order->itemsDiscounts ?? [];
    }

    /**
     * @param ArrayCollection $orders collection of id => \Order pairs
     */
    public function fetchDiscountsForMultiple(ArrayCollection $orders): void
    {
        $qb = sqlQueryBuilder()->select('od.id, IF(od.display_name = "", od.name, od.display_name) AS name, 
                od.name as admin_name, odt.name as discountType')
            ->from('order_discounts_orders', 'odo')
            ->join('odo', 'order_discounts', 'od', 'odo.id_order_discount = od.id')
            ->leftJoin('od', 'order_discounts_types', 'odt', 'odt.id = od.type')
            ->where(Operator::inIntArray($orders->getKeys(), 'id_order'))
            ->execute();
        $usedDiscounts = $qb->fetchAllAssociativeIndexed();

        foreach ($orders as $orderID => $order) {
            /** @var $order \Order */
            if (isset($order->itemsDiscounts)) {
                continue; // skip if already present
            }
            $itemsDiscounts = [];

            $items = $order->fetchItems();
            $products = array_filter($items, function ($i) {
                return !empty($i['id_product']);
            });
            foreach ($products as $item) {
                if ($discounts = $item['note']['discounts'] ?? null) {
                    $pieces = toDecimal($item['pieces']);
                    foreach ($discounts as $discount) {
                        $id = $discount['id'];
                        $name = $discount['name'] ?? $usedDiscounts[$id]['name'] ?? 'Sleva';
                        $nameAdmin = $usedDiscounts[$id]['admin_name'] ?? 'Sleva';
                        $type = $usedDiscounts[$id]['discountType'] ?? '';
                        $key = $id.'-'.$name;
                        if (!isset($itemsDiscounts[$key])) {
                            $itemsDiscounts[$key] = [
                                'name' => $name,
                                'totalPrice' => \DecimalConstants::zero(),
                                'nameAdmin' => $nameAdmin,
                                'type' => $type,
                            ];
                        }
                        $discountPrice = toDecimal($discount['discountPrice'])->mul($pieces)->addVat($item['vat'])->round(2);
                        $itemsDiscounts[$key]['totalPrice'] = $itemsDiscounts[$key]['totalPrice']->add($discountPrice);
                        $itemsDiscounts[$key]['itemsPartials'][$item['id']] = $discountPrice;
                    }
                }
            }

            foreach ($items as $item) {
                if (empty($item['id_product']) && !empty($item['note'])) {
                    $item_type = $item['note']['item_type'] ?? null;
                    if (($item_type == 'discount') && ($id_discount = $item['note']['id_discount'] ?? null)) {
                        $key = $id_discount.'-'.$item['descr'];
                        $itemsDiscounts[$key]['itemId'] = $item['id'];
                        $itemsDiscounts[$key]['name'] = $item['descr'];
                        $totalPrice = $itemsDiscounts[$key]['totalPrice'] ?? \DecimalConstants::zero();
                        if (!$item['total_price']['value_with_vat']->isZero()) {
                            $discountPrice = $item['total_price']['value_with_vat']->mul(\DecimalConstants::negativeOne());
                            $totalPrice = $totalPrice->add($discountPrice);
                        }
                        $itemsDiscounts[$key]['totalPrice'] = $totalPrice;
                    }
                }
            }

            $order->itemsDiscounts = $itemsDiscounts;
        }
    }

    /**
     * @throws \Exception
     */
    public function getDeliveryDate($order): ?\DateTime
    {
        $deliveryDate = $order->getData('deliveryDate');

        return !$deliveryDate
            ? null
            : new \DateTime($deliveryDate);
    }

    public function getPackages(\Order $order): ?array
    {
        if (isset($order->packages)) {
            return $order->packages;
        }

        $order->packages = [];

        if (findModule(\Modules::BALIKONOS)) {
            $balikobot = sqlQueryBuilder()->select('data')->from('balikonos')
                ->where(Operator::equals(['id_order' => $order->id]))
                ->execute()->fetchAll();
            if ($balikobot) {
                foreach ($balikobot as $balik) {
                    $data = json_decode($balik['data'] ?? '', true);
                    foreach ($data['response'] as $response) {
                        if ($package_id = $response['carrier_id'] ?? null) {
                            $order->packages[$package_id] = ['package_id' => $package_id];
                            if ($link = $response['track_url'] ?? null) {
                                $order->packages[$package_id]['track_url'] = $link;
                            }
                        }
                    }
                }

                return $order->packages;
            }
        }

        if ($order->package_id) {
            foreach (explode(',', $order->package_id) as $packageId) {
                if (empty($packageId)) {
                    continue;
                }

                $order->packages[$packageId] = ['package_id' => $packageId];
                $link = $order->getDeliveryType()->getDelivery()->getTrackAndTraceLink($packageId, $order);
                if ($link) {
                    $order->packages[$packageId]['track_url'] = $link;
                }
            }
        }

        return $order->packages;
    }

    public static function getUsedCoupons($order)
    {
        $customData = $order->getPurchaseState()->getCustomData();

        if ($customData['used_coupons'] ?? false) {
            return $customData['used_coupons'];
        }

        return [];
    }

    public static function getOrderSources(): array
    {
        $sources = [
            self::ORDER_SOURCE_SHOP => 'E-shop',
            self::ORDER_SOURCE_IMPORT => 'Import',
            self::ORDER_SOURCE_POS => 'Pokladna',
            self::ORDER_SOURCE_ADMIN => 'Administrace',
            self::ORDER_SOURCE_API => 'API',
        ];

        if (findModule(\Modules::RESERVATIONS)) {
            $sources[self::ORDER_SOURCE_RESERVATION] = 'Rezervace';
        }

        if (findModule(\Modules::RETURNS)) {
            $sources[self::ORDER_SOURCE_RETURNS] = 'Vratky';
        }

        if (findModule(\Modules::DROPSHIP)) {
            $sources[self::ORDER_SOURCE_DROPSHIP] = 'Dropship';
        }

        return $sources;
    }
}
