<?php
/**
 * GigaChat OAuth2 Auth Manager
 *
 * Manages OAuth2 token exchange with WordPress transient caching.
 * Access tokens are valid for ~30 minutes; we cache them for 25 minutes.
 *
 * @package SmartyPress_AI_Engine
 * @since 1.1.0
 */

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

/**
 * GigaChat Auth Manager class
 */
class SmartyPress_AI_GigaChat_Auth_Manager {

	/**
	 * OAuth token endpoint
	 */
	const OAUTH_ENDPOINT = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth';

	/**
	 * Token cache lifetime in seconds (25 minutes)
	 */
	const TOKEN_LIFETIME = 1500;

	/**
	 * Authorization key (base64 of client_id:client_secret)
	 *
	 * @var string
	 */
	private $auth_key;

	/**
	 * API scope
	 *
	 * @var string
	 */
	private $scope;

	/**
	 * SSL verify setting
	 *
	 * @var bool
	 */
	private $verify_ssl;

	/**
	 * WordPress transient key for caching access token
	 *
	 * @var string
	 */
	private $transient_key;

	/**
	 * Constructor
	 *
	 * @param string $auth_key   Authorization key (base64 of client_id:client_secret).
	 * @param string $scope      API scope (GIGACHAT_API_PERS, GIGACHAT_API_B2B, GIGACHAT_API_CORP).
	 * @param bool   $verify_ssl Whether to verify SSL certificate.
	 * @throws Exception If auth key is empty.
	 */
	public function __construct( $auth_key, $scope = 'GIGACHAT_API_PERS', $verify_ssl = true ) {
		if ( empty( $auth_key ) ) {
			throw new Exception(
				esc_html__( 'GigaChat authorization key cannot be empty.', 'smartypress-ai-engine' )
			);
		}

		$this->auth_key      = $auth_key;
		$this->scope         = $scope;
		$this->verify_ssl    = $verify_ssl;
		$this->transient_key = 'smartypress_gigachat_token_' . md5( $auth_key . $scope );
	}

	/**
	 * Get a valid access token (from cache or by requesting a new one)
	 *
	 * @return string Access token.
	 * @throws Exception On authentication failure.
	 */
	public function get_access_token() {
		// Try cache first.
		$cached = get_transient( $this->transient_key );
		if ( $cached ) {
			return $cached;
		}

		// Generate UUID v4 for RqUID header.
		$rq_uid = $this->generate_uuid_v4();

		$response = wp_remote_post(
			self::OAUTH_ENDPOINT,
			array(
				'headers'   => array(
					'Content-Type'  => 'application/x-www-form-urlencoded',
					'Accept'        => 'application/json',
					'Authorization' => 'Basic ' . $this->auth_key,
					'RqUID'         => $rq_uid,
				),
				'body'      => 'scope=' . $this->scope,
				'timeout'   => 30,
				'sslverify' => $this->verify_ssl,
			)
		);

		if ( is_wp_error( $response ) ) {
			throw new Exception(
				sprintf(
					/* translators: %s: error message */
					esc_html__( 'Failed to get GigaChat access token: %s', 'smartypress-ai-engine' ),
					esc_html( $response->get_error_message() )
				)
			);
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = wp_remote_retrieve_body( $response );
		$data = json_decode( $body, true );

		if ( 200 !== $code ) {
			$error = isset( $data['message'] ) ? $data['message'] : esc_html__( 'Failed to get access token', 'smartypress-ai-engine' );
			throw new Exception(
				sprintf(
					/* translators: %s: error message */
					esc_html__( 'GigaChat OAuth error: %s', 'smartypress-ai-engine' ),
					esc_html( $error )
				)
			);
		}

		$access_token = isset( $data['access_token'] ) ? $data['access_token'] : '';

		if ( empty( $access_token ) ) {
			throw new Exception(
				esc_html__( 'Access token not found in GigaChat response.', 'smartypress-ai-engine' )
			);
		}

		// Calculate cache lifetime based on expires_at if available.
		$cache_lifetime = self::TOKEN_LIFETIME;
		if ( ! empty( $data['expires_at'] ) ) {
			$expires_at = intval( $data['expires_at'] );
			// expires_at may be in milliseconds.
			if ( $expires_at > 9999999999 ) {
				$expires_at = intval( $expires_at / 1000 );
			}
			$remaining = $expires_at - time() - 60; // 60 second buffer.
			if ( $remaining > 0 ) {
				$cache_lifetime = $remaining;
			}
		}

		// Cache the token.
		set_transient( $this->transient_key, $access_token, $cache_lifetime );

		return $access_token;
	}

	/**
	 * Clear cached access token
	 *
	 * Useful when credentials change or on authentication errors.
	 */
	public function clear_cache() {
		delete_transient( $this->transient_key );
	}

	/**
	 * Generate UUID v4
	 *
	 * @return string UUID v4 string.
	 */
	private function generate_uuid_v4() {
		$data = function_exists( 'random_bytes' ) ? random_bytes( 16 ) : openssl_random_pseudo_bytes( 16 );

		// Set version to 0100 (UUID v4).
		$data[6] = chr( ord( $data[6] ) & 0x0f | 0x40 );
		// Set bits 6-7 to 10.
		$data[8] = chr( ord( $data[8] ) & 0x3f | 0x80 );

		return vsprintf( '%s%s-%s-%s-%s-%s%s%s', str_split( bin2hex( $data ), 4 ) );
	}
}
