<?php

/**
 * Copyright © Resurs Bank AB. All rights reserved.
 * See LICENSE for license details.
 */

declare(strict_types=1);

namespace Resursbank\MerchantApi\Config\Form;

use Exception;
use Module;
use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface;
use Resursbank\Ecom\Lib\Api\Environment;
use Resursbank\MerchantApi\Config\Config;
use Resursbank\MerchantApi\Logger\Logger;
use Throwable;

/**
 * Configuration page data provider.
 */
class DataProvider implements FormDataProviderInterface
{
    /** @var false|Module */
    private false|Module $module;

    /**
     * @param Config $config
     * @param Logger $logger
     */
    public function __construct(
        private readonly Config $config,
        private readonly Logger $logger
    ) {
        $this->module = Module::getInstanceByName('resursbank');
    }

    private function canGetAnnuityFactors(): bool
    {
        return !empty($this->config->getClientId(Environment::TEST)) ||
            !empty($this->config->getClientId(Environment::PROD)) &&
            !empty($this->config->getPartPaymentPaymentMethod());
    }

    /**
     * @inheridoc
     *
     * @return array
     */
    public function getData(): array
    {
        $test = Environment::TEST;
        $prod = Environment::PROD;

        // Determine whether annuity factors data can be included in the configuration during initialization.
        // This depends on the availability of client IDs for either test or production environments,
        // as well as the presence of a configured part payment method.
        $canGetAnnuityFactors = $this->canGetAnnuityFactors();

        $data = [
            'api' => [
                'environment' => $this->config->getEnvironment()->value,
                'store_id' => $this->config->getStoreId(),
                "client_id_$test->value" => $this->config->getClientId($test),
                "client_id_$prod->value" => $this->config->getClientId($prod),
                "aftershop_enabled" => $this->config->isAftershopEnabled(),
            ],
            'partpayment' => [
                'enabled' => $this->config->getPartPaymentEnabled(),
                'threshold' => $this->config->getPartPaymentThreshold(),
                'payment_method' => $canGetAnnuityFactors ? $this->config->getPartPaymentPaymentMethod() : '',
                'annuity_period' => $canGetAnnuityFactors ? $this->config->getPartPaymentAnnuityPeriod() : '',
            ],
            'advanced' => [
                'debug' => $this->config->isDebugEnabled(),
                'xdebug_session' => $this->config->getXdebugSession(),
            ],
            'callbacks' => [
                'ttl' => $this->config->getCallbackTtl(),
            ],
        ];

        return $data;
    }

    /**
     * {@inheritdoc}
     *
     * @todo The parent throws a specific type of Exception which we may wish
     * @todo to utilize, not sure if they are rendered in a specific way.
     */
    public function setData(
        array $data
    ): array {
        return array_filter([
            $this->setEnvironment(data: $data),
            $this->setStore(data: $data),
            $this->setClientId(data: $data, environment: Environment::TEST),
            $this->setClientId(data: $data, environment: Environment::PROD),
            $this->setClientSecret(data: $data, environment: Environment::TEST),
            $this->setClientSecret(data: $data, environment: Environment::PROD),
            $this->setDebug(data: $data),
            $this->setPartPaymentSettings(data: $data),
            $this->setAftershopEnabled(data: $data),
            $this->setCallbackTtl(data: $data),
            $this->setXdebugSession(data: $data),
        ]);
    }

    /**
     * Prepare Xdebug session setting.
     * @param array $data
     * @return string
     */
    private function setXdebugSession(array $data): string
    {
        $error = '';

        try {
            if (isset($data['advanced']['xdebug_session'])) {
                $this->config->setXdebugSession(
                    (string)$data['advanced']['xdebug_session']
                );
            }
        } catch (Exception $e) {
            $this->logger->exception($e);
            $error = $this->module->l('Failed to update Xdebug session value.');
        }

        return $error;
    }

