<?php

namespace BitApps\Pi\src\Authorization\OAuth2;

use BitApps\Pi\Config;
use BitApps\Pi\Helpers\Hash;
use BitApps\Pi\src\Authorization\AbstractBaseAuthorization;

if (!defined('ABSPATH')) {
    exit;
}


class OAuth2Authorization extends AbstractBaseAuthorization
{
    protected $connection;

    private $bodyParams;

    private $tokenDetails;

    private $refreshTokenUrl;

    private $expiresIn;

    public function __construct($connectionId)
    {
        parent::__construct($connectionId);
    }

    public function refreshAccessToken(): array
    {
        $tokenDetails = $this->tokenDetails;

        $response = $this->http->request($this->refreshTokenUrl ?? $tokenDetails['refreshTokenUrl'], 'POST', $this->getRefreshTokenBodyParams($tokenDetails));

        if ($this->http->getResponseCode() !== 200 || isset($response->error)) {
            return [
                'error'    => true,
                'message'  => $response->error ?? 'Token refresh failed',
                'response' => $response,
                'payload'  => $this->bodyParams,
            ];
        }

        $tokenDetails['access_token'] = Hash::encrypt($response->access_token);
        $tokenDetails = apply_filters(Config::VAR_PREFIX . 'before_store_connection_auth_details', $tokenDetails, $this->connection->app_slug ?? '');
        $tokenDetails['generated_at'] = time();

        return $this->updateConnection($this->connection, $tokenDetails);
    }

    public function getAuthDetails(): array
    {
        $tokenDetails = $this->setAuthDetails();

        if (isset($tokenDetails['error'])) {
            return $tokenDetails;
        }

        if ($this->isTokenExpired($tokenDetails['generated_at'], $tokenDetails['expires_in'])) {
            return $this->refreshAccessToken();
        }

        return $this->tokenDetails;
    }

    public function getAccessToken()
    {
        $tokenDetails = $this->setAuthDetails();

        $accessToken = $tokenDetails['access_token'];

        if (isset($tokenDetails['error'])) {
            return $tokenDetails;
        }

        if (isset($tokenDetails['generated_at'], $tokenDetails['expires_in']) && ($this->isTokenExpired($tokenDetails['generated_at'], $tokenDetails['expires_in']))) {
            $newAuthDetails = $this->refreshAccessToken();

            if (isset($newAuthDetails['error'])) {
                return $newAuthDetails;
            }

            $accessToken = $newAuthDetails['access_token'];
        }

        return 'Bearer ' . Hash::decrypt($accessToken);
    }

    public function setBodyParams($bodyParams)
    {
        $this->bodyParams = $bodyParams;

        return $this;
    }

    public function setRefreshTokenUrl($refreshTokenUrl)
    {
        $this->refreshTokenUrl = $refreshTokenUrl;

        return $this;
    }

    private function setAuthDetails()
    {
        $this->connection = $this->getConnection();

        if (!$this->connection) {
            return [
                'error'   => true,
                'message' => 'Connection not found',
            ];
        }

        return $this->tokenDetails = (array) $this->connection->auth_details;
    }

    private function getRefreshTokenBodyParams($tokenDetails)
    {
        if (isset($this->bodyParams) && !empty($this->bodyParams)) {
            return $this->bodyParams;
        }

        $grantType = $tokenDetails['grant_type'] ?? 'authorization_code';

        $body = [
            'grant_type'    => $grantType === 'client_credentials' ? 'client_credentials' : 'refresh_token',
            'client_id'     => $tokenDetails['client_id'],
            'client_secret' => Hash::decrypt($tokenDetails['client_secret']),
        ];

        if (!empty($tokenDetails['refresh_token'])) {
            $body['refresh_token'] = Hash::decrypt($tokenDetails['refresh_token']);
        }

        return $body;
    }
}
