<?php

namespace SugarCalendarBookings\SetupWizard;

use SugarCalendarBookings\Support\Bootable;
use SugarCalendarBookings\Support\Helpers\WP;
use SugarCalendarBookings\Plugin;

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

/**
 * Class SetupWizard.
 *
 * Main orchestrator for the Setup Wizard functionality.
 *
 * @since 1.2.0
 */
class SetupWizard implements Bootable {

	/**
	 * Setup Wizard URL.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	const URL = 'https://bookings.sugarcalendarapi.com/setupwizard/v1';

	/**
	 * Setup Wizard first run transient.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	const TRANSIENT_FIRST_RUN = 'sugar_calendar_bookings_setup_wizard_first_run';

	/**
	 * Setup Wizard redirect parameter.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	const REDIRECT_PARAMETER = 'sugar_calendar_bookings_setup_wizard';

	/**
	 * Register hooks.
	 *
	 * @since 1.2.0
	 */
	public function hooks() {

		add_action( 'admin_init', [ $this, 'maybe_redirect_first_run' ], PHP_INT_MAX );
		add_action( 'rest_api_init', [ $this, 'initialize_api' ] );
		add_filter( 'sugar_calendar_bookings_is_local_environment', [ $this, 'filter_is_local_environment' ] );

		if ( is_admin() ) {
			add_filter( 'allowed_redirect_hosts', [ $this, 'get_redirect_hosts' ] );
		}
	}

	/**
	 * Filter is_local_environment to allow bypassing via constant.
	 *
	 * When SCB_SETUP_WIZARD_URL is defined, we want to allow the Setup Wizard
	 * to run even on local environments for testing purposes.
	 *
	 * @since 1.2.0
	 *
	 * @param bool $is_local Whether the environment is local.
	 *
	 * @return bool
	 */
	public function filter_is_local_environment( $is_local ) {

		// Allow bypassing local environment check via constant.
		if ( defined( 'SCB_SETUP_WIZARD_URL' ) && SCB_SETUP_WIZARD_URL ) {
			return false;
		}

		return $is_local;
	}

	/**
	 * Maybe redirect to Setup Wizard on first run or via redirect parameter.
	 *
	 * @since 1.2.0
	 */
	public function maybe_redirect_first_run() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		// Check for redirect parameter first.
		if ( ! $this->has_redirect_parameter() && ! $this->is_first_run() ) {
			return;
		}

		// Check user capability.
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		// Skip for AJAX requests.
		if ( wp_doing_ajax() ) {
			return;
		}

