<?php
/**
 * The Paychef client API basic class file
 * @created   for Paychef API
 * @modified  2024 by Roberto Coria <support@paychef.com>
 * @since     v1.0
 */

namespace Paychef;

/**
 * All interactions with the API can be done with an instance of this class.
 * @package Paychef
 */
class Paychef
{
    /**
     * @var Communicator The object for the communication wrapper.
     */
    protected $communicator;
    private $logger;

    /**
     * Generates an API object to use for the whole interaction with Paychef.
     *
     * @param string $instance The name of the Paychef instance.
     * @param string $apiSecret The API secret which can be found in the Paychef administration.
     * @param string $communicationHandler The preferred communication handler. Default is cURL.
     * @param string $apiBaseDomain The base domain of the API URL.
     * @param string $version The version of the API to use.
     *
     * @throws PaychefException
     */
    public function __construct($instance, $apiSecret, $communicationHandler = '', $apiBaseDomain = Communicator::API_URL_BASE_DOMAIN, $version = null)
    {
        $this->communicator = new Communicator(
            $instance,
            $apiSecret,
            $communicationHandler ?: Communicator::DEFAULT_COMMUNICATION_HANDLER,
            $apiBaseDomain,
            $version
        );
    }

    /**
     * This method passes header to the request.
     * The format of the elements needs to be like this: 'Content-type: multipart/form-data'
     */
    public function setHttpHeaders(array $header): void
    {
        $this->communicator->httpHeaders = $header;
    }

    /**
     * This method returns the version of the API communicator which is the API version used for this
     * application.
     *
     * @return string The version of the API communicator
     */
    public function getVersion()
    {
        return $this->communicator->getVersion();
    }

    /**
     * This magic method is used to call any method available in communication object.
     *
     * @param string $method The name of the method called.
     * @param array $args The arguments passed to the method call. There can only be one argument which is the model.
     *
     * @return \Paychef\Models\Response\Base[]|\Paychef\Models\Response\Base
     * @throws \Paychef\PaychefException The model argument is missing or the method is not implemented
     */
    public function __call($method, $args)
    {
        if (!$this->communicator->methodAvailable($method)) {
            $this->log('error', 'Method not implemented', ['method' => $method]);
            throw new PaychefException('Method ' . $method . ' not implemented');
        }
        if (empty($args)) {
            $this->log('error', 'Argument model is missing', ['method' => $method]);
            throw new PaychefException('Argument model is missing');
        }
        $model = current($args);
        try {
            $result = $this->communicator->performApiRequest($method, $model);
            $this->log('info', 'API request performed successfully', [
                'method' => $method,
                'model' => get_class($model)
            ]);
            return $result;
        } catch (PaychefException $e) {
            $this->log('error', 'API request failed', [
                'method' => $method,
                'model' => get_class($model),
                'error' => $e->getMessage(),
                'reason' => $e->getReason()
            ]);
            throw $e;
        }
    }

    /**
     * Loggt eine Nachricht
     *
     * @param string $level Log-Level (z.B. 'info', 'error', 'debug')
     * @param string $message Die zu loggende Nachricht
     * @param array $context Zusätzliche Kontextinformationen
     */
    protected function log($level, $message, array $context = [])
    {
        if ($this->logger) {
            call_user_func($this->logger, $level, $message, $context);
        } elseif (function_exists('error_log')) {
            error_log(sprintf('[Paychef %s] %s %s',
                strtoupper($level),
                $message,
                !empty($context) ? ' Context: ' . json_encode($context) : ''
            ));
        }
    }


}
