<?php
/**
 * Google OAuth2 provider.
 *
 * @package GoogleSignIn
 */

namespace GoogleSignIn;

use WP_PowerSuite\Core\Contracts\ModuleSettingsRepository;
use WP_PowerSuite\OAuth\AbstractProvider;
use WP_PowerSuite\OAuth\Helpers\UrlHelper;
use WP_PowerSuite\OAuth\StateManager;
use WP_PowerSuite\OAuth\TokenHandler;
use WP_PowerSuite\OAuth\UI\ButtonRenderer;
use WP_PowerSuite\OAuth\Accounts\AccountLinkSettings;
use WP_PowerSuite\OAuth\UI\ButtonStyleHelper;

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

final class Provider extends AbstractProvider {

	public const USER_META = 'wpps_google_id';

	/** @var array<string, mixed>|null Parsed module settings cache. */
	private ?array $settings_cache = null;

	public function __construct( ModuleSettingsRepository $settings, ?StateManager $state = null, ?TokenHandler $token = null ) {
		parent::__construct( $settings );
		if ( $state ) {
			$this->state = $state;
		}
		if ( $token ) {
			$this->tasks = $token;
		}
	}

	public function getProviderSlug(): string {
		return 'google';
	}

	public function getModuleId(): string {
		return 'google-sign-in';
	}

	public function getRedirectUri(): string {
		return UrlHelper::callbackUrl( $this->getProviderSlug() );
	}

	public function getLegacyAction(): string {
		return 'wpps_google_signin_callback';
	}

	protected function errorQueryArg(): string {
		return 'wpps_google_error';
	}

