<?php

declare(strict_types=1);

namespace Gls\GlsPoland\Configuration\Api;

use Gls\GlsPoland\AdePlus\UserCredentials;
use Gls\GlsPoland\AdePlus\UserCredentialsInterface;
use Gls\GlsPoland\Configuration\ConfigurationInterface as Persistence;

final class PersistentConfiguration implements PersistentConfigurationInterface
{
    private const PROD_USERNAME = 'GLS_POLAND_API_PROD_USERNAME';
    private const PROD_PASSWORD = 'GLS_POLAND_API_PROD_PASSWORD';
    private const TEST_USERNAME = 'GLS_POLAND_API_TEST_USERNAME';
    private const TEST_PASSWORD = 'GLS_POLAND_API_TEST_PASSWORD';
    private const SANDBOX_ENABLED = 'GLS_POLAND_API_SANDBOX_ENABLED';
    private const INTEGRATOR_ID = 'GLS_POLAND_ADE_PLUS_INTEGRATOR_ID';

    private $configuration;

    private $prodCredentials;
    private $testCredentials;

    public function __construct(Persistence $configuration)
    {
        $this->configuration = $configuration;
    }

    public function isSandboxEnabled(): bool
    {
        return (bool) $this->configuration->get(self::SANDBOX_ENABLED);
    }

    public function setSandboxEnabled(bool $enabled): void
    {
        $this->configuration->set(self::SANDBOX_ENABLED, $enabled);
    }

    public function getIntegratorId(): ?string
    {
        $value = $this->configuration->get(self::INTEGRATOR_ID);
        if (null === $value || '' === trim((string) $value)) {
            $env = $_SERVER[self::INTEGRATOR_ID] ?? $_ENV[self::INTEGRATOR_ID] ?? getenv(self::INTEGRATOR_ID);

            return is_string($env) && '' !== trim($env)
                ? trim($env)
                : null;
        }

        return trim((string) $value);
    }

    public function setIntegratorId(?string $integratorId): void
    {
        $normalized = null !== $integratorId ? trim($integratorId) : null;

        $this->configuration->set(self::INTEGRATOR_ID, '' === $normalized ? null : $normalized);
    }

    public function getCredentials(): ?UserCredentialsInterface
    {
        return $this->isSandboxEnabled()
            ? $this->getTestCredentials()
            : $this->getProdCredentials();
    }

    public function getProdCredentials(): ?UserCredentialsInterface
    {
        if (!isset($this->prodCredentials)) {
            $username = $this->configuration->get(self::PROD_USERNAME);
            $password = $this->configuration->get(self::PROD_PASSWORD);

            $this->prodCredentials = null !== $username && null !== $password
                ? new UserCredentials((string) $username, (string) $password)
                : false;
        }

        return $this->prodCredentials ?: null;
    }

    public function setProdCredentials(?UserCredentialsInterface $credentials): void
    {
        if ($credentials === $this->getProdCredentials()) {
            return;
        }

        $this->configuration->set(self::PROD_USERNAME, $credentials ? $credentials->getUsername() : null);
        $this->configuration->set(self::PROD_PASSWORD, $credentials ? $credentials->getPassword() : null);

        $this->prodCredentials = $credentials ?? false;
    }

    public function getTestCredentials(): ?UserCredentialsInterface
    {
        if (!isset($this->testCredentials)) {
            $username = $this->configuration->get(self::TEST_USERNAME);
            $password = $this->configuration->get(self::TEST_PASSWORD);

            $this->testCredentials = null !== $username && null !== $password
                ? new UserCredentials((string) $username, (string) $password)
                : false;
        }

        return $this->testCredentials ?: null;
    }

    public function setTestCredentials(?UserCredentialsInterface $credentials): void
    {
        if ($credentials === $this->getTestCredentials()) {
            return;
        }

        $this->configuration->set(self::TEST_USERNAME, $credentials ? $credentials->getUsername() : null);
        $this->configuration->set(self::TEST_PASSWORD, $credentials ? $credentials->getPassword() : null);

        $this->testCredentials = $credentials ?? false;
    }

    public function asNonPersistent(): ConfigurationInterface
    {
        return InMemoryConfiguration::from($this);
    }

    public function save(ConfigurationInterface $configuration): void
    {
        $this->setProdCredentials($configuration->getProdCredentials());
        $this->setTestCredentials($configuration->getTestCredentials());
        $this->setSandboxEnabled($configuration->isSandboxEnabled());
        $this->setIntegratorId($configuration->getIntegratorId());
    }
}
