<?php

namespace KupShop\ReclamationsSuppliersBundle\Util;

use KupShop\BalikonosBundle\Balikobot;
use KupShop\BalikonosBundle\Exception\BalikonosException;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Suppliers\SuppliersUtil;
use KupShop\ReclamationsSuppliersBundle\Exception\InvalidSupplierInfo;
use Query\Operator;

class ReclamationsSuppliersUtil
{
    public function addItemFromReclamation($supplierId, $reclamation)
    {
        $supplierReclamationId = sqlQueryBuilder()->select('id')
            ->from('reclamations_suppliers', 'rs')
            ->where(Operator::equals([
                'status' => 0,
                'id_supplier' => $supplierId,
            ]))->execute()->fetchOne();

        if (!$supplierReclamationId) {
            $supplierReclamationId = $this->createReclamation($supplierId);
        }

        $item = $reclamation->getItem();

        sqlQueryBuilder()->insert('reclamations_suppliers_items')
            ->directValues([
                'id_reclamation_supplier' => $supplierReclamationId,
                'id_reclamation' => $reclamation['id'],
                'pieces' => $reclamation['pieces'],
                'id_product' => $item['id_product'],
                'id_variation' => $item['id_variation'],
            ])->execute();

        $this->logHistory($supplierReclamationId, 'Přidán produkt z reklamace '.$reclamation['code']);
    }

    public function addItem($supplierReclamationId, $productId, $variationId, $pieces, $data = null)
    {
        sqlQueryBuilder()->insert('reclamations_suppliers_items')
            ->directValues([
                'id_reclamation_supplier' => $supplierReclamationId,
                'pieces' => $pieces,
                'id_product' => $productId,
                'id_variation' => $variationId,
                'data' => json_encode($data ?: ''),
            ])->execute();
    }

