<?php

namespace External\CykloSpecialityBundle\Import;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use KupShop\CatalogBundle\Product\ProductFinder;
use KupShop\RewriteBundle\Util\Rewrite;
use Query\Operator;

class ImportData
{
    private $writeLineCallback;

    public function __construct(
        private readonly Rewrite $rewrite,
        private readonly ProductFinder $productFinder,
    ) {
    }

    public function importProductsRewrite($xml)
    {
        foreach ($xml->product as $product) {
            $productId = null;

            if (!empty($product->variants)) {
                foreach ($product->variants->variant as $variant) {
                    if ($variationId = $this->productFinder->getVariationIdByCode((string) $variant->code)) {
                        // pridam rewrite produktu
                        if (empty($productId)) {
                            $productId = $this->getProductId($variationId);
                            $this->rewrite->addRewrite((string) $product->url, Rewrite::TYPE_PRODUCT, $productId);
                        }

                        $this->rewrite->addRewrite((string) $variant->url, Rewrite::TYPE_VARIATION, $variationId);
                        continue;
                    }
                    $this->log('Variation not found code: '.$variant->code);
                }
                continue;
            }

            if (empty($productId) && $productId = $this->productFinder->getProductIdByCode((string) $product->code)) {
                $this->rewrite->addRewrite((string) $product->url, Rewrite::TYPE_PRODUCT, $productId);
                continue;
            }

            $this->log('Product not found code: '.$product->code);
        }
    }

    public function importUsers($xml)
    {
        foreach ($xml->customer as $user) {
            $email = $user->email;
            $invoiceAddress = $user->billing_address;
            $deliveryAddress = $user->delivery_address;

            if (!$email) {
                continue;
            }

            $values = [
                'email' => (string) $email,
                'custom_data' => json_encode(['imported' => true]),
                'name' => (string) $invoiceAddress->firstname,
                'surname' => (string) $invoiceAddress->surname,
                'street' => (string) $invoiceAddress->street,
                'city' => (string) $invoiceAddress->city,
                'zip' => (string) $invoiceAddress->postcode,
                'country' => (string) $invoiceAddress->country,
                'phone' => (string) $invoiceAddress->phone,
            ];

            if ($deliveryAddress) {
                $values += [
                    'delivery_name' => (string) $deliveryAddress->firstname,
                    'delivery_surname' => (string) $deliveryAddress->surname,
                    'delivery_street' => (string) $deliveryAddress->street,
                    'delivery_city' => (string) $deliveryAddress->city,
                    'delivery_zip' => (string) $deliveryAddress->postcode,
                    'delivery_country' => (string) $deliveryAddress->country,
                    'delivery_phone' => (string) $deliveryAddress->phone,
                ];
            }

            try {
                sqlQueryBuilder()->insert('users')
                    ->directValues($values)
                    ->execute();
            } catch (UniqueConstraintViolationException $e) {
                $this->log('User already exists '.$email);
                continue;
            }
        }
    }