		// Skip for REST API requests.
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return;
		}

		// Skip for local environments (unless redirect parameter is set).
		if ( ! $this->has_redirect_parameter() && WP::is_local_environment() ) {
			return;
		}

		// Hand off to the wizard via a POST bridge so the token rides in the
		// form body rather than the URL (no leak to history / Referer / logs).
		$this->render_bridge();
		exit();
	}

	/**
	 * Render the wizard launch bridge.
	 *
	 * Outputs a tiny HTML page with an auto-submitting POST form whose
	 * body carries the wizard bootstrap data to the wizard SPA. Replaces
	 * the prior wp_safe_redirect, which put the token in the Location URL.
	 *
	 * @since 1.4.1
	 *
	 * @return void
	 */
	public function render_bridge() {

		$action_url = defined( 'SCB_SETUP_WIZARD_URL' ) ? SCB_SETUP_WIZARD_URL : self::URL;

		$fields = [
			'token'       => Plugin::get( Auth::class )->get_token(),
			'rest_url'    => Plugin::get( RestApi::class )->get_url(),
			'exit_url'    => $this->get_exit_url(),
			'restart_url' => $this->get_restart_url(),
		];

		$inputs = '';

		foreach ( $fields as $name => $value ) {
			$inputs .= sprintf(
				'<input type="hidden" name="%s" value="%s" />',
				esc_attr( $name ),
				esc_attr( $value )
			);
		}

		$action  = esc_url( $action_url );
		$charset = esc_attr( get_bloginfo( 'charset' ) );
		$title   = esc_html__( 'Sugar Calendar Bookings', 'sugar-calendar-bookings-scheduling-appointments-lite' );
		$loading = esc_html__( 'Launching the Setup Wizard…', 'sugar-calendar-bookings-scheduling-appointments-lite' );
		$cta     = esc_html__( 'Continue to Setup Wizard', 'sugar-calendar-bookings-scheduling-appointments-lite' );

		$style = 'html, body { margin: 0; }'
			. 'body {'
			. ' min-height: calc(100vh - 4px);'
			. ' background: #F0F0F1;'
			. ' border-top: 4px solid #FF8845;'
			. ' display: flex;'
			. ' align-items: center;'
			. ' justify-content: center;'
			. ' font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;'
			. ' color: #2C3338;'
			. '}'
			. '.scb-bridge { text-align: center; }'
			. '.scb-bridge .spinner {'
			. ' width: 48px; height: 48px; margin: 0 auto 16px;'
			. ' border: 4px solid #DCDCDE; border-top-color: #FF8845;'
			. ' border-radius: 50%; animation: scb-spin 1s linear infinite;'
			. '}'
			. '.scb-bridge p { margin: 0; color: #50575E; font-size: 14px; }'
			. '@keyframes scb-spin { to { transform: rotate( 360deg ); } }';

		nocache_headers();
		header( 'Referrer-Policy: no-referrer' );
		header( 'X-Frame-Options: DENY' );
		header( 'X-Content-Type-Options: nosniff' );
		header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );

		// All interpolated values are pre-escaped above ($action via esc_url; $charset, $inputs values via esc_attr; $title/$loading/$cta via esc_html__).
		// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
		echo '<!DOCTYPE html><html><head>'
			. '<meta charset="' . $charset . '" />'
			. '<meta name="robots" content="noindex,nofollow" />'
			. '<title>' . $title . '</title>'
			. '<style>' . $style . '</style>'
			. '</head><body>'
			. '<div class="scb-bridge">'
			. '<div class="spinner" aria-hidden="true"></div>'
			. '<p>' . $loading . '</p>'
			. '</div>'
			. '<form id="scb-setup-wizard-bridge" method="POST" action="' . $action . '">'
			. $inputs
			. '<noscript><button type="submit">' . $cta . '</button></noscript>'
			. '</form>'
			. '<script>document.getElementById(\'scb-setup-wizard-bridge\').submit();</script>'
			. '</body></html>';
		// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Check if the redirect parameter is set.
	 *
	 * @since 1.2.0
	 *
	 * @return bool
	 */
	private function has_redirect_parameter() {

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		return ! empty( $_GET[ self::REDIRECT_PARAMETER ] ) && sanitize_text_field( wp_unslash( $_GET[ self::REDIRECT_PARAMETER ] ) );
	}

	/**
	 * Initialize the rest API.
	 *
	 * @since 1.2.0
	 */
	public function initialize_api() {

		Plugin::get( RestApi::class )->register_routes();
	}

	/**
	 * Get the setup wizard URL for a given sub-path.
	 *
	 * Token transport is handled by render_bridge() (POST body). This helper
	 * builds plain wizard URLs — callers that need a token-bearing entrypoint
	 * should route users through maybe_redirect_first_run() so the bridge runs.
	 *
	 * @since 1.2.0
	 * @since 1.4.1 Dropped the empty-path branch that embedded the token in the URL.
	 *
	 * @param string $path Optional URL path.
	 *
	 * @return string
	 */
	public function get_url( $path = '' ) {

		$base_url = defined( 'SCB_SETUP_WIZARD_URL' ) ? SCB_SETUP_WIZARD_URL : self::URL;

		// Ensure base URL has trailing slash for proper path concatenation.
		$base_url = trailingslashit( $base_url );

		return $base_url . ltrim( $path, '/' );
	}

	/**
	 * Filter safe redirect hosts.
	 *
	 * @since 1.2.0
	 *
	 * @param array $hosts List of hosts.
	 *
	 * @return array
	 */
	public function get_redirect_hosts( $hosts ) {

		$base_url = defined( 'SCB_SETUP_WIZARD_URL' ) ? SCB_SETUP_WIZARD_URL : self::URL;
		$host     = wp_parse_url( $base_url, PHP_URL_HOST );

		if ( ! empty( $host ) ) {
			$hosts[] = $host;
		}

		return $hosts;
	}

	/**
	 * Whether it is the first time Setup Wizard is run.
	 *
	 * @since 1.2.0
	 *
	 * @return bool
	 */
	public function is_first_run() {

		return (bool) get_transient( self::TRANSIENT_FIRST_RUN );
	}

	/**
	 * The URL the user should land on when closing the Setup Wizard.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	public function get_exit_url() {

		return admin_url( 'admin.php?page=sugar-calendar-bookings-calendar' );
	}

	/**
	 * The URL the user should land on when restarting the Setup Wizard.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	public function get_restart_url() {

		return add_query_arg(
			self::REDIRECT_PARAMETER,
			1,
			$this->get_exit_url()
		);
	}
}
