<?php
/**
 * OAuth 2.1 authorization endpoint + consent screen. Phase 4 of AI-9.
 *
 * Browser-facing: GET /awfah-oauth/authorize (pretty rewrite). Requires the
 * visitor to be logged into WordPress, shows a consent screen, and on approval
 * issues a single-use PKCE-bound authorization code and redirects back to the
 * client's redirect_uri.
 *
 * @package AtlasAiDev\Awfah
 * @subpackage OAuth
 */

declare(strict_types=1);

namespace AtlasAiDev\Awfah\OAuth;

defined( 'ABSPATH' ) || exit;

/**
 * Class Authorize_Controller
 */
class Authorize_Controller {

	const QUERY_VAR    = 'awfah_oauth_authorize';
	const CODE_TTL     = 60; // seconds.
	const NONCE_ACTION = 'awfah_oauth_consent';

	/**
	 * Wire hooks. (Rewrite rule + query var are registered centrally in OAuth_Server.)
	 */
	public function __construct() {
		add_action( 'template_redirect', array( $this, 'maybe_handle' ) );
	}

	/**
	 * Dispatch when the authorize endpoint is hit.
	 *
	 * @return void
	 */
	public function maybe_handle(): void {
		if ( '' === (string) get_query_var( self::QUERY_VAR ) ) {
			return;
		}
		if ( ! OAuth_Server::is_enabled() ) {
			status_header( 404 );
			exit;
		}

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- OAuth params, not a WP form; validated below.
		$response_type = isset( $_GET['response_type'] ) ? sanitize_text_field( wp_unslash( $_GET['response_type'] ) ) : '';
		$client_id     = isset( $_GET['client_id'] ) ? sanitize_text_field( wp_unslash( $_GET['client_id'] ) ) : '';
		$redirect_uri  = isset( $_GET['redirect_uri'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_uri'] ), array( 'https', 'http' ) ) : '';
		$state         = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( $_GET['state'] ) ) : '';
		$scope         = isset( $_GET['scope'] ) ? sanitize_text_field( wp_unslash( $_GET['scope'] ) ) : '';
		$challenge     = isset( $_GET['code_challenge'] ) ? sanitize_text_field( wp_unslash( $_GET['code_challenge'] ) ) : '';
		$challenge_m   = isset( $_GET['code_challenge_method'] ) ? sanitize_text_field( wp_unslash( $_GET['code_challenge_method'] ) ) : '';
		$is_post       = ( 'POST' === ( $_SERVER['REQUEST_METHOD'] ?? 'GET' ) );
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

		$client = OAuth_Repository::get_client( $client_id );

		// Validate client + redirect_uri BEFORE redirecting anywhere (open-redirect guard).
		if ( ! $client || ! in_array( $redirect_uri, (array) $client['redirect_uris'], true ) ) {
			$this->fail_page( __( 'Invalid client or redirect URI.', 'ai-workflow-automation-ai-agent-hub' ) );
		}
		if ( 'code' !== $response_type ) {
			$this->redirect_error( $redirect_uri, 'unsupported_response_type', $state );
		}
		if ( 'S256' !== $challenge_m || '' === $challenge ) {
			$this->redirect_error( $redirect_uri, 'invalid_request', $state ); // PKCE S256 required.
		}

		// Require WordPress login; bounce through wp-login and come back to the
		// full current URL (with all OAuth query args). REQUEST_URI already includes
		// the site's subdirectory path, so build scheme://host + REQUEST_URI directly
		// — do NOT wrap it in home_url(), which would prepend the subdir a second time.
		if ( ! is_user_logged_in() ) {
			$scheme = is_ssl() ? 'https://' : 'http://';
			$host   = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
			$uri    = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '/awfah-oauth/authorize'; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
			$self   = esc_url_raw( $scheme . $host . $uri );
			wp_safe_redirect( wp_login_url( $self ) );
			exit;
		}

		// Effective scope = requested ∩ client-registered ∩ supported (Pro may narrow further).
		$eff_scope = $this->effective_scope( $scope, (string) $client['scope'] );

		// Capability gate: only users who can manage content may authorize an MCP
		// connection — blocks low-privilege roles (subscribers, customers, etc.) even
		// if they obtain the authorize URL. Filterable; Pro layers per-role policy here.
		$min_cap = apply_filters( 'awfah_oauth_authorize_min_capability', 'edit_posts' );
		if ( $min_cap && ! current_user_can( $min_cap ) ) {
			$this->redirect_error( $redirect_uri, 'access_denied', $state );
		}

		/**
		 * Allow Pro / third parties to deny an authorization (pending-client approval,
		 * per-role policy, IP rules, …). Return false to deny with access_denied.
		 *
		 * @param bool   $allowed Whether to allow this authorization.
		 * @param int    $user_id Current WordPress user id.
		 * @param array  $client  The registered client row.
		 * @param string $scope   Effective scope being granted.
		 */
		if ( ! apply_filters( 'awfah_oauth_authorize_allowed', true, get_current_user_id(), $client, $eff_scope ) ) {
			$this->redirect_error( $redirect_uri, 'access_denied', $state );
		}

		// Handle consent submission.
		if ( $is_post ) {
			$nonce = isset( $_POST['_awfah_oauth_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_awfah_oauth_nonce'] ) ) : '';
			if ( ! wp_verify_nonce( $nonce, self::NONCE_ACTION ) ) {
				$this->fail_page( __( 'Security check failed. Please try connecting again.', 'ai-workflow-automation-ai-agent-hub' ) );
			}
			if ( ! isset( $_POST['authorize'] ) || 'allow' !== sanitize_text_field( wp_unslash( $_POST['authorize'] ) ) ) {
				$this->redirect_error( $redirect_uri, 'access_denied', $state );
			}

			// Issue a single-use authorization code bound to the PKCE challenge.
			$code = OAuth_Server::random_token( 'code_' );

			/**
			 * Fires just before an authorization code is issued (audit/observe).
			 *
			 * @param int    $user_id   WordPress user authorizing.
			 * @param array  $client    Client row.
			 * @param string $eff_scope Granted scope.
			 */
			do_action( 'awfah_oauth_before_issue_code', get_current_user_id(), $client, $eff_scope );

			OAuth_Repository::insert_token(
				array(
					'token'                 => $code,
					'type'                  => 'code',
					'client_id'             => $client_id,
					'user_id'               => get_current_user_id(),
					'scope'                 => $eff_scope,
					'code_challenge'        => $challenge,
					'code_challenge_method' => 'S256',
					'redirect_uri'          => $redirect_uri,
					'expires'               => gmdate( 'Y-m-d H:i:s', time() + self::CODE_TTL ),
				)
			);

			$sep = ( false === strpos( $redirect_uri, '?' ) ) ? '?' : '&';
			$to  = $redirect_uri . $sep . http_build_query( array_filter( array( 'code' => $code, 'state' => $state ) ) );
			wp_redirect( $to ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- client-registered redirect_uri, validated above.
			exit;
		}

		// Render consent screen (GET, logged in).
		$this->render_consent( $client, $eff_scope );
	}

	/**
	 * Intersection of requested scope and the client's registered scope.
	 *
	 * @param string $requested Requested scope.
	 * @param string $client_scope Client's registered scope.
	 * @return string
	 */
	private function effective_scope( string $requested, string $client_scope ): string {
		$req = array_filter( array_map( 'trim', explode( ' ', $requested ) ) );
		$cli = array_filter( array_map( 'trim', explode( ' ', $client_scope ) ) );
		if ( empty( $req ) ) {
			$req = $cli;
		}
		$kept  = array_values( array_intersect( $req, $cli, OAuth_Server::scopes() ) );
		$scope = empty( $kept ) ? implode( ' ', OAuth_Server::scopes() ) : implode( ' ', $kept );

		/**
		 * Filter the effective scope granted for this authorization. Pro may narrow
		 * it by role/policy; must return a space-delimited subset of supported scopes.
		 *
		 * @param string $scope        Computed effective scope.
		 * @param int    $user_id      Current user id.
		 * @param string $client_scope Client's registered scope.
		 */
		return (string) apply_filters( 'awfah_oauth_effective_scope', $scope, get_current_user_id(), $client_scope );
	}

	/**
	 * Redirect back to the client with an OAuth error.
	 *
	 * @param string $redirect_uri Validated redirect URI.
	 * @param string $error        OAuth error code.
	 * @param string $state        Client state.
	 * @return void
	 */
	private function redirect_error( string $redirect_uri, string $error, string $state ): void {
		$sep = ( false === strpos( $redirect_uri, '?' ) ) ? '?' : '&';
		$to  = $redirect_uri . $sep . http_build_query( array_filter( array( 'error' => $error, 'state' => $state ) ) );
		wp_redirect( $to ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- validated client redirect_uri.
		exit;
	}

	/**
	 * Render a fatal consent error page (no redirect — untrusted target).
	 *
	 * @param string $message Message.
	 * @return void
	 */
	private function fail_page( string $message ): void {
		wp_die( esc_html( $message ), esc_html__( 'Authorization Error', 'ai-workflow-automation-ai-agent-hub' ), array( 'response' => 400 ) );
	}

	/**
	 * Render the consent screen.
	 *
	 * @param array  $client Client row.
	 * @param string $scope  Effective scope.
	 * @return void
	 */
	private function render_consent( array $client, string $scope ): void {
		$user        = wp_get_current_user();
		$client_name = $client['client_name'] ? $client['client_name'] : __( 'An MCP client', 'ai-workflow-automation-ai-agent-hub' );
		$scope_lines = array(
			'mcp:read'        => __( 'Read your site content, settings, and data', 'ai-workflow-automation-ai-agent-hub' ),
			'mcp:write'       => __( 'Create and update posts, pages, media, and users', 'ai-workflow-automation-ai-agent-hub' ),
			'mcp:woocommerce' => __( 'Manage your WooCommerce store', 'ai-workflow-automation-ai-agent-hub' ),
		);
		$granted = array_filter( array_map( 'trim', explode( ' ', $scope ) ) );

		nocache_headers();
		header( 'Content-Type: text/html; charset=utf-8' );
		?>
<!doctype html>
<html <?php language_attributes(); ?>>
<head>
	<meta charset="<?php bloginfo( 'charset' ); ?>">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<meta name="robots" content="noindex,nofollow">
	<title><?php echo esc_html( __( 'Authorize MCP Connection', 'ai-workflow-automation-ai-agent-hub' ) . ' — AtlasAI Connector' ); ?></title>
	<style>
		body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f0f1;margin:0;color:#1d2327}
		.box{max-width:440px;margin:8vh auto;background:#fff;border:1px solid #dcdcde;border-radius:10px;padding:32px;box-shadow:0 1px 3px rgba(0,0,0,.06)}
		h1{font-size:20px;margin:0 0 4px}.sub{color:#646970;font-size:13px;margin:0 0 20px}
		.client{font-weight:600}.scopes{list-style:none;padding:0;margin:0 0 24px}
		.scopes li{padding:10px 0;border-top:1px solid #f0f0f1;font-size:14px;display:flex;gap:8px}
		.scopes li:before{content:"✓";color:#2271b1;font-weight:700}
		.who{font-size:12px;color:#646970;margin:0 0 20px}
		.btns{display:flex;gap:10px}button{flex:1;padding:11px;border-radius:6px;border:1px solid #2271b1;font-size:14px;cursor:pointer}
		.allow{background:#2271b1;color:#fff}.deny{background:#fff;color:#2271b1}
		.brand{font-size:12px;color:#8c8f94;text-align:center;margin-top:18px}
	</style>
</head>
<body>
	<div class="box">
		<h1><?php esc_html_e( 'Authorize connection', 'ai-workflow-automation-ai-agent-hub' ); ?></h1>
		<p class="sub"><span class="client"><?php echo esc_html( $client_name ); ?></span> <?php esc_html_e( 'wants to connect to your WordPress site via MCP and will be able to:', 'ai-workflow-automation-ai-agent-hub' ); ?></p>
		<ul class="scopes">
			<?php foreach ( $granted as $s ) : ?>
				<li><?php echo esc_html( $scope_lines[ $s ] ?? $s ); ?></li>
			<?php endforeach; ?>
		</ul>
		<p class="who">
			<?php
			/* translators: 1: user login, 2: user role note */
			printf( esc_html__( 'Signed in as %1$s. Access is limited to what your role permits.', 'ai-workflow-automation-ai-agent-hub' ), '<strong>' . esc_html( $user->user_login ) . '</strong>' );
			?>
		</p>
		<form method="post">
			<input type="hidden" name="_awfah_oauth_nonce" value="<?php echo esc_attr( wp_create_nonce( self::NONCE_ACTION ) ); ?>">
			<div class="btns">
				<button class="deny" type="submit" name="authorize" value="deny"><?php esc_html_e( 'Cancel', 'ai-workflow-automation-ai-agent-hub' ); ?></button>
				<button class="allow" type="submit" name="authorize" value="allow"><?php esc_html_e( 'Allow', 'ai-workflow-automation-ai-agent-hub' ); ?></button>
			</div>
		</form>
		<p class="brand"><?php echo esc_html( sprintf( /* translators: %s: plugin brand name (do not translate) */ __( 'Secured by %s', 'ai-workflow-automation-ai-agent-hub' ), 'AtlasAI Connector' ) ); ?></p>
	</div>
</body>
</html>
		<?php
		exit;
	}
}