    /**
     * Set part payment settings.
     *
     * @param array $data
     * @return string
     */
    private function setPartPaymentSettings(array $data): string
    {
        $error = '';

        try {
            if (isset($data['partpayment']['enabled'])) {
                $this->config->setPartPaymentEnabled(
                    state: (bool)$data['partpayment']['enabled']
                );
            }
            if (isset($data['partpayment']['payment_method'])) {
                $this->config->setPartPaymentPaymentMethod(
                    methodId: $data['partpayment']['payment_method']
                );
            }
            if (isset($data['partpayment']['annuity_period'])) {
                $this->config->setPartPaymentAnnuityPeriod(
                    period: (int)$data['partpayment']['annuity_period']
                );
            }
            if (isset($data['partpayment']['threshold'])) {
                $this->config->setPartPaymentThreshold(
                    value: (int)$data['partpayment']['threshold']
                );
            }
        } catch (Throwable $error) {
            $this->logger->exception(error: $error);
        }

        return $error;
    }

    /**
     * @param array $data
     *
     * @return string
     */
    private function setEnvironment(
        array $data
    ): string {
        $error = '';

        try {
            if (isset($data['api']['environment'])) {
                $this->config->setEnvironment(
                    Environment::from($data['api']['environment'])
                );
            }
        } catch (Exception $e) {
            $this->logger->exception($e);

            $error = $this->module->l('Failed to update API environment. Check the core log for the actual Exception.');
        }

        return $error;
    }

    /**
     * @param array $data
     *
     * @return string
     */
    private function setCallbackTtl(
        array $data
    ): string {
        $error = '';

        try {
            if (isset($data['callbacks']['ttl'])) {
                $this->config->setCallbackTtl(
                    (int)$data['callbacks']['ttl']
                );
            }
        } catch (Exception $e) {
            $this->logger->exception($e);

            $error = $this->module->l('Failed to update callbacks TTL. Check the core log for the actual Exception.');
        }

        return $error;
    }

    /**
     * @param array $data
     *
     * @return string
     */
    private function setStore(
        array $data
    ): string {
        $error = '';

        try {
            if (isset($data['api']['store_id'])) {
                $this->config->setStoreId($data['api']['store_id']);
            }
        } catch (Exception $e) {
            $this->logger->exception($e);

            $error = $this->module->l('Failed to update API store. Check the core log for the actual Exception.');
        }

        return $error;
    }

    /**
     * @param array $data
     * @param Environment $environment
     *
     * @return string
     */
    private function setClientId(
        array $data,
        Environment $environment
    ): string {
        $error = '';

        try {
            if (isset($data['api']["client_id_$environment->value"])) {
                $this->config->setClientId(
                    $data['api']["client_id_$environment->value"],
                    $environment
                );
            }
        } catch (Exception $e) {
            $this->logger->exception($e);

            $error = $this->module->l('Failed to update API client_id for environment') . ' ' . $environment->name;
        }

        return $error;
    }

    /**
     * @param array $data
     * @param Environment $environment
     *
     * @return string
     */
    private function setClientSecret(
        array $data,
        Environment $environment
    ): string {
        $error = '';

        try {
            if (
                isset($data['api']["client_secret_$environment->value"]) &&
                (string)$data['api']["client_secret_$environment->value"] !== ''
            ) {
                $this->config->setClientSecret(
                    $data['api']["client_secret_$environment->value"],
                    $environment
                );
            }
        } catch (Exception $e) {
            $this->logger->exception($e);

            $error = $this->module->l('Failed to update API clientSecret for environment') . ' ' . $environment->name;
        }

        return $error;
    }

    /**
     * @param array $data
     *
     * @return string
     */
    private function setDebug(
        array $data
    ): string {
        $error = '';

        try {
            if (isset($data['advanced']['debug'])) {
                $this->config->setIsDebugEnabled(
                    (int)$data['advanced']['debug'] === 1
                );
            }
        } catch (Exception $e) {
            $this->logger->exception($e);

            $error = $this->module->l('Failed to update debug mode.');
        }

        return $error;
    }

    /**
     * @param array $data
     *
     * @return string
     */
    private function setAftershopEnabled(
        array $data
    ): string {
        $error = '';

        try {
            if (isset($data['api']['aftershop_enabled'])) {
                $this->config->setIsAftershopEnabled(
                    (int)$data['api']['aftershop_enabled'] === 1
                );
            }
        } catch (Exception $e) {
            $this->logger->exception($e);

            $error = $this->module->l('Failed to update aftershop setting.');
        }

        return $error;
    }
}
