<?php

declare(strict_types=1);

namespace External\ZNZBundle\Synchronizers;

use External\ZNZBundle\Exception\ZNZException;
use External\ZNZBundle\Util\ZNZApi;
use External\ZNZBundle\Util\ZNZConfiguration;
use External\ZNZBundle\Util\ZNZLogger;
use External\ZNZBundle\Util\ZNZUtil;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Contexts;

abstract class BaseSynchronizer implements SynchronizerInterface
{
    protected static string $type;
    protected static bool $logging = true;

    protected ZNZConfiguration $configuration;
    protected ZNZUtil $znzUtil;
    protected ZNZLogger $logger;

    public function __construct(ZNZConfiguration $configuration, ZNZUtil $znzUtil, ZNZLogger $logger)
    {
        $this->configuration = $configuration;
        $this->znzUtil = $znzUtil;
        $this->logger = $logger;
    }

    public static function getType(): string
    {
        return static::$type;
    }

    public static function isLoggingEnabled(): bool
    {
        return static::$logging;
    }

    public static function getIdField(): ?string
    {
        return null;
    }

    public function process(array $data): void
    {
        if (!($method = (static::getHandledTables()[$data['Tabulka']] ?? null))) {
            return;
        }

        if (!method_exists($this, $method)) {
            throw new ZNZException(
                sprintf('Method "%s" does not exists in "%s" class!', $method, static::class)
            );
        }

        // log to kibana
        if (static::isLoggingEnabled()) {
            $this->logger->kibana(sprintf('Processing change of "%s"', static::getType()), [
                'type' => static::getType(),
                'helios_table' => $data['Tabulka'],
                'data' => $data,
            ]);
        }

        $languageContext = Contexts::get(LanguageContext::class);

        $idField = static::getIdField();

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

        if (method_exists($this, 'preprocessItems')) {
            $items = $this->preprocessItems($items);
        }

        // pokud message nema data a je to delete message, tak nasimuluju prazdnej item, kterej se ptoom doplni
        // aspon zakladnim infem pri priprave itemu - jinak by se mi ta delete message nezpracovala a ztratila
        if (empty($items) && ($data['Smazat'] ?? false)) {
            $items = [[]];
        }

        foreach ($items as $item) {
            // prepare item
            $item['meta']['id_change'] = $data['ID'] ?? null;
            $item['meta']['delete'] = $data['Smazat'] ?? false;
            $item['meta']['website'] = $data['IDWebsite'] ?? null;
            $item['meta']['unique_id'] = $data['IDTabulka'] ?? null;

            if ($idField && $item['meta']['delete']) {
                $item[$idField] = (int) $data['IDTabulka'];
            }

            // find language
            $language = $languageContext->getDefaultId();
            if (!empty($data['IDLanguage'])) {
                foreach ($languageContext->getAll() as $lang) {
                    if ($lang->getLocale() === $data['IDLanguage']) {
                        $language = $lang->getId();
                    }
                }
            }

            $item['meta']['language'] = $language;

            // detect if it is translation object
            $defaultLanguage = $languageContext->getAll()[$languageContext->getDefaultId()];
            $item['meta']['translation'] = false;
            if ($data['IDLanguage'] !== null && $defaultLanguage->getLocale() !== $data['IDLanguage']) {
                $item['meta']['translation'] = true;
            }

            $this->{$method}($item);
        }
    }

    protected function isDeleteMessage(array $item): bool
    {
        return (bool) ($item['meta']['delete'] ?? false);
    }

    public function isMessageWithStoreRequiredValid(array $item): bool
    {
        if (!$this->configuration->isB2BMode()) {
            return true;
        }

        return !empty($item['IdSklad']);
    }

    protected function getZNZApi(): ZNZApi
    {
        if ($this instanceof SynchronizerOutInterface && !property_exists($this, 'znzApi')) {
            throw new \RuntimeException('Invalid call of \'getZNZApi\' method!');
        }

        $parameters = $this->configuration->getHeliosConnectionParameters(
            $this->useTestConnection()
        );

        // nastavim spravne udaje helios
        $this->znzApi->setConnectionParameters(
            [
                'db_name' => $parameters['dbname'],
                'db_user' => $parameters['user'],
                'db_pass' => $parameters['pass'],
                'db_host' => $parameters['host'],
                'db_port' => $parameters['port'],
            ]
        );

        return $this->znzApi;
    }

    protected function getLastSyncTime(): ?\DateTimeImmutable
    {
        $sync = \Settings::getDefault()->loadValue('znz_sync') ?: [];

        if (!empty($sync[static::getType()])) {
            $datetime = new \DateTimeImmutable();
            $datetime = $datetime->setTimestamp($sync[static::getType()]);

            return $datetime;
        }

        return null;
    }

    protected function updateLastSyncTime(int $time): void
    {
        $dbcfg = \Settings::getDefault();

        $sync = $dbcfg->loadValue('znz_sync') ?: [];
        $sync[static::getType()] = $time;

        $dbcfg->saveValue('znz_sync', $sync);
    }
}
