<?php
/**
 * GitHub OAuth2 provider
 *
 * @package GitHubSignIn
 */

namespace GitHubSignIn;

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_github_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 'github';
	}

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

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

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

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

	/**
	 * @return array<string, mixed>
	 */
	public function getSettings(): array {
		if ( $this->settings_cache !== null ) {
			return $this->settings_cache;
		}
		$raw = $this->settings->all( [] );
		$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'            => ! empty( $raw['store_avatar_url'] ),
			'woo_login'                   => ! empty( $raw['woo_login'] ),
			'allow_signup'                => ! isset( $raw['allow_signup'] ) || ! empty( $raw['allow_signup'] ),
		];
		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 );
		$args = [
			'client_id'     => $cid,
			'redirect_uri'  => $this->getRedirectUri(),
			'scope'         => Config::SCOPE,
			'state'         => $st['state'],
			'allow_signup'  => ( ! empty( $s['allow_signup'] ) ) ? 'true' : 'false',
		];
		/**
		 * Optional query args for `https://github.com/login/oauth/authorize` (e.g. remove `prompt` for legacy behaviour).
		 *
		 * @param array<string, string> $args          Keys are query parameter names; values are strings.
		 * @param string                 $redirect_after
		 * @param int                    $link_user_id 0 = sign-in, non-zero = Connect to existing user.
		 */
		$args = apply_filters( 'wpps_github_oauth_authorize_args', $args, $redirect_after, $link_user_id );
		return add_query_arg( $args, 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,
			[
				'client_id'     => $cid,
				'client_secret' => $sec,
				'code'          => $code,
				'redirect_uri'  => $redirect_uri_used,
			],
			15,
			[ 'Accept' => 'application/json' ]
		);
		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 GitHub.', 'wp-powersuite' ) );
		}
		$user = $this->tasks->getJson(
			Config::USERINFO,
			[
				'Authorization' => 'Bearer ' . $at,
				'Accept'        => 'application/vnd.github.v3+json',
			]
		);
		if ( is_wp_error( $user ) ) {
			return $user;
		}
		if ( empty( $user['id'] ) || empty( $user['login'] ) ) {
			return new \WP_Error( 'profile', __( 'Invalid GitHub profile response.', 'wp-powersuite' ) );
		}
		$gid = (string) (int) $user['id'];
		$mail           = '';
		$email_verified = false;
		$eml            = $this->tasks->getJson(
			Config::EMAILS,
			[
				'Authorization' => 'Bearer ' . $at,
				'Accept'        => 'application/vnd.github.v3+json',
			]
		);
		if ( is_array( $eml ) && ! is_wp_error( $eml ) ) {
			foreach ( $eml as $e ) {
				if ( ! is_array( $e ) || empty( $e['verified'] ) || empty( $e['email'] ) ) {
					continue;
				}
				if ( ! empty( $e['primary'] ) ) {
					$mail           = sanitize_email( (string) $e['email'] );
					$email_verified = true;
					break;
				}
			}
			if ( $mail === '' ) {
				foreach ( $eml as $e ) {
					if ( is_array( $e ) && ! empty( $e['verified'] ) && ! empty( $e['email'] ) ) {
						$mail           = sanitize_email( (string) $e['email'] );
						$email_verified = true;
						break;
					}
				}
			}
		}
		if ( $mail === '' ) {
			$mail = $gid . '+' . (string) $user['login'] . '@users.noreply.github.com';
		}
		return [
			'id'             => (string) $gid,
			'email'          => $mail,
			'email_verified' => $email_verified,
			'user_login'     => (string) ( $user['login'] ?? 'user' ),
			'display_name'   => (string) ( $user['name'] ?? (string) ( $user['login'] ?? '' ) ),
			'avatar_url'     => (string) ( $user['avatar_url'] ?? '' ),
		];
	}

	/**
	 * @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_github_avatar',
			'provider'              => 'github',
			'email_on_login'        => 'auto' === $el ? 'auto' : 'manual',
		];
	}

	/**
	 * Noreply: skip email-based linking (handled via skip flag when email is noreply).
	 *
	 * @param array<string, mixed> $a
	 */
	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 GitHub', 'wp-powersuite' );
		$icon  = self::iconSvg();
		$a['provider'] = 'github';
		return ButtonRenderer::render( $label, $url, $icon, $a );
	}

	public static function iconSvg(): string {
		return '<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>';
	}
}
