<?php
namespace Incoviba\Service\Contabilidad;

use DateTimeInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Implement\Exception;
use Incoviba\Repository;
use Incoviba\Service;
use Incoviba\Common\Ideal;

class Nubox extends Ideal\Service
{
    public function __construct(protected LoggerInterface $logger,
                                protected Repository\Nubox $nuboxRepository,
                                protected Service\Redis $redisService,
                                protected ClientInterface $client,
                                protected RequestFactoryInterface $requestFactory,
                                protected string $api_url)
    {
        parent::__construct($logger);
    }

    protected array $tokens;
    public function getToken(int $inmobiliaria_rut): string
    {
        if (!isset($this->tokens[$inmobiliaria_rut])) {
            $redisKey = "token_nubox:{$inmobiliaria_rut}";
            try {
                $this->tokens[$inmobiliaria_rut] = $this->redisService->get($redisKey);
            } catch (Exception\EmptyRedis) {
                $nubox = $this->nuboxRepository->fetchByInmobiliaria($inmobiliaria_rut);
                $request = $this->requestFactory
                    ->createRequest('POST', implode('/', [$this->api_url, 'autenticar']))
                    ->withHeader('Authorization', "Basic {$nubox->getLogin()}")
                    ->withHeader('Content-Type', 'application/json')
                    ->withHeader('Accept', 'application/json');
                $response = $this->client->sendRequest($request);
                if ($response->getStatusCode() !== 200) {
                    throw new Exception\HttpResponse($response->getReasonPhrase(), $response->getStatusCode());
                }
                $sistemas = json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);

                $this->setToken($inmobiliaria_rut, $response->getHeaderLine('Token'))
                    ->setSistemas($inmobiliaria_rut, $sistemas['Sistemas']);
            }
        }
        return $this->tokens[$inmobiliaria_rut];
    }
    public function setToken(int $inmobiliaria_rut, string $token): Nubox
    {
        $this->tokens[$inmobiliaria_rut] = $token;

        $redisKey = "token_nubox:{$inmobiliaria_rut}";
        $this->redisService->set($redisKey, $this->tokens[$inmobiliaria_rut], 60 * 15);

        return $this;
    }

    protected array $sistemas;
    public function getSistemas(int $inmobiliaria_rut): array
    {
        if (!isset($this->sistemas[$inmobiliaria_rut])) {
            $redisKey = "nubox:{$inmobiliaria_rut}";
            $this->sistemas[$inmobiliaria_rut] = json_decode($this->redisService->get($redisKey));
        }
        return $this->sistemas[$inmobiliaria_rut];
    }
    public function setSistemas(int $inmobiliaria_rut, array $sistemas): Nubox
    {
        $this->sistemas[$inmobiliaria_rut] = $sistemas;

        $redisKey = "nubox:{$inmobiliaria_rut}";
        $this->redisService->set($redisKey, json_encode($sistemas));

        return $this;
    }

    public function getLibroMayor(int $inmobiliaria_rut, DateTimeInterface $from, DateTimeInterface $to): array
    {
        $inmobiliaria = $this->nuboxRepository->fetchByInmobiliaria($inmobiliaria_rut);
        $query = [
            'NumeroSerie' => 1,
            'CodigoEmpresa' => $inmobiliaria->alias,
            'DDInicio' => $from->format('j'),
            'MMInicio' => $from->format('n'),
            'YYInicio' => $from->format('Y'),
            'DDTermino' => $to->format('j'),
            'MMTermino' => $to->format('n'),
            'YYTermino' => $to->format('Y'),
            'CodigoCentroDeCosto' => 0,
            'CodigoSucursal' => 0,
            'CodigoCuenta' => 0,
            'ModoIFRS' => 'false',
            'CuentasConSaldo' => 'false',
            'IncluirCodigoCuenta' => 'true',
        ];
        $uri = 'contabilidad/libro-mayor?' . http_build_query($query);
        $response = $this->send($uri, $inmobiliaria_rut);
        if ($response->getStatusCode() !== 200) {
            throw new Exception\HttpResponse($response->getReasonPhrase(), $response->getStatusCode());
        }
        return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
    }
    public function getLibroDiario(int $inmobiliaria_rut, DateTimeInterface $from, DateTimeInterface $to): array
    {
        $inmobiliaria = $this->nuboxRepository->fetchByInmobiliaria($inmobiliaria_rut);
        $query = [
            'ModoIFRS' => 0,
            'DDInicio' => $from->format('j'),
            'MMInicio' => $from->format('n'),
            'YYInicio' => $from->format('Y'),
            'DDTermino' => $to->format('j'),
            'MMTermino' => $to->format('n'),
            'YYTermino' => $to->format('Y'),
            'Sucursal' => 0,
            'codigoEmpresa' => $inmobiliaria->alias,
            'NumeroSerie' => 1,
            'CuentasConSaldo' => 0,
            'incluirCodigoCuenta' => 1
        ];
        $uri = 'contabilidad/libro-diario?' . http_build_query($query);
        $response = $this->send($uri, $inmobiliaria_rut);
        if ($response->getStatusCode() !== 200) {
            $this->logger->debug($uri);
            throw new Exception\HttpResponse($response->getReasonPhrase(), $response->getStatusCode());
        }
        return json_decode($response->getBody()->getContents(), JSON_OBJECT_AS_ARRAY);
    }

    private function send(string $uri, int $inmobiliaria_rut, string $method = 'GET', ?StreamInterface $body = null): ResponseInterface
    {
        $request = $this->requestFactory
            ->createRequest($method, implode('/', [$this->api_url, $uri]))
            ->withHeader('token', $this->getToken($inmobiliaria_rut));
        if ($body !== null) {
            $request = $request->withBody($body);
        }
        return $this->client->sendRequest($request);
    }
}
