<?php
namespace Zeedhi\ZhuLog\Logger;

use Zeedhi\ZhuLog\Logger\Persistence\PersistenceInterface;
use Zeedhi\ZhuLog\Logger\LoggerInfoProvider;
use Zeedhi\ZhuLog\Logger\Processor\Processor;
use Zeedhi\Framework\DTO\Request;
use Zeedhi\Framework\HTTP;

class Logger {

    /** @var LoggerInterface */
    protected $persistenceStrategy;
    /** @var PersistenceInterface */
    protected $loggerInfoProvider;
    /** @var Processor */
    protected $processor;

    /** @var string */
    protected $requestType;
    /** @var bool */
    protected $requestSkipped = false;

    /**
     * __construct
     *
     * @param PersistenceInterface $persistenceStrategy
     * @param LoggerInfoProvider   $loggerInfoProvider
     * @param Processor|null       $processor
     */
    public function __construct(PersistenceInterface $persistenceStrategy, LoggerInfoProvider $loggerInfoProvider, Processor $processor = null) {
        $this->persistenceStrategy = $persistenceStrategy;
        $this->loggerInfoProvider  = $loggerInfoProvider;
        $this->processor           = $processor;
    }

    /**
     * Logs the request.
     * 
     * @param string  $uri
     * @param string  $method
     * @param string  $requestType
     *
     * @throws Exception
     */
    public function logRequest(string $uri, string $method, string $requestType) {
        $this->requestSkipped = false;
        try {
            $request = HTTP\Request::initFromGlobals();
            $content = $request->getContent();
            $requestContent = $this->processRequestContent($content, $uri, $method);
            $userData = $this->getUserData();
            $contextData = $this->getContextData();

            $this->persistenceStrategy->logRequest($method, $uri, $requestType, $requestContent, ...$userData, ...$contextData);
        } catch (Exception $e) {
            if ($e->getCode() === Exception::SKIP_EXCEPTION) {
                $this->requestSkipped = true;
            } else {
                throw $e;
            }
        }
    }

    /**
     * Gets user data as numeric array.
     *
     * @return array [userId, organizationId, productId]
     */
    public function getUserData(){
        $userData[] = $this->loggerInfoProvider->getUserId();
        $userData[] = $this->loggerInfoProvider->getOrganizationId();
        $userData[] = $this->loggerInfoProvider->getProductId();
        return $userData;
    }
    /**
     * Gets context data as numeric array.
     *
     * @return array [containerName, widgetName]
     */
    public function getContextData(){
        $contextData[] = $this->loggerInfoProvider->getContainerName();
        $contextData[] = $this->loggerInfoProvider->getWidgetName();
        return $contextData;
    }

    /**
     * Logs the response.
     * 
     * @param int $status
     * @param array $content
     */
    public function logResponse(int $status, array $content) {
        if (!$this->requestSkipped) {
            $responseContent = $this->processResponseContent($content);
            $this->persistenceStrategy->logResponse($status, $responseContent);
        }
    }

    /**
     * Logs the exception.
     * 
     * @param \Exception $exception
     * @param string  $uri
     * @param string  $method
     * @param string  $requestType
     */
    public function logException(\Exception $exception, string $uri, string $method, string $requestType) {
        $exceptionContent = $this->processExceptionContent($exception->getMessage());
        $userData = $this->getUserData();
        $contextData = $this->getContextData();
        $this->persistenceStrategy->logException($method, $uri, $requestType, $exceptionContent, ...$userData, ...$contextData);
    }

    /**
     * Retrieves the built content as a json.
     * 
     * @param string $request
     * @param string $route
     * @param string $method
     *
     * @return string|null
     */
    protected function processRequestContent(string $request, string $route, string $method) {
        $processedRequest = null;

        if ($this->processor) {
            $json_decode = json_decode($request, true) ?: [];
            $processedRequest = json_encode($this->processor->processRequest($json_decode, $route, $method));
        }

        return $processedRequest;
    }

    /**
     * Retrieves the built content as a json.
     * 
     * @param array $content
     *
     * @return string|null
     */
    protected function processResponseContent(array $content) {
        if ($this->processor) {
            $content = $this->processor->processResponse($content);
        }

        return json_encode($content) ?: 'Error encoding response: ' . json_last_error_msg();
    }

    /**
     * Retrieves the built content as a json.
     * 
     * @param string $message The exception message.
     *
     * @return string|null
     */
    protected function processExceptionContent(string $content) {
        if ($this->processor) {
            $content = $this->processor->processException($content);
        }

        return json_encode($content) ?: 'Error encoding response: ' . json_last_error_msg();
    }
}