<?php

namespace App\HttpClients\Clients;

use Illuminate\Support\Facades\Http;

use App\HttpClients\DTOs\Hacienda\TokenRequest;
use App\HttpClients\DTOs\Hacienda\TokenResponse;
use App\HttpClients\DTOs\Hacienda\DocStatusResponse;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Validation\UnauthorizedException;

class HaciendaClient 
{
    protected $isSandboxMode = false;

    protected $tokenResponse = null;

    public function useSandboxMode(bool $indicator): void
    {
        $this->isSandboxMode = $indicator;
    }

    public function authenticate(string $username, string $password): void
    {
        $tokenRequest = TokenRequest::from([
            'grant_type'    => 'password',
            'client_id'     => $this->getClientId(),
            'username'      => $username,
            'password'      => $password
        ]);

        $this->requestToken($tokenRequest);
    }

    protected function requestToken(TokenRequest $data): TokenResponse
    {
        try {
            $response = Http::baseUrl($this->getAuthEndpoint())
                ->asForm()
                ->timeout(3)
                ->connectTimeout(5)
                ->withOptions(['verify' => false])
                ->post('/', $data->toArray());

            if ($response->status() === 401) 
            {
                throw new UnauthorizedException('No se ha podido conectar con Hacienda por un tema de credenciales'); 
            } 
            
            if ($response->failed()) 
            { 
                throw new \Exception("No ha sido posible conectar con Hacienda"); 
            }

            $data = $response->json();

            $this->tokenResponse = TokenResponse::from($data);

        } catch (ConnectionException $e) {
            throw new \RuntimeException('No ha sido posible conectar con Hacienda');
        }

        return $this->tokenResponse;
    }

    public function sendDocument(array $payload)
    {
        $this->validateToken();

        $body = [
            'clave'          => $payload['clave'],
            'fecha'          => $payload['fecha'],
            'emisor'         =>  [
                'tipoIdentificacion'   => $payload['emisor']['tipo_identificacion'],
                'numeroIdentificacion' => $payload['emisor']['numero_identificacion'],
            ],
            'comprobanteXml' => $payload['comprobanteXml'],
            'callbackUrl' => 'https://standards-lib-text-ice.trycloudflare.com/api/v1/webhook/mh'
        ];

        if (!empty($payload['receptor'])) 
        { 
            $body['receptor'] = [
                'tipoIdentificacion'   => $payload['receptor']['tipo_identificacion'],
                'numeroIdentificacion' => $payload['receptor']['numero_identificacion'],
            ];
        }

        $response = Http::baseUrl($this->getEndpoint())
            ->timeout(10)
            ->withOptions(['verify' => false])
            ->withToken($this->getToken())
            ->post('recepcion', $body);
        
        return $response;
    }

    public function getDocumentReceptionStatus(string $key): DocStatusResponse   
    {
        $this->validateToken();

        $response = Http::baseUrl($this->getEndpoint())
            ->withToken($this->getToken())
            ->timeout(5)
            ->withOptions(['verify' => false])
            ->throw()
            ->get("/recepcion/{$key}");
        
        $data = DocStatusResponse::from($response->json());

        return $data;
    }

    public function getTokenResponse(): ?TokenResponse
    {
        return $this->tokenResponse;
    }

    public function getToken(): string
    {
        return $this->tokenResponse->access_token;
    }

    public function isAuthenticated(): bool
    {
        if ($this->tokenResponse === null) {
            return false;
        }

        return !$this->tokenResponse->isExpired();
    }

    private function validateToken()
    {
        if ($this->isAuthenticated()) {
            return;
        }

        throw new \Exception('El token de acceso a Hacienda ha expirado o no existe');
    }

    public function getClientId(): string
    {
        return $this->isSandboxMode ? 'api-stag' : 'api-prod';
    }

    public function getEndpoint(): string
    {
        $environment = $this->isSandboxMode ? 'api-sandbox' : 'api';
        $url = "https://{$environment}.comprobanteselectronicos.go.cr/recepcion/v1";

        return $url;
    }

    public function getAuthEndpoint(): string
    {
        $environment = $this->isSandboxMode ? 'rut-stag' : 'rut';
        $url = "https://idp.comprobanteselectronicos.go.cr/auth/realms/{$environment}/protocol/openid-connect/token";

        return $url;
    }
}