	/**
	 * @return array<string, mixed>
	 */
	public function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}
		$raw = $this->settings->all( [] );
		// store_avatar_url: on by default; explicit false/0 in DB is respected for legacy.
		$store_avatar = ! array_key_exists( 'store_avatar_url', $raw ) || ! empty( $raw['store_avatar_url'] );

		$this->settings_cache = [
			'client_id'                   => trim( (string) ( $raw['client_id'] ?? '' ) ),
			'client_secret'               => trim( (string) ( $raw['client_secret'] ?? '' ) ),
			'auto_register'               => ! empty( $raw['auto_register'] ),
			'bypass_registration_setting' => ! empty( $raw['bypass_registration_setting'] ),
			'default_role'                => in_array( $raw['default_role'] ?? 'subscriber', [ 'subscriber', 'contributor', 'author', 'editor' ], true ) ? ( $raw['default_role'] ?? 'subscriber' ) : 'subscriber',
			'redirect_after_login'        => trim( (string) ( $raw['redirect_after_login'] ?? '' ) ),
			'button_style'                => ButtonStyleHelper::sanitize( (string) ( $raw['button_style'] ?? 'default' ) ),
			'button_display'              => ButtonStyleHelper::sanitizeDisplay( (string) ( $raw['button_display'] ?? 'label' ) ),
			'button_label_inline'         => ButtonStyleHelper::sanitizeLabelInline( $raw['button_label_inline'] ?? false ),
			'store_avatar_url'            => $store_avatar,
			'woo_login'                   => ! empty( $raw['woo_login'] ),
		];
		return $this->settings_cache;
	}

	public function getAuthorizationUrl( string $redirect_after = '', int $link_user_id = 0 ): string {
		$s  = $this->getSettings();
		$cid = $s['client_id'] ?? '';
		if ( $cid === '' ) {
			return '';
		}
		$st = $this->state->createState( $this->getStateStorageKey(), $redirect_after, $link_user_id );
		return add_query_arg(
			[
				'client_id'     => $cid,
				'redirect_uri'  => $this->getRedirectUri(),
				'response_type' => 'code',
				'scope'         => Config::SCOPES,
				'access_type'   => 'online',
				'state'         => $st['state'],
				'prompt'        => 'select_account',
			],
			Config::AUTH_URL
		);
	}

	/**
	 * @return array<string, mixed>|\WP_Error
	 */
	public function exchangeCodeForProfile( string $code, string $redirect_uri_used ): array|\WP_Error {
		$s   = $this->getSettings();
		$cid = $s['client_id'] ?? '';
		$sec = $s['client_secret'] ?? '';
		$data = $this->tasks->postForm(
			Config::TOKEN_URL,
			[
				'code'          => $code,
				'client_id'     => $cid,
				'client_secret' => $sec,
				'redirect_uri'  => $redirect_uri_used,
				'grant_type'    => 'authorization_code',
			]
		);
		if ( is_wp_error( $data ) ) {
			return $data;
		}
		$at = (string) ( $data['access_token'] ?? '' );
		if ( $at === '' ) {
			return new \WP_Error( 'token', __( 'Failed to get access token from Google.', 'wp-powersuite' ) );
		}
		$info = $this->tasks->getJson( Config::USERINFO, [ 'Authorization' => 'Bearer ' . $at ] );
		if ( is_wp_error( $info ) ) {
			return $info;
		}
		$profile = OidcProfile::fromClaims( $info );
		if ( $profile === null && ! empty( $data['id_token'] ) && is_string( $data['id_token'] ) ) {
			$claims = OidcProfile::decodeJwtPayload( $data['id_token'] );
			if ( is_array( $claims ) ) {
				$profile = OidcProfile::fromClaims( $claims );
			}
		}
		if ( $profile === null ) {
			return new \WP_Error( 'profile', __( 'Could not read Google profile or email.', 'wp-powersuite' ) );
		}
		if ( ! $profile['email_verified'] ) {
			return new \WP_Error( 'email_unverified', __( 'Google did not verify this email address. Use a verified Google account.', 'wp-powersuite' ) );
		}
		return $profile;
	}

	/**
	 * @return array<string, mixed>
	 */
	public function getUserResolverOptions(): array {
		$s = $this->getSettings();
		$el = AccountLinkSettings::get()['email_on_login'] ?? 'auto';
		return [
			'user_meta_key'        => self::USER_META,
			'auto_register'        => (bool) ( $s['auto_register'] ?? false ),
			'bypass_registration'  => (bool) ( $s['bypass_registration_setting'] ?? false ),
			'default_role'         => (string) ( $s['default_role'] ?? 'subscriber' ),
			'store_avatar'         => (bool) ( $s['store_avatar_url'] ?? false ),
			'avatar_meta_key'      => 'wpps_google_avatar',
			'provider'             => 'google',
			'email_on_login'       => 'auto' === $el ? 'auto' : 'manual',
		];
	}

	/**
	 * @param array<string, mixed> $a shortcode / UI options
	 */
	public function renderButtonMarkup( array $a ): string {
		$url = $this->getAuthorizationUrl( (string) ( $a['redirect'] ?? '' ), 0 );
		if ( $url === '' ) {
			return '';
		}
		$s     = $this->getSettings();
		$a     = ButtonStyleHelper::applyFromModuleSettings( $a, $s );
		$label = ! empty( $a['label'] ) ? (string) $a['label'] : __( 'Sign in with Google', 'wp-powersuite' );
		$icon  = self::iconSvg();
		$a['provider'] = 'google';
		return ButtonRenderer::render( $label, $url, $icon, $a );
	}

	public static function iconSvg(): string {
		return '<svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg" focusable="false" aria-hidden="true"><path fill="#4285F4" d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z"/><path fill="#34A853" d="M9 18c2.43 0 4.467-.806 6.168-2.18l-2.908-2.258c-.806.54-1.837.86-3.06.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332C2.438 15.983 5.482 18 9 18z"/><path fill="#FBBC05" d="M3.964 10.71c-.18-.54-.282-1.117-.282-1.71 0-.593.102-1.17.282-1.71V4.958H.957C.347 6.173 0 7.548 0 9s.348 2.827.957 4.042l3.007-2.332z"/><path fill="#EA4335" d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0 5.482 0 2.438 2.017.957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z"/></svg>';
	}
}
