<?php

namespace KupShop\PohodaBundle\Utils;

use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\KupShopBundle\Util\Logging\Initiator;
use KupShop\KupShopBundle\Util\Logging\LoggerInfoHelper;
use KupShop\KupShopBundle\Util\System\PathFinder;
use KupShop\PohodaBundle\Exceptions\PohodaException;
use KupShop\PohodaBundle\PohodaBundle;
use Symfony\Contracts\Service\Attribute\Required;

class PohodaConnector
{
    use PohodaConnectorHelper;

    #[Required]
    public LoggerInfoHelper $loggerInfoHelper;

    protected $flockFile;

    /**
     * @return bool|false|string|string[]|null
     *
     * @throws PohodaException
     */
    public function sendRequest($XmlBodyRequest)
    {
        $this->setInitiatorPohoda();

        $response = $this->executeConnection('xml', $XmlBodyRequest);
        if (!$response) {
            throw new PohodaException('Pohoda mServer return empty response!');
        }

        return $response['response'];
    }

    /**
     * @return bool|string
     *
     * @throws PohodaException
     */
    public function getPohodaFile($filePath)
    {
        $this->setInitiatorPohoda();

        $filePath = str_replace([' ', '\\'], ['%20', '/'], $filePath);
        $response = $this->executeConnection('documents/'.$filePath);

        return $response['response'];
    }

    public function addActivityLog($severity, $msg, $data = [], $tags = [])
    {
        $msg = 'Pohoda: '.$msg;

        return addActivityLog($severity, ActivityLog::TYPE_SYNC, $msg, $data, array_merge($tags, [PohodaBundle::LOG_TAG_POHODA]));
    }

    /**
     * @return string
     *
     * @throws \SmartyException
     */
    public function getRequestContent($template, $data)
    {
        $smarty = createSmarty(false, true);

        $smarty->assign(($this->getCurrent() ?? []) + $data);
        $smarty->assign(['pohodaConnector' => $this]);

        return $smarty->fetch($template);
    }

    /**
     * @return array|bool
     */
    public function checkConnection()
    {
        if (!($this->getCurrent()['url'] ?? false)) {
            return false;
        }

        try {
            $response = $this->executeConnection('status');
            if (($response['code'] ?? false) && $response['code'] == 200) {
                return $response['response'];
            } else {
                return false;
            }
        } catch (PohodaException $e) {
            return false;
        }
    }

    public function lockSync($timeout)
    {
        $startTime = getScriptTime();

        $t = new PathFinder();
        $this->flockFile = fopen($t->getDataDir().'tmp/pohoda_sync.lock', 'wb');

        while (!flock($this->flockFile, LOCK_EX | LOCK_NB)) {
            if ((getScriptTime() - $startTime) > $timeout) {
                throw new Exceptions\LockException();
            }
            var_dump('Waiting for lock');
            flush();
            sleep(1);
        }

        return true;
    }

    public function unLockSync()
    {
        flock($this->flockFile, LOCK_UN);
        fclose($this->flockFile);

        return true;
    }

    public function checkValidResponse(\SimpleXMLElement $xml)
    {
        if ($xml->xpath("rsp:responsePack[@state != 'ok']")) {
            throw new PohodaException('Pohoda responsePack error.');
        }
    }

    /**
     * @throws PohodaException
     */
    public function executeConnections($function)
    {
        if (!is_callable($function)) {
            throw new PohodaException('Unable to call function.');
        }

        $this->getIterator()->rewind();
        while ($this->getIterator()->valid()) {
            $function();
            $this->getIterator()->next();
        }
    }

    private function executeConnection($urlSuffix = '', $XmlBodyRequest = null)
    {
        $config = $this->getCurrent();

        $header_parameters = $this->getHeaderParameters();

        $curl_options = [
            CURLOPT_HTTPHEADER => $header_parameters,
            CURLOPT_HTTPAUTH => CURLAUTH_ANY,
            CURLOPT_HEADER => false,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_ENCODING => '',
            CURLOPT_ACCEPT_ENCODING => 'gzip',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_TCP_KEEPALIVE => 1,
        ];

        if ($XmlBodyRequest ?? false) {
            $curl_options[CURLOPT_POSTFIELDS] = $XmlBodyRequest;
            $curl_options[CURLOPT_TIMEOUT] = 1200;
            $curl_options[CURLOPT_POST] = 1;
        }

        if (!$this->isMServerQuickInstance()) {
            $curl_options[CURLOPT_TIMEOUT] = 60 * 60;
        }

        $urlField = $this->isMServerQuickInstance() ? 'url' : 'url_long';

        $curl = curl_init("{$config[$urlField]}/{$urlSuffix}");
        curl_setopt_array($curl, $curl_options);

        $return = curl_exec($curl);

        if ($error_number = curl_errno($curl)) {
            if (in_array($error_number, [CURLE_OPERATION_TIMEDOUT, CURLE_OPERATION_TIMEOUTED])) {
                throw new PohodaException('mServer neodpověděl v časovém limitu!');
            } else {
                throw new PohodaException("Nedefinová chyba č. - {$error_number}");
            }
        }

        $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        switch ($http_code) {
            case '200':
                break;
            case '404':
                return false;
            default:
                throw new PohodaException("Chyba na straně mServeru: Error - {$http_code} detected. Seznam stavových kódů https://www.stormware.cz/pohoda/xml/mserver/provyvojare/");
        }

        $this->saveLastSuccessConnectionTime();

        curl_close($curl);

        return [
            'code' => $http_code,
            'response' => $return,
        ];
    }

    public function isActive($skip_development = false)
    {
        if (isDevelopment() && ($this->getCurrent()['dev_activate'] ?? 'N') == 'Y') {
            return true;
        }

        if (($this->getCurrent()['active'] ?? 'N') != 'Y' || (isLocalDevelopment() && !$skip_development)) {
            return false;
        }

        $maintenanceFrom = (new \DateTime())->setTimestamp(strtotime($this->getCurrent()['maintenanceFrom'] ?? false))->format(
            'H:i'
        );
        $maintenanceTo = (new \DateTime())->setTimestamp(strtotime($this->getCurrent()['maintenanceTo'] ?? false))->format('H:i');
        $now = (new \DateTime())->format('H:i');
        if (
            ($now >= $maintenanceFrom && $now <= $maintenanceTo)
            || ($maintenanceFrom > $maintenanceTo && ($now <= $maintenanceTo || $now >= $maintenanceFrom))) {
            return false;
        }

        return true;
    }

    /**
     * @return string[]
     */
    public function getHeaderParameters()
    {
        $config = $this->getCurrent();

        $header_parameters = [
            'Content-Type: text/xml',
            'STW-Authorization: Basic '.base64_encode("{$config['username']}:{$config['password']}"),
        ];

        if ($this->getMServerName()) {
            $header_parameters[] = "Stw-Server-Name: {$this->getMServerName()}";
        }

        return $header_parameters;
    }

    /**
     * @return string
     */
    public function getMServerPrefix()
    {
        if ($this->getMServerCount() > 1) {
            return $this->getCurrentKey().'_';
        }

        return '';
    }

    public function setInitiatorPohoda(): void
    {
        $this->loggerInfoHelper->setInitiator(Initiator::Pohoda);
    }
}
