<?php
/**
 * The guided Google connection flow.
 *
 * Renders the connection UI on the DrezSync settings tab and handles the three
 * actions: saving the credentials, starting the OAuth handshake, and the
 * callback where Google returns an authorization code we exchange for a token.
 * All handlers verify a nonce and the user's capability.
 *
 * @package DrezSync
 */

defined( 'ABSPATH' ) || exit;

/**
 * Connection screen and OAuth handlers.
 */
class DrezSync_Connect {

	/**
	 * Google connection.
	 *
	 * @var DrezSync_Connection
	 */
	private $connection;

	/**
	 * Constructor.
	 *
	 * @param DrezSync_Connection $connection Connection service.
	 */
	public function __construct( DrezSync_Connection $connection ) {
		$this->connection = $connection;

		add_action( 'admin_post_drezsync_save_credentials', array( $this, 'handle_save_credentials' ) );
		add_action( 'admin_post_drezsync_disconnect', array( $this, 'handle_disconnect' ) );
		add_action( 'admin_post_drezsync_reset', array( $this, 'handle_reset' ) );
		add_action( 'admin_post_drezsync_oauth', array( $this, 'handle_oauth_callback' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
		add_filter( 'gform_noconflict_styles', array( $this, 'allow_noconflict_style' ) );
	}

	/**
	 * Keep our stylesheet alive when Gravity Forms no-conflict mode is on.
	 *
	 * No-conflict mode strips every style Gravity Forms does not recognise from
	 * its own admin pages, which includes the settings tab this screen renders on.
	 * Site owners with several add-ons routinely turn it on, so this is a normal
	 * configuration and not an edge case.
	 *
	 * This became necessary in 1.6.8. Before the styles were moved to the style
	 * API they were echoed as a raw block, which no-conflict mode cannot touch
	 * because it only ever dequeues registered handles. Complying with the
	 * WordPress.org review is what exposed us to it.
	 *
	 * @param array $styles Handles Gravity Forms will allow through.
	 * @return array
	 */
	public function allow_noconflict_style( $styles ) {
		$styles[] = 'drezsync-admin';
		return $styles;
	}

	/**
	 * The redirect URI Google returns to after consent.
	 *
	 * @return string
	 */
	public function redirect_uri() {
		return admin_url( 'admin-post.php?action=drezsync_oauth' );
	}

	/**
	 * URL of the DrezSync settings tab, with optional extra query args.
	 *
	 * @param array $args Extra query args.
	 * @return string
	 */
	private function settings_url( $args = array() ) {
		return add_query_arg(
			array_merge(
				array(
					'page'    => 'gf_settings',
					'subview' => 'drezsync',
				),
				$args
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * The Google authorization URL, tagged with a CSRF state nonce.
	 *
	 * @return string Empty string if the library or credentials are missing.
	 */
	private function auth_url() {
		$client = $this->connection->get_oauth_client( $this->redirect_uri() );
		if ( ! $client ) {
			return '';
		}
		$client->setState( wp_create_nonce( 'drezsync_oauth' ) );
		return $client->createAuthUrl();
	}

	/**
	 * Render the connection UI on the settings tab.
	 */
	public function render() {
		$this->maybe_notice();

		$connected = $this->connection->is_connected();

		echo '<div class="drezsync-settings">';

		echo '<div class="drezsync-settings__header">';
		echo '<span class="drezsync-settings__mark"><span class="dashicons dashicons-calendar-alt"></span></span>';
		echo '<div><p class="drezsync-settings__title">DrezSync</p>';
		echo '<p class="drezsync-settings__sub">' . esc_html__( 'Calendar booking for Gravity Forms', 'drezsync' ) . '</p></div>';
		if ( $connected ) {
			echo '<span class="drezsync-settings__status drezsync-settings__status--on"><span class="dashicons dashicons-yes-alt" style="width:16px;height:16px;font-size:16px;"></span>' . esc_html__( 'Connected', 'drezsync' ) . '</span>';
		} else {
			echo '<span class="drezsync-settings__status drezsync-settings__status--off">' . esc_html__( 'Not connected', 'drezsync' ) . '</span>';
		}
		echo '</div>';

		echo '<div class="drezsync-card">';
		if ( $connected ) {
			$this->render_connected();
		} elseif ( $this->connection->get_credentials() ) {
			$this->render_authorize();
		} else {
			$this->render_upload();
		}
		echo '</div>';

		$this->render_pro_footer();

		echo '<p style="color:#c3c4c7;font-size:11px;margin-top:14px;">DrezSync ' . esc_html( DREZSYNC_VERSION ) . '</p>';

		echo '</div>';
	}

	/**
	 * Show Pro status, or a tasteful upgrade card on the free plan.
	 */
	private function render_pro_footer() {
		if ( DrezSync_License::is_pro() ) {
			echo '<p style="margin-top:18px;color:#1e7e45;font-size:13px;"><span class="dashicons dashicons-yes-alt" style="vertical-align:middle;"></span> ';
			echo esc_html__( 'DrezSync Pro is active.', 'drezsync' ) . '</p>';
			return;
		}

		$shared = array(
			__( 'Time-slot booking', 'drezsync' ),
			__( 'Google Calendar sync', 'drezsync' ),
			__( 'Auto-block booked times', 'drezsync' ),
			__( 'Multiple booking fields per form', 'drezsync' ),
			__( 'Custom accent color', 'drezsync' ),
		);
		$pro = array(
			__( 'Date-range booking for rentals', 'drezsync' ),
			__( 'Check-in and check-out times', 'drezsync' ),
			__( 'Slot capacity for group bookings', 'drezsync' ),
			__( 'Overlap protection across fields', 'drezsync' ),
			__( 'Calendar chosen by another field', 'drezsync' ),
			__( 'Custom event titles and customer invites', 'drezsync' ),
			__( 'Priority support', 'drezsync' ),
		);

		echo '<div style="margin-top:18px;background:#fff7ed;border:1px solid #fed7aa;border-radius:12px;padding:18px 20px;">';
		echo '<p style="margin:0 0 2px;font-weight:600;font-size:14px;">' . esc_html__( "You're on the free plan", 'drezsync' ) . '</p>';
		echo '<p style="margin:0;color:#646970;font-size:13px;">' . esc_html__( 'Here is how free compares to Pro.', 'drezsync' ) . '</p>';

		echo '<table class="drezsync-compare"><thead><tr><th>' . esc_html__( 'Feature', 'drezsync' ) . '</th><th class="col">' . esc_html__( 'Free', 'drezsync' ) . '</th><th class="col">' . esc_html__( 'Pro', 'drezsync' ) . '</th></tr></thead><tbody>';
		foreach ( $shared as $label ) {
			echo '<tr><td>' . esc_html( $label ) . '</td><td class="col yes">&#10003;</td><td class="col yes">&#10003;</td></tr>';
		}
		foreach ( $pro as $label ) {
			echo '<tr><td>' . esc_html( $label ) . '</td><td class="col no">&times;</td><td class="col pro">&#10003;</td></tr>';
		}
		echo '</tbody></table>';

		echo '<a class="button" href="' . esc_url( DREZSYNC_PRO_URL ) . '" target="_blank" rel="noopener" style="background:#ff9405;border-color:#ff9405;color:#2b1a00;">' . esc_html__( 'Go Pro', 'drezsync' ) . '</a>';
		echo '</div>';
	}

	/**
	 * Connected state: confirmation and a disconnect button.
	 */
	private function render_connected() {
		echo '<div class="drezsync-connected">';
		echo '<span class="dashicons dashicons-yes-alt" style="width:24px;height:24px;font-size:24px;color:#1e7e45;"></span>';
		echo '<div><p style="margin:0;font-weight:600;">' . esc_html__( 'Google connected.', 'drezsync' ) . '</p>';
		echo '<p style="margin:2px 0 0;color:#646970;">' . esc_html__( 'Bookings sync to your calendar automatically.', 'drezsync' ) . '</p></div>';
		echo '</div>';

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="margin-top:16px;">';
		wp_nonce_field( 'drezsync_disconnect' );
		echo '<input type="hidden" name="action" value="drezsync_disconnect" />';
		echo '<button type="submit" class="button" onclick="return confirm(\'' . esc_js( __( 'Disconnect from Google? Your credentials are kept, so you can reconnect in one click.', 'drezsync' ) ) . '\');">' . esc_html__( 'Disconnect', 'drezsync' ) . '</button>';
		echo '</form>';
	}

	/**
	 * Credentials saved, awaiting authorization.
	 */
	private function render_authorize() {
		$auth_url = $this->auth_url();

		echo '<p>' . esc_html__( 'Your credentials are saved. Now authorize DrezSync to access your calendar.', 'drezsync' ) . '</p>';

		if ( $auth_url ) {
			echo '<p><a class="button drezsync-btn" href="' . esc_url( $auth_url ) . '">';
			echo esc_html__( 'Connect with Google', 'drezsync' ) . '</a></p>';
		} else {
			echo '<p>' . esc_html__( 'The Google library could not be loaded. Please reinstall the plugin.', 'drezsync' ) . '</p>';
		}

		echo '<p style="margin-top:8px;"><a href="' . esc_url( DREZSYNC_KB_CONNECT_URL ) . '" target="_blank" rel="noopener">';
		echo esc_html__( 'Trouble connecting? Read the guide', 'drezsync' ) . ' &rarr;</a></p>';

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '" style="margin-top:12px;">';
		wp_nonce_field( 'drezsync_reset' );
		echo '<input type="hidden" name="action" value="drezsync_reset" />';
		echo '<button type="submit" class="button-link" onclick="return confirm(\'' . esc_js( __( 'This removes your saved Google credentials. You will need your credentials file to set up again. Continue?', 'drezsync' ) ) . '\');">' . esc_html__( 'Start over with different credentials', 'drezsync' ) . '</button>';
		echo '</form>';
	}

	/**
	 * No credentials yet: show the redirect URL and the paste box.
	 */
	private function render_upload() {
		echo '<p>' . esc_html__( 'Connect a Google account to start syncing bookings. Two quick steps:', 'drezsync' ) . '</p>';

		echo '<h4>' . esc_html__( 'Step 1: Add this redirect URL in Google', 'drezsync' ) . '</h4>';
		echo '<p>' . esc_html__( 'When you create your OAuth credentials in Google, paste this exact URL as an authorized redirect URI:', 'drezsync' ) . '</p>';
		echo '<input type="text" class="large-text code" readonly onclick="this.select();" value="' . esc_attr( $this->redirect_uri() ) . '" />';

		echo '<h4 style="margin-top:20px;">' . esc_html__( 'Step 2: Paste your credentials', 'drezsync' ) . '</h4>';
		echo '<p>' . esc_html__( 'Open the credentials JSON file you downloaded from Google, copy all of it, and paste it here.', 'drezsync' ) . '</p>';

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		wp_nonce_field( 'drezsync_save_credentials' );
		echo '<input type="hidden" name="action" value="drezsync_save_credentials" />';
		echo '<textarea name="drezsync_credentials" class="large-text code" rows="6" placeholder="{ &quot;web&quot;: { ... } }"></textarea>';
		echo '<p><button type="submit" class="button drezsync-btn">' . esc_html__( 'Save credentials', 'drezsync' ) . '</button></p>';
		echo '</form>';

		echo '<p style="margin-top:16px;"><a href="' . esc_url( DREZSYNC_KB_CONNECT_URL ) . '" target="_blank" rel="noopener">';
		echo esc_html__( 'Full step-by-step setup guide', 'drezsync' ) . ' &rarr;</a></p>';
	}

	/**
	 * Save the pasted credentials JSON.
	 */
	public function handle_save_credentials() {
		$this->guard( 'drezsync_save_credentials' );

		// Credentials are JSON — unslash but do not run through a text sanitizer,
		// which would corrupt the structure. save_credentials() validates it.
		// Nonce is verified in guard() above; the checker cannot trace across methods.
		$json = isset( $_POST['drezsync_credentials'] ) ? wp_unslash( $_POST['drezsync_credentials'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput, WordPress.Security.NonceVerification.Missing
		$saved = $this->connection->save_credentials( $json );

		$this->redirect_back( $saved ? array() : array( 'drezsync_error' => 'credentials' ) );
	}

	/**
	 * Disconnect and clear stored credentials and token.
	 */
	public function handle_disconnect() {
		$this->guard( 'drezsync_disconnect' );
		$this->connection->revoke();
		$this->redirect_back( array( 'drezsync_disconnected' => 1 ) );
	}

	/**
	 * Remove the stored credentials and token entirely (start over).
	 */
	public function handle_reset() {
		$this->guard( 'drezsync_reset' );
		$this->connection->disconnect();
		$this->redirect_back( array( 'drezsync_disconnected' => 1 ) );
	}

	/**
	 * Handle Google's redirect back with an authorization code.
	 */
	public function handle_oauth_callback() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'drezsync' ) );
		}

		$state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( $_GET['state'] ) ) : '';
		if ( ! wp_verify_nonce( $state, 'drezsync_oauth' ) ) {
			$this->redirect_back( array( 'drezsync_error' => 'state' ) );
		}

		if ( empty( $_GET['code'] ) ) {
			$this->redirect_back( array( 'drezsync_error' => 'oauth' ) );
		}

		$code   = sanitize_text_field( wp_unslash( $_GET['code'] ) );
		$client = $this->connection->get_oauth_client( $this->redirect_uri() );
		if ( ! $client ) {
			$this->redirect_back( array( 'drezsync_error' => 'lib' ) );
		}

		$token = $client->fetchAccessTokenWithAuthCode( $code );
		if ( ! is_array( $token ) || ! empty( $token['error'] ) ) {
			$this->redirect_back( array( 'drezsync_error' => 'oauth' ) );
		}

		$this->connection->save_token( $token );
		$this->redirect_back( array( 'drezsync_connected' => 1 ) );
	}

	/**
	 * Capability + nonce check shared by the POST handlers.
	 *
	 * @param string $action Nonce action.
	 */
	private function guard( $action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You do not have permission to do that.', 'drezsync' ) );
		}
		check_admin_referer( $action );
	}

	/**
	 * Redirect back to the settings tab and stop.
	 *
	 * @param array $args Query args (e.g. a status flag).
	 */
	private function redirect_back( $args = array() ) {
		wp_safe_redirect( $this->settings_url( $args ) );
		exit;
	}

	/**
	 * Show an inline notice based on the status flag in the URL.
	 */
	private function maybe_notice() {
		if ( isset( $_GET['drezsync_connected'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$this->notice( 'success', __( "You're connected. Bookings will now sync automatically.", 'drezsync' ) );
		} elseif ( isset( $_GET['drezsync_disconnected'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$this->notice( 'info', __( 'Disconnected from Google.', 'drezsync' ) );
		} elseif ( isset( $_GET['drezsync_error'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$code = sanitize_key( wp_unslash( $_GET['drezsync_error'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$this->notice( 'error', $this->error_message( $code ) );
		}
	}

	/**
	 * Print an inline admin notice.
	 *
	 * @param string $type    success|info|error.
	 * @param string $message Text.
	 */
	private function notice( $type, $message ) {
		printf( '<div class="notice notice-%1$s inline"><p>%2$s</p></div>', esc_attr( $type ), esc_html( $message ) );
	}

	/**
	 * Friendly message for an error code.
	 *
	 * @param string $code Error code.
	 * @return string
	 */
	private function error_message( $code ) {
		switch ( $code ) {
			case 'credentials':
				return __( "That didn't look like a valid Google credentials file. Check that you copied the whole JSON.", 'drezsync' );
			case 'state':
				return __( 'The connection request expired. Please try connecting again.', 'drezsync' );
			case 'lib':
				return __( 'The Google library could not be loaded. Please reinstall the plugin.', 'drezsync' );
			case 'oauth':
				return __( 'Google declined the connection. Please try again.', 'drezsync' );
			default:
				return __( 'Something went wrong. Please try again.', 'drezsync' );
		}
	}

	/**
	 * Load the settings-screen styles.
	 *
	 * WordPress.org asks that CSS reach the page through the style API rather
	 * than a raw style tag. The handle carries no file of its own; registering
	 * it with no source is the documented way to hold an inline stylesheet, and
	 * because we own the handle the CSS can never fail to attach.
	 *
	 * This runs on admin_enqueue_scripts rather than from render() so the styles
	 * land in the head. Enqueuing during render() would print them in the footer
	 * and the screen would flash unstyled first.
	 */
	public function enqueue_styles() {
		if ( ! $this->is_settings_screen() ) {
			return;
		}
		wp_register_style( 'drezsync-admin', false, array(), DREZSYNC_VERSION );
		wp_enqueue_style( 'drezsync-admin' );
		wp_add_inline_style( 'drezsync-admin', $this->settings_css() );
	}

	/**
	 * Is this one of the two screens that render the connection UI?
	 *
	 * render() is reached from the top-level DrezSync page and from the Gravity
	 * Forms settings tab, so both are matched here. This only reads the admin
	 * page routing to decide whether to load a stylesheet, so there is nothing
	 * to verify a nonce against; a miss costs an unstyled screen, never a broken
	 * one.
	 *
	 * @return bool
	 */
	private function is_settings_screen() {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
		$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';

		if ( 'drezsync' === $page ) {
			return true;
		}

		if ( 'gf_settings' === $page ) {
			$subview = isset( $_GET['subview'] ) ? sanitize_key( wp_unslash( $_GET['subview'] ) ) : '';
			return 'drezsync' === $subview;
		}
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

		return false;
	}

	/**
	 * The settings-screen stylesheet.
	 *
	 * Kept as a nowdoc so the CSS stays readable and PHP leaves it alone.
	 *
	 * @return string
	 */
	private function settings_css() {
		return <<<'CSS'
.drezsync-settings{max-width:680px;}
.drezsync-settings__header{display:flex;align-items:center;gap:12px;border-top:3px solid #ff4e43;padding-top:18px;margin:6px 0 22px;}
.drezsync-settings__mark{width:40px;height:40px;border-radius:10px;background:#ff4e43;display:flex;align-items:center;justify-content:center;flex:none;}
.drezsync-settings__mark .dashicons{color:#fff;width:24px;height:24px;font-size:24px;}
.drezsync-settings__title{font-size:18px;font-weight:600;line-height:1.2;margin:0;color:#1d2327;}
.drezsync-settings__sub{font-size:13px;color:#646970;margin:2px 0 0;}
.drezsync-settings__status{margin-left:auto;font-size:13px;padding:5px 12px;border-radius:20px;display:inline-flex;align-items:center;gap:6px;}
.drezsync-settings__status--on{background:#e6f4ea;color:#1e7e45;}
.drezsync-settings__status--off{background:#f0f0f1;color:#646970;}
.drezsync-card{background:#fff;border:1px solid #dcdcde;border-radius:12px;padding:20px 22px;}
.drezsync-card h4{margin:0 0 6px;font-size:14px;}
.drezsync-settings a.drezsync-btn,.drezsync-settings button.drezsync-btn{background:#ff4e43;border-color:#ff4e43;color:#fff;text-shadow:none;box-shadow:none;}
.drezsync-settings a.drezsync-btn:hover,.drezsync-settings button.drezsync-btn:hover,.drezsync-settings a.drezsync-btn:focus,.drezsync-settings button.drezsync-btn:focus{background:#e23c31;border-color:#e23c31;color:#fff;}
.drezsync-card input[readonly]{background:#f6f7f7;}
.drezsync-card textarea{font-family:Menlo,Consolas,monospace;}
.drezsync-connected{display:flex;align-items:center;gap:12px;}
.drezsync-compare{width:100%;border-collapse:collapse;margin:14px 0 16px;font-size:13px;}
.drezsync-compare th,.drezsync-compare td{padding:8px 10px;border-bottom:1px solid #f0f0f1;text-align:left;}
.drezsync-compare thead th{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:#646970;font-weight:600;}
.drezsync-compare .col{text-align:center;width:64px;}
.drezsync-compare .yes{color:#1e7e45;font-weight:600;}
.drezsync-compare .pro{color:#c2650a;font-weight:600;}
.drezsync-compare .no{color:#c3c4c7;}
CSS;
	}

}
