<?php

namespace App\Client;

use App\Interface\ClientInterface;
use GuzzleHttp\ClientInterface as GuzzleInterface;

class MangadexClient implements ClientInterface
{
    private const AUTHENTICATION_URL = 'https://auth.mangadex.org/realms/mangadex/protocol/openid-connect/token';
    private const API_URL = 'https://api.mangadex.org';
    private GuzzleInterface $httpClient;
    private string $clientId;
    private string $clientSecret;
    private string $username;
    private string $password;
    private ?string $accessToken = null;
    private ?string $refreshToken = null;

    public function __construct(GuzzleInterface $httpClient, string $clientId, string $clientSecret, string $username, string $password)
    {
        $this->httpClient = $httpClient;
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->username = $username;
        $this->password = $password;
        $this->authenticate();
    }

    public function authenticate(): void
    {
        $response = $this->httpClient->request('POST', self::AUTHENTICATION_URL, [
            'form_params' => [
                'grant_type' => 'password',
                'username' => $this->username,
                'password' => $this->password,
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
            ],
        ]);

        $data = json_decode($response->getBody()->getContents(), true);
        $this->accessToken = $data['access_token'];
        $this->refreshToken = $data['refresh_token'];
    }

    public function refresh(): void
    {
        $response = $this->httpClient->request('POST', self::AUTHENTICATION_URL, [
            'form_params' => [
                'grant_type' => 'refresh_token',
                'refresh_token' => $this->refreshToken,
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
            ],
        ]);

        $data = json_decode($response->getBody()->getContents(), true);
        $this->accessToken = $data['access_token'];
    }

    private function request(string $method, string $endpoint, array $options = []): array
    {
        $options['headers']['Authorization'] = 'Bearer ' . $this->accessToken;

        $response = $this->httpClient->request($method, self::API_URL . $endpoint, $options);

        if ($response->getStatusCode() === 429) {
            $this->refresh();
            $options['headers']['Authorization'] = 'Bearer ' . $this->accessToken;
            $response = $this->httpClient->request($method, self::API_URL . $endpoint, $options);
        }

        return json_decode($response->getBody()->getContents(), true);
    }

    public function get(string $endpoint, array $params = []): array
    {
        return $this->request('GET', $endpoint, ['query' => $params]);
    }

    public function post(string $endpoint, array $data): array
    {
        return $this->request('POST', $endpoint, ['json' => $data]);
    }
}