    public function getItems($supplierReclamationId)
    {
        $items = sqlFetchAll(sqlQueryBuilder()
            ->select('rsi.id, COALESCE(pv.code, p.code) product_code, COALESCE(pv.ean, p.ean) product_ean, p.id product_id, pv.id variation_id,
             r.id reclamation_id, r.code reclamation_code, r.status reclamation_status, rsi.pieces, p.title product_title, pv.title variation_title, r.user_note reclamation_reason,
              pos.code supplier_code, rsi.data')
            ->from('reclamations_suppliers_items', 'rsi')
            ->innerJoin('rsi', 'reclamations_suppliers', 'rs', 'rsi.id_reclamation_supplier = rs.id')
            ->leftJoin('rsi', 'reclamations', 'r', 'rsi.id_reclamation = r.id')
            ->leftJoin('rsi', 'products', 'p', 'p.id = rsi.id_product')
            ->leftJoin('rsi', 'products_variations', 'pv', 'pv.id = rsi.id_variation')
            ->leftJoin('rs', 'products_of_suppliers', 'pos', 'pos.id_product = rsi.id_product AND pos.id_variation <=> rsi.id_variation AND  pos.id_supplier = rs.id_supplier')
            ->where(Operator::equals(['id_reclamation_supplier' => $supplierReclamationId]))
            ->orWhere(Operator::equals(['rs.code' => $supplierReclamationId]))
            ->execute(), 'id');

        $items = array_map(function ($item) {
            $item['data'] = json_decode($item['data'] ?? '', true);

            return $item;
        }, $items);

        return $items;
    }

    public function removeItem($where)
    {
        sqlQueryBuilder()->delete('reclamations_suppliers_items')
            ->where(Operator::equalsNullable($where))
            ->execute();
    }

    public function getReclamationSupplier(int $reclamationSupplierId)
    {
        $reclamationSupplier = sqlQueryBuilder()->select('*')->from('reclamations_suppliers')
            ->where(Operator::equals(['id' => $reclamationSupplierId]))
            ->execute()->fetchAssociative();

        $reclamationSupplier['data'] = json_decode($reclamationSupplier['data'] ?? '', true);

        return $reclamationSupplier;
    }

    public function createReclamation($supplierId): int
    {
        $deliveries = \Delivery::getAll(false);

        sqlQueryBuilder()->insert('reclamations_suppliers')
            ->directValues([
                'status' => 0,
                'id_supplier' => $supplierId,
                'id_delivery' => $deliveries ? reset($deliveries)['id'] : null,
            ])->execute();

        $reclamationSupplierId = sqlInsertId();

        sqlQueryBuilder()->update('reclamations_suppliers')
            ->directValues([
                'code' => $this->getCode($reclamationSupplierId),
            ])
            ->where(Operator::equals(['id' => $reclamationSupplierId]))
            ->execute();

        return $reclamationSupplierId;
    }

    public function getCode($reclamationSupplierId)
    {
        return 'RD'.sprintf('%03d', $reclamationSupplierId);
    }

    public function getStatuses(): array
    {
        return [
            0 => translate('status_0', 'ReclamationsSuppliers'),
            1 => translate('status_1', 'ReclamationsSuppliers'),
            2 => translate('status_2', 'ReclamationsSuppliers'),
        ];
    }

    public function logHistory(int $reclamationSupplierId, $comment, $status = 0, ?\DateTime $date = null)
    {
        global $adminID;

        if ($date === null) {
            $date = new \DateTime();
        }

        $history = $this->getHistory($reclamationSupplierId);
        $history[] = [
            'comment' => $comment,
            'status' => $status,
            'date' => $date->format('Y-m-d H:i:s'),
            'admin' => !empty($adminID) ? $adminID : null,
        ];

        sqlQueryBuilder()->update('reclamations_suppliers')->directValues([
            'history' => json_encode($history),
        ])->andWhere(Operator::equals(['id' => $reclamationSupplierId]))
            ->execute();

        return true;
    }

    public function getHistory(int $reclamationSupplierId)
    {
        $history = sqlQueryBuilder()->select('history')->from('reclamations_suppliers')
            ->where(Operator::equals(['id' => $reclamationSupplierId]))
            ->execute()->fetchColumn();

        return $this->parseHistory($history);
    }

    public function parseHistory(?string $historyJson)
    {
        if (!$historyJson) {
            return [];
        }
        $history = json_decode($historyJson, true);

        if (!empty($history)) {
            $admins = sqlFetchAll(sqlQueryBuilder()->select('id, login')->from('admins')
                ->execute(),
                'id');

            foreach ($history as &$h) {
                $h['status_name'] = $this->getStatuses()[$h['status']] ?? null;
                $h['admin_name'] = $admins[$h['admin']]['login'] ?? null;
            }
        }

        return $history;
    }

    public function changeStatus($reclamationSupplierId, $newStatusId)
    {
        $reclamationSupplier = $this->getReclamationSupplier($reclamationSupplierId);

        if (!$reclamationSupplier || $reclamationSupplier['status'] == $newStatusId) {
            return;
        }

        $values = ['status' => $newStatusId];

        if ($newStatusId == 1) {
            $values['date_accepted'] = (new \DateTime())->format('Y-m-d H:i:s');
            if (!$this->isAlreadyInBalikobot($reclamationSupplierId)) {
                $balikonosId = $this->sendToBalikobot($reclamationSupplierId);
                $values['id_balikonos'] = $balikonosId;
            }
        }
        if ($newStatusId == 2) {
            $values['date_handle'] = (new \DateTime())->format('Y-m-d H:i:s');
        }
        sqlQueryBuilder()->update('reclamations_suppliers')
            ->directValues($values)
            ->andWhere(Operator::equals(['id' => $reclamationSupplierId]))
            ->execute();
    }

    protected function isAlreadyInBalikobot($reclamationSupplierId)
    {
        $idBalikobot = sqlQueryBuilder()->select('id_balikonos')
            ->from('reclamations_suppliers', 'rs')
            ->where(Operator::equals(['id' => $reclamationSupplierId]))
            ->execute()->fetchOne();

        if ($idBalikobot) {
            return true;
        }

        return false;
    }

    /**
     * @throws InvalidSupplierInfo
     */
    public function sendToBalikobot($reclamationSupplierId)
    {
        $balikobot = ServiceContainer::getService(Balikobot::class);
        $suppliersUtil = ServiceContainer::getService(SuppliersUtil::class);
        $data = $this->getReclamationSupplier($reclamationSupplierId);

        $supplier = sqlQueryBuilder()->select('s.*')
            ->from('reclamations_suppliers', 'rs')
            ->leftJoin('rs', 'suppliers', 's', 's.id = rs.id_supplier')
            ->where(Operator::equals(['rs.id' => $reclamationSupplierId]))
            ->execute()->fetchAssociative();

        $address = $suppliersUtil->parseAddress($supplier);

        if (!$suppliersUtil->validateAddress($address)) {
            throw new InvalidSupplierInfo('Dodavatel nemá vyplněné údaje!');
        }

        $balikobot->setIDs([
            0 => [
                'type' => Balikobot::TYPE_CUSTOM,
                'invoice_email' => $supplier['email'],
                'invoice_name' => $address['name'],
                'invoice_surname' => $address['surname'],
                'invoice_street' => $address['street'],
                'invoice_city' => $address['city'],
                'invoice_zip' => $address['zip'],
                'invoice_country' => $address['country'] ? $address['country'] : 'CZ',
                'invoice_phone' => $supplier['phone'],
                'total_price' => 0,
                'id_delivery' => $data['id_delivery'],
                'cod_price' => 0,
                'order_id' => '9999'.$reclamationSupplierId,
                'id_supplier' => $supplier['id'],
                //                'weight' => $data['weight'],
            ],
        ]);

        $balikobot->sendDeliveries();

        $result = $balikobot->getResult();
        $result = reset($result);

        if (!empty($result['error_message']) && $result['error_message'] != 'OK') {
            throw new BalikonosException('Chyba při odesílání do balíkobotu: '.$result['error_message']);
        }

        return $result['id_balikonos'] ?? null;
    }
}
