<?php

declare(strict_types=1);

namespace Gls\GlsPoland\AdePlus;

use Gls\GlsPoland\AdePlus\Authorization\AuthorizingCaller;
use Gls\GlsPoland\AdePlus\Fault\FaultConverterInterface;
use Gls\GlsPoland\AdePlus\Fault\FaultConvertingCaller;
use Gls\GlsPoland\Soap\Client\Caller\CallerInterface;
use Gls\GlsPoland\Soap\Client\Caller\EngineCaller;
use Gls\GlsPoland\Soap\Client\Caller\LoggingCaller;
use Gls\GlsPoland\Soap\Client\Soap\DefaultEngineFactory;
use Gls\GlsPoland\Soap\Engine\EngineInterface;
use Gls\GlsPoland\Soap\ExtSoapEngine\Configuration\ClassMap\ClassMapCollection;
use Gls\GlsPoland\Soap\ExtSoapEngine\ExtSoapOptions;
use Psr\Log\LoggerInterface;

final class ClientFactory implements ClientFactoryInterface
{
    private $faultConverter;
    private $testWsdlUrl;
    private $prodWsdlUrl;
    private $integratorId;

    private $classmap;

    public function __construct(FaultConverterInterface $faultConverter, string $testWsdlUrl, string $prodWsdlUrl, string $integratorId)
    {
        $this->faultConverter = $faultConverter;
        $this->integratorId = $integratorId;
        $this->testWsdlUrl = $testWsdlUrl;
        $this->prodWsdlUrl = $prodWsdlUrl;
    }

    public function create(bool $sandbox = false, UserCredentialsInterface $userCredentials = null, LoggerInterface $logger = null): Client
    {
        $caller = $this->createCaller($sandbox, $userCredentials, $logger);

        return new Client($caller, $sandbox);
    }

    private function createCaller(bool $sandbox, ?UserCredentialsInterface $userCredentials, ?LoggerInterface $logger): CallerInterface
    {
        $engine = $this->createEngine($sandbox);
        $caller = new EngineCaller($engine);
        $caller = new FaultConvertingCaller($caller, $this->faultConverter);

        if (null !== $userCredentials) {
            $caller = new AuthorizingCaller($caller, $userCredentials, $this->integratorId);
        }

        if (null !== $logger) {
            $caller = new LoggingCaller($caller, $logger);
        }

        return $caller;
    }

    private function createEngine(bool $sandbox): EngineInterface
    {
        $wsdl = $sandbox ? $this->testWsdlUrl : $this->prodWsdlUrl;
        $options = ExtSoapOptions::defaults($wsdl)->setClassMap($this->getClassMap());

        return DefaultEngineFactory::create($options);
    }

    private function getClassMap(): ClassMapCollection
    {
        return $this->classmap ?? ($this->classmap = ClassMapFactory::getCollection());
    }
}
