<?php
/**
 * Google OAuth 2.0 client.
 *
 * @package StoreSheet
 */

declare( strict_types=1 );

namespace StoreSheet\API;

defined( 'ABSPATH' ) || exit;

/**
 * Handles Google OAuth 2.0 token lifecycle.
 *
 * Scopes used:
 *   - https://www.googleapis.com/auth/drive.file       (read/write files created by this app)
 *   - https://www.googleapis.com/auth/spreadsheets     (read/write spreadsheet data)
 */
class GoogleOAuthClient extends ApiClient {

	private const AUTH_URL     = 'https://accounts.google.com/o/oauth2/v2/auth';
	private const TOKEN_URL    = 'https://oauth2.googleapis.com/token';
	private const REVOKE_URL   = 'https://oauth2.googleapis.com/revoke';
	private const USERINFO_URL = 'https://www.googleapis.com/oauth2/v3/userinfo';
	private const CACHE_KEY    = 'storesheet_access_token';

	private const SCOPES = array(
		'https://www.googleapis.com/auth/drive.file',
		'https://www.googleapis.com/auth/spreadsheets',
	);

	private string $client_id;
	private string $client_secret;

	public function __construct() {
		$settings            = get_option( 'storesheet_settings', array() );
		$this->client_id     = $settings['oauth_client_id'] ?? '';
		$this->client_secret = $settings['oauth_client_secret'] ?? '';
	}

	/**
	 * Build the Google consent screen URL.
	 *
	 * @param string $redirect_uri The callback URL (WP REST endpoint).
	 * @param string $state        CSRF nonce.
	 * @return string|\WP_Error
	 */
	public function get_auth_url( string $redirect_uri, string $state ) {
		if ( empty( $this->client_id ) ) {
			return new \WP_Error( 'missing_client_id', __( 'Google OAuth Client ID is not configured.', 'storesheet' ) );
		}

		/**
		 * Filter the OAuth scopes requested at consent time. The free build asks
		 * only for drive.file + spreadsheets; the Pro build adds the Apps Script
		 * scopes it needs to auto-deploy the edit-back trigger, so free users are
		 * never asked to grant script access they don't use.
		 *
		 * @since 1.1.0
		 * @param string[] $scopes Default free scopes.
		 */
		$scopes = apply_filters( 'storesheet_oauth_scopes', self::SCOPES );

		return add_query_arg(
			array(
				'client_id'     => $this->client_id,
				'redirect_uri'  => $redirect_uri,
				'response_type' => 'code',
				'scope'         => implode( ' ', array_unique( (array) $scopes ) ),
				'access_type'   => 'offline',
				'prompt'        => 'consent',
				'state'         => $state,
			),
			self::AUTH_URL
		);
	}

	/**
	 * Exchange authorization code for tokens.
	 *
	 * @param string $code         Code from Google callback.
	 * @param string $redirect_uri Must match the original request.
	 * @return array|\WP_Error Token response array on success.
	 */
	public function exchange_code( string $code, string $redirect_uri ) {
		return $this->http_post(
			self::TOKEN_URL,
			array(
				'body' => array(
					'code'          => $code,
					'client_id'     => $this->client_id,
					'client_secret' => $this->client_secret,
					'redirect_uri'  => $redirect_uri,
					'grant_type'    => 'authorization_code',
				),
			)
		);
	}

	/**
	 * Return a valid access token, refreshing if needed.
	 *
	 * @return string|\WP_Error
	 */
	public function get_access_token() {
		$cached = get_transient( self::CACHE_KEY );
		if ( $cached ) {
			return $cached;
		}

		$settings      = get_option( 'storesheet_settings', array() );
		$refresh_token = $settings['oauth_refresh_token'] ?? '';

		if ( empty( $refresh_token ) ) {
			return new \WP_Error( 'not_connected', __( 'Google account not connected. Please complete OAuth setup.', 'storesheet' ) );
		}

		$data = $this->http_post(
			self::TOKEN_URL,
			array(
				'body' => array(
					'refresh_token' => $refresh_token,
					'client_id'     => $this->client_id,
					'client_secret' => $this->client_secret,
					'grant_type'    => 'refresh_token',
				),
			)
		);

		if ( is_wp_error( $data ) ) {
			return $data;
		}

		$access_token = $data['access_token'] ?? '';
		$expires_in   = (int) ( $data['expires_in'] ?? 3600 );

		set_transient( self::CACHE_KEY, $access_token, $expires_in - 60 );

		return $access_token;
	}

	/**
	 * Revoke the stored refresh token and clear stored credentials.
	 *
	 * @return bool
	 */
	public function revoke() {
		$settings      = get_option( 'storesheet_settings', array() );
		$refresh_token = $settings['oauth_refresh_token'] ?? '';

		if ( $refresh_token ) {
			wp_remote_post(
				self::REVOKE_URL,
				array(
					'timeout' => 15,
					'body'    => array( 'token' => $refresh_token ),
				)
			);
		}

		$this->clear_tokens();

		return true;
	}

	/**
	 * Return connected status and account email (if connected).
	 *
	 * @return array{connected: bool, email: string}
	 */
	public function get_status(): array {
		$settings  = get_option( 'storesheet_settings', array() );
		$connected = ! empty( $settings['oauth_refresh_token'] );

		return array(
			'connected' => $connected,
			'email'     => $connected ? ( $settings['oauth_account_email'] ?? '' ) : '',
		);
	}

	/**
	 * Fetch and store the connected account email.
	 *
	 * @param string $access_token Fresh access token.
	 * @return void
	 */
	public function store_account_email( string $access_token ): void {
		$response = wp_remote_get(
			self::USERINFO_URL,
			array(
				'timeout' => 15,
				'headers' => array( 'Authorization' => 'Bearer ' . $access_token ),
			)
		);

		if ( is_wp_error( $response ) ) {
			return;
		}

		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( ! empty( $body['email'] ) ) {
			$settings                        = get_option( 'storesheet_settings', array() );
			$settings['oauth_account_email'] = sanitize_email( $body['email'] );
			update_option( 'storesheet_settings', $settings );
		}
	}

	/**
	 * Store tokens returned from exchange_code().
	 *
	 * @param array $tokens Token data from Google.
	 */
	public function store_tokens( array $tokens ): void {
		$settings = get_option( 'storesheet_settings', array() );

		if ( ! empty( $tokens['refresh_token'] ) ) {
			$settings['oauth_refresh_token'] = sanitize_text_field( $tokens['refresh_token'] );
		}

		$settings['oauth_token_exists'] = true;
		update_option( 'storesheet_settings', $settings );

		delete_transient( self::CACHE_KEY );

		if ( ! empty( $tokens['access_token'] ) ) {
			$this->store_account_email( $tokens['access_token'] );
		}
	}

	private function clear_tokens(): void {
		$settings = get_option( 'storesheet_settings', array() );
		unset( $settings['oauth_refresh_token'], $settings['oauth_account_email'] );
		$settings['oauth_token_exists'] = false;
		update_option( 'storesheet_settings', $settings );
		delete_transient( self::CACHE_KEY );
	}

	/**
	 * @param int          $code HTTP status code.
	 * @param array<mixed> $data Decoded response body.
	 */
	protected function error_from_response( int $code, array $data ): \WP_Error {
		return new \WP_Error(
			'google_oauth_error',
			$data['error_description'] ?? $data['error'] ?? 'Unknown Google OAuth error',
			array( 'status' => $code )
		);
	}
}