    public function importOrders($xml)
    {
        $statuses = getStatuses('handled');

        $first = 156338;
        $last = 156837;
        $availableIDs = [];
        for ($i = $first; $i <= $last; $i++) {
            $availableIDs[] = $i;
        }

        foreach ($xml->order as $order) {
            $code = (int) $order->number;
            $invoiceAddress = $order->billing_address;
            $deliveryAddress = $order->delivery_address;

            if (!$code) {
                continue;
            }

            if ($code < 51857) {
                continue;
            }

            $id = sqlQueryBuilder()->select('id')
                ->from('orders')
                ->andWhere(Operator::equals(['order_no' => $code]))
                ->execute()->fetchOne();

            if ($id) {
                continue;
            }

            $values = [
                'id' => array_shift($availableIDs),
                'source' => 'import',
                'status' => $statuses[0],
                'note_admin' => json_encode([]),
                'order_no' => (string) $code,
                'invoice_name' => (string) $invoiceAddress->firstname,
                'invoice_surname' => (string) $invoiceAddress->surname,
                'invoice_street' => (string) $invoiceAddress->street,
                'invoice_city' => (string) $invoiceAddress->city,
                'invoice_zip' => (string) $invoiceAddress->postcode,
                'invoice_country' => (string) $invoiceAddress->country,
                'invoice_phone' => (string) $invoiceAddress->phone,
                'invoice_email' => (string) $invoiceAddress->email,
                'total_price' => (float) $order->price_with_vat,
                'currency' => (string) $order->currency,
            ];

            if ($userID = $this->getUserByEmail((string) $invoiceAddress->email)) {
                $values += ['id_user' => $userID];
            }

            if ($deliveryAddress) {
                $values += [
                    'delivery_name' => (string) $deliveryAddress->firstname,
                    'delivery_surname' => (string) $deliveryAddress->surname,
                    'delivery_street' => (string) $deliveryAddress->street,
                    'delivery_city' => (string) $deliveryAddress->city,
                    'delivery_zip' => (string) $deliveryAddress->postcode,
                    'delivery_country' => (string) $deliveryAddress->country,
                    'delivery_phone' => (string) $deliveryAddress->phone,
                ];
            }

            try {
                $orderId = sqlGetConnection()->transactional(function () use ($values) {
                    sqlQueryBuilder()->insert('orders')->directValues($values)->execute();

                    return sqlInsertId();
                });
            } catch (UniqueConstraintViolationException $e) {
                $this->log('Order already exists '.$code);
                continue;
            }

            if (!$orderId) {
                continue;
            }

            foreach ($order->items->item as $item) {
                $itemValues = [
                    'id_order' => $orderId,
                    'pieces' => (int) $item->amount,
                    'descr' => (string) $item->name,
                    'piece_price' => (float) $item->unit_price_without_vat,
                    'total_price' => (float) $item->unit_price_without_vat * (float) $item->amount,
                    'tax' => (float) $item->vat,
                ];

                $productId = $this->productFinder->getProductIdByCode((string) $item->code);
                $variationId = $this->productFinder->getVariationIdByCode((string) $item->code);

                if (!$productId && $variationId) {
                    $productId = sqlQueryBuilder()->select('id_product')
                        ->from('products_variations')
                        ->andWhere(Operator::equals(['id' => $variationId]))
                        ->execute()->fetchOne();
                }

                $itemValues['id_product'] = $productId;
                $itemValues['id_variation'] = $variationId;

                sqlQueryBuilder()->insert('order_items')->directValues($itemValues)->execute();
            }

            $paymentPrice = \Decimal::create((string) $order->payment->price_with_vat);
            if (!$paymentPrice->isZero()) {
                sqlQueryBuilder()->insert('order_items')->directValues([
                    'id_order' => $orderId,
                    'pieces' => 1,
                    'note' => json_encode(['item_type' => 'payment']),
                    'descr' => (string) $order->payment->name,
                    'piece_price' => $paymentPrice->removeVat((string) $order->payment->vat)->asFloat(),
                    'total_price' => $paymentPrice->removeVat((string) $order->payment->vat)->asFloat(),
                    'tax' => (float) $order->payment->vat,
                ])->execute();
            }

            $deliveryPrice = \Decimal::create((string) $order->shipping->price_with_vat);
            if (!$deliveryPrice->isZero()) {
                sqlQueryBuilder()->insert('order_items')->directValues([
                    'id_order' => $orderId,
                    'pieces' => 1,
                    'note' => json_encode(['item_type' => 'delivery']),
                    'descr' => (string) $order->shipping->name,
                    'piece_price' => $deliveryPrice->removeVat((string) $order->shipping->vat)->asFloat(),
                    'total_price' => $deliveryPrice->removeVat((string) $order->shipping->vat)->asFloat(),
                    'tax' => (float) $order->shipping->vat,
                ])->execute();
            }
        }
    }

    public function setWriteLineCallback(callable $callback)
    {
        $this->writeLineCallback = $callback;
    }

    private function log($str): void
    {
        if ($this->writeLineCallback) {
            call_user_func($this->writeLineCallback, $str);
        }
    }

    private function getUserByEmail(string $email): ?int
    {
        $id = sqlQueryBuilder()->select('id')
            ->from('users')
            ->andWhere(Operator::equals(['email' => $email]))
            ->execute()->fetchOne();

        if ($id === false) {
            return null;
        }

        return $id;
    }

    private function getProductId(int $variationId)
    {
        $id = sqlQueryBuilder()->select('id_product')
            ->from('products_variations')
            ->andWhere(Operator::equals(['id' => $variationId]))
            ->execute()->fetchOne();

        if ($id === false) {
            return null;
        }

        return $id;
    }
}
