<?php

// Exit if accessed directly
if (!defined('ABSPATH')) exit;

require_once __DIR__ . '/../vendor/autoload.php'; // Carrega SDK
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class ESLP_WindowsLiveAuthCallback {
    
    private static $eslp_extra = null;

	/**
	 * Class constructor.
	 *
	 * Initializes the class by requiring the necessary file and 
	 * instantiating the ESLP_Easy_Social_Login_Extra class.
	 */
    public function __construct() {    
        require_once plugin_dir_path(dirname(__FILE__)) . 'includes/eslp-easy-social-login-extra.php';
        self::$eslp_extra = new ESLP_Easy_Social_Login_Extra();
    }

	/**
	 * Handles the Microsoft OAuth callback after user authentication.
	 *
	 * This function processes the OAuth response from Microsoft, verifies security parameters,
	 * exchanges the authorization code for an access token, retrieves user information,
	 * and logs the user into WordPress.
	 */
    public function handle_windows_live_oauth_callback() {
        if (isset($_GET['code']) && isset($_GET['state'])) {
			// Sanitize and decode the state parameter.
			$state_raw = wp_unslash( $_GET['state'] ); // Remove slashes.
			$state_raw = sanitize_text_field( $state_raw ); // Sanitize input.
			
			$state = json_decode( $state_raw, true ); // Decode JSON to array.
		
			// Verifica se o nonce (state) recebido é válido
			if (!wp_verify_nonce($state['nonce'], 'eslp_windowslive_login')) {
				echo esc_html( self::$eslp_easy_social_login_extra->eslp_esc_message(
					__( 'Security check failed. Invalid nonce.', 'easy-social-login' ), 'die'
					)
				);
			}

			// Retrieve and sanitize the authorization code.
			$auth_code = wp_unslash( $_GET['code'] ); // Remove slashes.
			$auth_code = sanitize_textarea_field( $auth_code ); // Use a more appropriate sanitizer.
			
            // Retrieve Microsoft OAuth configuration settings.
			$client_id = get_option('eslp_windows_live_client_id');
			$client_secret = get_option('eslp_windows_live_client_secret');
            $redirect_uri = get_option('eslp_windowslive_oauth_redirect_uri');

            try {
				// Exchange the authorization code for an access token.
                $token = self::exchange_code_for_token($auth_code, $client_id, $client_secret, $redirect_uri);
				
				// Retrieve user information from Microsoft
				$user_data = self::get_user_info($token);
				
				// Authenticate the user in WordPress.
                self::authenticate_user($user_data, $state['redirect_url']);
            } catch (Exception $e) {
				echo esc_html( $eslp_easy_social_login_extra->eslp_esc_message(
					__( 'Error during the login process: ', 'easy-social-login' ) . esc_html( $e->getMessage() ),
					'die'
					)
				);
            }
        } else {
			// Redirect safely if the required parameters are missing.
            wp_safe_redirect( sanitize_url( home_url() ) );
            exit;
        }
    }

	/**
	 * Exchanges the authorization code for an access token using Microsoft's OAuth2 endpoint.
	 *
	 * This function makes a POST request to the Microsoft OAuth2 token endpoint,
	 * sending the necessary credentials and authorization code to retrieve an access token.
	 *
	 * @param string $auth_code     The authorization code received from Microsoft after user login.
	 * @param string $client_id     The registered Microsoft application client ID.
	 * @param string $client_secret The registered Microsoft application client secret.
	 * @param string $redirect_uri  The redirect URI used in the OAuth flow, sanitized before use.
	 *
	 * @return string The access token if the exchange is successful.
	 *
	 * @throws Exception If the access token is not returned or if a request error occurs.
	 */
	private function exchange_code_for_token( $auth_code, $client_id, $client_secret, $redirect_uri ) {
		$client = new Client();

		try {
			$response = $client->post( 'https://login.microsoftonline.com/common/oauth2/v2.0/token', [
				'form_params' => [
					'client_id'     => sanitize_text_field( $client_id ),
					'client_secret' => sanitize_text_field( $client_secret ),
					'scope'         => 'https://graph.microsoft.com/User.Read',
					'grant_type'    => 'authorization_code',
					'code'          => sanitize_text_field( $auth_code ),
					'redirect_uri'  => esc_url_raw( $redirect_uri ),
				],
			]);

			$data = json_decode( $response->getBody(), true );

			if ( isset( $data['access_token'] ) ) {
				return sanitize_text_field( $data['access_token'] );
			} else {
				throw new Exception(
					esc_html(
						self::$eslp_easy_social_login_extra->eslp_esc_message(
							__( 'Access token missing from response.', 'easy-social-login' )
						)
					)
				);
			}

		} catch ( RequestException $e ) {
			$error_message = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage();

			// Log error details for developers (do not display to users)
			error_log( '[Easy Social Login] Microsoft OAuth token exchange error: ' . sanitize_textarea_field( $error_message ) );

			throw new Exception(
				esc_html(
					self::$eslp_easy_social_login_extra->eslp_esc_message(
						__( 'Error obtaining access token.', 'easy-social-login' )
					)
				)
			);
		}
	}

	/**
	 * Retrieves user profile information from Microsoft Graph API using the access token.
	 *
	 * This function fetches user details from the authenticated Microsoft account.
	 * It expects either 'mail' or 'userPrincipalName' to be present in the response.
	 *
	 * @param string $access_token The access token obtained from Microsoft's OAuth2 flow.
	 *
	 * @return array Associative array containing the user information.
	 *
	 * @throws Exception If the request fails or user data is missing in the response.
	 */
	private function get_user_info( $access_token ) {
		$client = new Client();

		try {
			$response = $client->get( 'https://graph.microsoft.com/v1.0/me', [
				'headers' => [
					'Authorization' => 'Bearer ' . sanitize_text_field( $access_token ),
					'Accept'        => 'application/json',
				],
			] );

			// Check for a successful response.
			if ( $response->getStatusCode() !== 200 ) {
				throw new Exception(
					esc_html(
						self::$eslp_easy_social_login_extra->eslp_esc_message(
							__( 'Unexpected response from Microsoft.', 'easy-social-login' )
						)
					)
				);
			}

			$data = json_decode( $response->getBody(), true );

			// Ensure email or userPrincipalName is present.
			if ( isset( $data['mail'] ) || isset( $data['userPrincipalName'] ) ) {
				return $data;
			} else {
				throw new Exception(
					esc_html(
						self::$eslp_easy_social_login_extra->eslp_esc_message(
							__( 'User details not found.', 'easy-social-login' )
						)
					)
				);
			}

		} catch ( RequestException $e ) {
			$error_message = $e->getResponse() ? $e->getResponse()->getBody()->getContents() : $e->getMessage();

			// Log error details for developers (not shown to users).
			error_log( '[Easy Social Login] Microsoft Graph user info error: ' . sanitize_textarea_field( $error_message ) );

			throw new Exception(
				esc_html(
					self::$eslp_easy_social_login_extra->eslp_esc_message(
						__( 'Error retrieving user information.', 'easy-social-login' )
					)
				)
			);
		}
	}

	/**
	 * Authenticates or registers a user based on Microsoft OAuth data and logs them into WordPress.
	 *
	 * This method checks if a user with the provided email exists. If not, it creates a new WordPress user,
	 * stores Microsoft user metadata, and optionally notifies the admin. Then it logs the user in and redirects.
	 *
	 * @param array  $user_data    The user information received from Microsoft Graph API.
	 * @param string $redirect_url The URL to redirect to after successful login.
	 *
	 * @return void
	 */
	private function authenticate_user( $user_data, $redirect_url ) {
		// Retrieve email from response (fallback to userPrincipalName if mail is unavailable).
		$email        = sanitize_email( $user_data['mail'] ?? $user_data['userPrincipalName'] );
		$microsoft_id = sanitize_text_field( $user_data['id'] );
		$user         = get_user_by( 'email', $email );

		if ( ! $user ) {
			// Create new user.
			$user_id = wp_create_user( $email, wp_generate_password(), $email );

			$role = sanitize_text_field( get_option( 'eslp_easy_social_login_user_role', 'subscriber' ) );

			wp_update_user( [
				'ID'           => $user_id,
				'display_name' => sanitize_text_field( $user_data['displayName'] ?? '' ),
				'nickname'     => sanitize_text_field( $user_data['displayName'] ?? '' ),
				'first_name'   => sanitize_text_field( $user_data['givenName'] ?? '' ),
				'last_name'    => sanitize_text_field( $user_data['surname'] ?? '' ),
				'role'         => $role,
			] );

			update_user_meta( $user_id, 'eslp_microsoft_id', $microsoft_id );

			$user = get_user_by( 'id', $user_id );

			// Optionally notify admin.
			if ( 'not-exists' !== get_option( 'eslp_easy_social_login_notify_admin', 'not-exists' ) ) {
				if ( true === get_option( 'eslp_easy_social_login_notify_admin' ) ) {
					wp_new_user_notification( $user->ID, null, 'admin' );
				}
			}
		}

		// Add host to allowed redirect hosts.
		add_filter( 'allowed_redirect_hosts', function ( $hosts ) use ( $redirect_url ) {
			$parsed_url = wp_parse_url( $redirect_url );
			if ( isset( $parsed_url['host'] ) ) {
				$hosts[] = $parsed_url['host'];
			}
			return $hosts;
		} );

		// Authenticate and redirect user.
		wp_set_auth_cookie( $user->ID, true );
		wp_safe_redirect( esc_url_raw( $redirect_url ) );
		exit;
	}
}

$windows_live_auth_callback = new ESLP_WindowsLiveAuthCallback();
$windows_live_auth_callback->handle_windows_live_oauth_callback();
