<?php

declare(strict_types=1);

namespace KupShop\GraphQLBundle\ApiAdmin\Util\Catalog;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use KupShop\GraphQLBundle\ApiAdmin\Types\Catalog\Producer\Input\GPSRCompanyInput;
use KupShop\GraphQLBundle\ApiAdmin\Types\Catalog\Producer\Input\ProducerInput;
use KupShop\GraphQLBundle\ApiAdmin\Types\Catalog\Producer\Response\ProducerMutateResponse;
use KupShop\GraphQLBundle\ApiAdmin\Types\Collection\ProducerCollection;
use KupShop\GraphQLBundle\ApiShared\Types\Product\Producer;
use KupShop\GraphQLBundle\Exception\GraphQLNotFoundException;
use KupShop\GraphQLBundle\Exception\GraphQLValidationException;
use KupShop\KupShopBundle\Util\Database\QueryHint;
use KupShop\KupShopBundle\Util\ObjectUtil;
use Query\Operator;

class ProducerUtil
{
    public function getProducer(int $id): Producer
    {
        $producer = $this->getProducerQueryBuilder()
            ->where(Operator::equals(['id' => $id]))
            ->execute()->fetchAssociative();

        if (!$producer) {
            throw new GraphQLNotFoundException(
                sprintf('Producer with ID "%s" was not found!', $id)
            );
        }

        return new Producer(
            $this->prepareProducer($producer)
        );
    }

    public function getProducers(): ProducerCollection
    {
        $qb = $this->getProducerQueryBuilder();

        $producers = [];
        foreach ($qb->execute() as $item) {
            $producers[] = new Producer(
                $this->prepareProducer($item)
            );
        }

        return new ProducerCollection($producers);
    }

    public function createOrUpdateProducer(ProducerInput $input): ProducerMutateResponse
    {
        if (empty($input->id) && empty($input->name)) {
            throw new GraphQLValidationException('Field "name" is mandatory when creating producer!');
        }

        $producerId = null;
        if (!empty($input->id)) {
            $producerId = $input->id;

            $producerExists = sqlQueryBuilder()
                ->select('id')
                ->from('producers')
                ->where(Operator::equals(['id' => $producerId]))
                ->execute()->fetchOne();

            if (!$producerExists) {
                throw new GraphQLNotFoundException(
                    sprintf('Producer with ID "%s" was not found!', $producerId)
                );
            }
        }

        $producerData = [];

        if (ObjectUtil::isPropertyInitialized($input, 'name')) {
            $producerData['name'] = $input->name;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'web')) {
            $producerData['web'] = $input->web;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'active')) {
            $producerData['active'] = $input->active ? 'Y' : 'N';
        }

        if (isset($input->gpsrCompany)) {
            $gpsrData = $this->checkGPSRInput($input->gpsrCompany);

            $producerData = array_merge($producerData, $gpsrData);
        }

        if (isset($input->gpsrImportCompany)) {
            $gpsrImportData = $this->checkGPSRInput($input->gpsrImportCompany, 'import_');

            $producerData = array_merge($producerData, $gpsrImportData);
        }

        if ($producerId) {
            if (!empty($producerData)) {
                sqlQueryBuilder()
                    ->update('producers')
                    ->directValues($producerData)
                    ->where(Operator::equals(['id' => $input->id]))
                    ->execute();
            }
        } else {
            try {
                $producerId = sqlGetConnection()->transactional(function () use ($producerData) {
                    sqlQueryBuilder()
                        ->insert('producers')
                        ->directValues($producerData)
                        ->execute();

                    return (int) sqlInsertId();
                });
            } catch (UniqueConstraintViolationException $e) {
                throw new GraphQLValidationException(
                    sprintf('Producer with name "%s" exists already!', $input->name)
                );
            }
        }

        return new ProducerMutateResponse(
            true,
            QueryHint::withRouteToMaster(fn () => $this->getProducer($producerId))
        );
    }

    private function getProducerQueryBuilder()
    {
        return sqlQueryBuilder()
            ->select('pr.*, pr.name as title')
            ->from('producers', 'pr');
    }

    private function prepareProducer(array $producer): array
    {
        if (!empty($producer['photo'])) {
            $producer['image'] = getImage($producer['id'], $producer['photo'], '../producer/', 7, $producer['name']);
        }

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

        return $producer;
    }

    public function checkGPSRInput(GPSRCompanyInput $input, string $field = ''): array
    {
        $gpsrData = [];

        if (ObjectUtil::isPropertyInitialized($input, 'name')) {
            $gpsrData[$field.'company_name'] = $input->name;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'street')) {
            $gpsrData[$field.'company_street'] = $input->street;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'city')) {
            $gpsrData[$field.'company_city'] = $input->city;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'zip')) {
            $gpsrData[$field.'company_zip'] = $input->zip;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'country')) {
            $gpsrData[$field.'company_country'] = $input->country;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'email')) {
            $gpsrData[$field.'company_email'] = $input->email;
        }

        if (ObjectUtil::isPropertyInitialized($input, 'web')) {
            $gpsrData[$field.'company_web'] = $input->web;
        }

        return $gpsrData;
    }
}
