<?php
/**
 * WordPress Connectors bridge.
 *
 * Mirrors API keys between WordPress 7.0 Connectors and SmartyPress AI Engine
 * settings when the user opts into managed mode.
 *
 * @package SmartyPress_AI_Engine
 * @since 1.0.22
 */

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

/**
 * WordPress Connectors bridge class.
 */
class SmartyPress_AI_WP_Connectors_Bridge {

	/**
	 * Bridge mode option name.
	 *
	 * @var string
	 */
	const OPTION_MODE = 'smartypress_ai_connectors_mode';

	/**
	 * Plugin settings option name.
	 *
	 * @var string
	 */
	const SETTINGS_OPTION = 'smartypress_ai_settings';

	/**
	 * AJAX nonce action.
	 *
	 * @var string
	 */
	const NONCE_ACTION = 'smartypress_ai_connectors';

	/**
	 * Reentrance guard for two-way option mirroring.
	 *
	 * @var bool
	 */
	private static $bridging = false;

	/**
	 * Constructor.
	 */
	public function __construct() {
		add_action( 'wp_ajax_smartypress_ai_connectors_set_mode', array( $this, 'ajax_set_mode' ) );

		if ( 'off' !== $this->get_mode() ) {
			add_action( 'admin_footer', array( $this, 'maybe_render_banner' ) );
		}

		if ( 'managed' !== $this->get_mode() ) {
			return;
		}

		add_action( 'wp_connectors_init', array( $this, 'customize_registry' ), 20 );
		add_action( 'updated_option', array( $this, 'maybe_bridge_key_to_settings' ), 10, 3 );
		add_action( 'added_option', array( $this, 'maybe_bridge_added_key_to_settings' ), 10, 2 );
		add_action( 'update_option_' . self::SETTINGS_OPTION, array( $this, 'mirror_settings_to_connectors' ), 10, 2 );
		add_action( 'admin_init', array( $this, 'initial_sync' ), 99 );
		add_filter( 'script_module_data_options-connectors-wp-admin', array( $this, 'mark_bridged_as_connected' ), 20 );
	}

	/**
	 * Get supported provider map.
	 *
	 * @return array<string,array<string,string>> Provider map.
	 */
	private function get_provider_map() {
		return array(
			'openai'     => array(
				'name'            => 'OpenAI',
				'description'     => __( 'Use the OpenAI API key managed by SmartyPress AI Engine.', 'smartypress-ai-engine' ),
				'settings_key'    => 'openai_api_key',
				'connector_key'   => 'connectors_ai_openai_api_key',
				'credentials_url' => 'https://platform.openai.com/api-keys',
				'constant_name'   => 'OPENAI_API_KEY',
			),
			'anthropic'  => array(
				'name'            => 'Claude',
				'description'     => __( 'Use the Anthropic Claude API key managed by SmartyPress AI Engine.', 'smartypress-ai-engine' ),
				'settings_key'    => 'anthropic_api_key',
				'connector_key'   => 'connectors_ai_anthropic_api_key',
				'credentials_url' => 'https://console.anthropic.com/settings/keys',
				'constant_name'   => 'ANTHROPIC_API_KEY',
			),
			'openrouter' => array(
				'name'            => 'OpenRouter',
				'description'     => __( 'Use the OpenRouter API key managed by SmartyPress AI Engine.', 'smartypress-ai-engine' ),
				'settings_key'    => 'openrouter_api_key',
				'connector_key'   => 'connectors_ai_openrouter_api_key',
				'credentials_url' => 'https://openrouter.ai/keys',
				'constant_name'   => 'OPENROUTER_API_KEY',
			),
		);
	}

	/**
	 * Get the current bridge mode.
	 *
	 * @return string Bridge mode.
	 */
	private function get_mode() {
		$mode = get_option( self::OPTION_MODE, 'observe' );
		return in_array( $mode, array( 'observe', 'managed', 'off' ), true ) ? $mode : 'observe';
	}

	/**
	 * Update bridge mode from the Connectors banner.
	 *
	 * @return void
	 */
	public function ajax_set_mode() {
		check_ajax_referer( self::NONCE_ACTION, 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'forbidden' ), 403 );
		}

		$mode = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : '';
		if ( ! in_array( $mode, array( 'observe', 'managed', 'off' ), true ) ) {
			wp_send_json_error( array( 'message' => 'invalid_mode' ), 400 );
		}

		update_option( self::OPTION_MODE, $mode, false );

		if ( 'managed' === $mode ) {
			$this->initial_sync();
		}

		wp_send_json_success( array( 'mode' => $mode ) );
	}

	/**
	 * Mirror a newly added connector key to SmartyPress settings.
	 *
	 * @param string $option Option name.
	 * @param mixed  $value  New option value.
	 * @return void
	 */
	public function maybe_bridge_added_key_to_settings( $option, $value ) {
		$this->maybe_bridge_key_to_settings( $option, '', $value );
	}

	/**
	 * Mirror a connector API key to SmartyPress settings.
	 *
	 * @param string $option    Option name.
	 * @param mixed  $old_value Previous option value.
	 * @param mixed  $new_value New option value.
	 * @return void
	 */
	public function maybe_bridge_key_to_settings( $option, $old_value, $new_value ) {
		if ( self::$bridging || $old_value === $new_value ) {
			return;
		}

		$provider = $this->get_provider_by_connector_key( $option );
		if ( empty( $provider ) ) {
			return;
		}

		$key = is_string( $new_value ) ? sanitize_text_field( $new_value ) : '';
		if ( false !== strpos( $key, '***' ) ) {
			return;
		}

		$settings = get_option( self::SETTINGS_OPTION, array() );
		if ( ! is_array( $settings ) ) {
			$settings = array();
		}

		if ( ( $settings[ $provider['settings_key'] ] ?? '' ) === $key ) {
			return;
		}

		$settings[ $provider['settings_key'] ] = $key;

		self::$bridging = true;
		try {
			update_option( self::SETTINGS_OPTION, $settings );
			SmartyPress_AI_Providers::init( $settings );
		} finally {
			self::$bridging = false;
		}
	}

	/**
	 * Mirror SmartyPress settings API keys to WordPress Connectors.
	 *
	 * @param mixed $old_value Previous settings.
	 * @param mixed $new_value New settings.
	 * @return void
	 */
	public function mirror_settings_to_connectors( $old_value, $new_value ) {
		if ( self::$bridging || ! is_array( $new_value ) ) {
			return;
		}

		self::$bridging = true;
		try {
			foreach ( $this->get_provider_map() as $provider_id => $provider ) {
				if ( ! $this->is_connector_available( $provider_id ) ) {
					continue;
				}

				$key = isset( $new_value[ $provider['settings_key'] ] ) ? sanitize_text_field( $new_value[ $provider['settings_key'] ] ) : '';
				if ( false !== strpos( $key, '***' ) ) {
					continue;
				}

				if ( get_option( $provider['connector_key'], '' ) !== $key ) {
					update_option( $provider['connector_key'], $key, false );
				}
			}
		} finally {
			self::$bridging = false;
		}
	}

	/**
	 * Initial sync from SmartyPress settings to Connectors.
	 *
	 * @return void
	 */
	public function initial_sync() {
		$settings = get_option( self::SETTINGS_OPTION, array() );
		if ( is_array( $settings ) ) {
			$this->mirror_settings_to_connectors( array(), $settings );
		}
	}

	/**
	 * Customize the WordPress Connectors registry in managed mode.
	 *
	 * @param WP_Connector_Registry $registry Connector registry.
	 * @return void
	 */
	public function customize_registry( $registry ) {
		$providers = $this->get_available_managed_providers( $registry );
		$provider_ids = array_keys( $providers );

		foreach ( array_keys( $registry->get_all_registered() ) as $connector_id ) {
			if ( ! in_array( $connector_id, $provider_ids, true ) ) {
				$registry->unregister( $connector_id );
			}
		}

		foreach ( $providers as $provider_id => $provider ) {
			if ( $registry->is_registered( $provider_id ) ) {
				$registry->unregister( $provider_id );
			}

			$registry->register(
				$provider_id,
				array(
					'name'           => $provider['name'],
					'description'    => $provider['description'],
					'type'           => 'ai_provider',
					'plugin'         => array(
						'file'      => SMARTYPRESS_AI_PLUGIN_BASENAME,
						'is_active' => '__return_true',
					),
					'authentication' => array(
						'method'          => 'api_key',
						'credentials_url' => $provider['credentials_url'],
						'setting_name'    => $provider['connector_key'],
						'constant_name'   => $provider['constant_name'],
						'env_var_name'    => $provider['constant_name'],
					),
				)
			);
		}
	}

	/**
	 * Mark bridged connectors as connected in the Connectors SPA data.
	 *
	 * @param array $data Script module data.
	 * @return array Updated script module data.
	 */
	public function mark_bridged_as_connected( array $data ) {
		if ( empty( $data['connectors'] ) || ! is_array( $data['connectors'] ) ) {
			return $data;
		}

		$settings = get_option( self::SETTINGS_OPTION, array() );
		if ( ! is_array( $settings ) ) {
			return $data;
		}

		foreach ( $this->get_provider_map() as $provider_id => $provider ) {
			if ( empty( $settings[ $provider['settings_key'] ] ) || empty( $data['connectors'][ $provider_id ]['authentication'] ) ) {
				continue;
			}

			$data['connectors'][ $provider_id ]['authentication']['isConnected'] = true;
			if ( empty( $data['connectors'][ $provider_id ]['authentication']['keySource'] ) || 'none' === $data['connectors'][ $provider_id ]['authentication']['keySource'] ) {
				$data['connectors'][ $provider_id ]['authentication']['keySource'] = 'database';
			}
		}

		return $data;
	}

	/**
	 * Render the opt-in banner on the WordPress Connectors page.
	 *
	 * @return void
	 */
	public function maybe_render_banner() {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		$screen_id = $screen ? $screen->id : ( $GLOBALS['hook_suffix'] ?? '' );
		$target_screens = array( 'options-connectors', 'options-connectors.php', 'settings_page_options-connectors-wp-admin' );

		if ( ! in_array( $screen_id, $target_screens, true ) ) {
			return;
		}

		$this->render_banner();
	}

	/**
	 * Render banner markup, styles, and behavior.
	 *
	 * @return void
	 */
	private function render_banner() {
		$is_managed = 'managed' === $this->get_mode();
		$payload = array(
			'nonce'        => wp_create_nonce( self::NONCE_ACTION ),
			'ajaxUrl'      => admin_url( 'admin-ajax.php' ),
			'settingsUrl'  => admin_url( 'admin.php?page=smartypress-ai#/settings' ),
			'managed'      => $is_managed,
			'title'        => $is_managed ? __( 'Managed by SmartyPress AI.', 'smartypress-ai-engine' ) : __( 'SmartyPress AI can manage your AI connectors.', 'smartypress-ai-engine' ),
			'description'  => $is_managed ? __( 'OpenAI and Claude keys are synchronized with SmartyPress AI settings.', 'smartypress-ai-engine' ) : __( 'Enable managed mode to mirror supported AI provider keys between WordPress Connectors and SmartyPress AI.', 'smartypress-ai-engine' ),
			'enable'       => __( 'Enable managed mode', 'smartypress-ai-engine' ),
			'dismiss'      => __( 'Dismiss', 'smartypress-ai-engine' ),
			'configure'    => __( 'Configure SmartyPress AI', 'smartypress-ai-engine' ),
			'stop'         => __( 'Stop managing', 'smartypress-ai-engine' ),
			'action'       => 'smartypress_ai_connectors_set_mode',
			'managedClass' => 'is-managed',
		);
		?>
		<style>
			.smartypress-ai-connectors-banner {
				margin: 0 0 16px;
				padding: 12px 16px;
				display: flex;
				align-items: center;
				gap: 12px;
				background: #f0f6fc;
				border: 1px solid #c3d9f0;
				border-radius: 4px;
				color: #1e1e1e;
				font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
			}
			.smartypress-ai-connectors-banner.is-managed {
				background: #edfaef;
				border-color: #b8e6c0;
			}
			.smartypress-ai-connectors-banner__icon {
				width: 32px;
				height: 32px;
				display: flex;
				align-items: center;
				justify-content: center;
				flex: 0 0 auto;
				border-radius: 50%;
				background: #2271b1;
				color: #fff;
				font-weight: 700;
			}
			.smartypress-ai-connectors-banner.is-managed .smartypress-ai-connectors-banner__icon {
				background: #008a20;
			}
			.smartypress-ai-connectors-banner__text {
				flex: 1 1 auto;
				min-width: 0;
			}
			.smartypress-ai-connectors-banner__text strong {
				display: block;
				font-weight: 600;
			}
			.smartypress-ai-connectors-banner__text span {
				color: #50575e;
			}
			.smartypress-ai-connectors-banner__actions {
				display: flex;
				gap: 8px;
				flex: 0 0 auto;
			}
		</style>
		<script>
		(function () {
			var data = <?php echo wp_json_encode( $payload ); ?>;

			function createButton(label, mode, primary) {
				var button = document.createElement('button');
				button.type = 'button';
				button.className = primary ? 'button button-primary' : 'button';
				button.textContent = label;
				button.addEventListener('click', function () {
					button.disabled = true;
					var formData = new FormData();
					formData.append('action', data.action);
					formData.append('nonce', data.nonce);
					formData.append('mode', mode);
					fetch(data.ajaxUrl, {
						method: 'POST',
						credentials: 'same-origin',
						body: formData
					}).then(function () {
						window.location.reload();
					}).catch(function () {
						button.disabled = false;
					});
				});
				return button;
			}

			function createLink(label, url) {
				var link = document.createElement('a');
				link.className = 'button button-primary';
				link.href = url;
				link.textContent = label;
				return link;
			}

			function buildBanner() {
				var banner = document.createElement('div');
				banner.className = 'smartypress-ai-connectors-banner' + (data.managed ? ' ' + data.managedClass : '');
				banner.setAttribute('role', 'status');

				var icon = document.createElement('span');
				icon.className = 'smartypress-ai-connectors-banner__icon';
				icon.textContent = data.managed ? 'OK' : 'AI';

				var text = document.createElement('div');
				text.className = 'smartypress-ai-connectors-banner__text';
				var title = document.createElement('strong');
				title.textContent = data.title;
				var description = document.createElement('span');
				description.textContent = data.description;
				text.appendChild(title);
				text.appendChild(description);

				var actions = document.createElement('div');
				actions.className = 'smartypress-ai-connectors-banner__actions';
				if (data.managed) {
					actions.appendChild(createLink(data.configure, data.settingsUrl));
					actions.appendChild(createButton(data.stop, 'observe', false));
				} else {
					actions.appendChild(createButton(data.enable, 'managed', true));
					actions.appendChild(createButton(data.dismiss, 'off', false));
				}

				banner.appendChild(icon);
				banner.appendChild(text);
				banner.appendChild(actions);
				return banner;
			}

			function hideExternalPromos() {
				if (!data.managed) {
					return;
				}

				document.querySelectorAll('.mwai-wpai-banner').forEach(function (node) {
					node.style.display = 'none';
				});

				var page = document.querySelector('.connectors-page');
				if (!page) {
					return;
				}

				Array.prototype.forEach.call(page.children, function (node) {
					if (node.classList.contains('smartypress-ai-connectors-banner')) {
						return;
					}
					if (node.textContent && node.textContent.indexOf('The AI plugin can use your AI connectors') !== -1) {
						node.style.display = 'none';
					}
				});
			}

			function insertBanner() {
				var existing = document.querySelector('.smartypress-ai-connectors-banner');
				var page = document.querySelector('.connectors-page');
				if (page) {
					hideExternalPromos();
					if (existing) {
						if (existing.parentNode !== page) {
							page.insertBefore(existing, page.firstChild);
						}
						return;
					}
					page.insertBefore(buildBanner(), page.firstChild);
					return;
				}
				if (existing) {
					hideExternalPromos();
					return;
				}
				var app = document.getElementById('options-connectors-wp-admin-app') || document.getElementById('options-connectors-app');
				if (app) {
					app.insertBefore(buildBanner(), app.firstChild);
				}
			}

			var attempts = 0;
			var interval = window.setInterval(function () {
				insertBanner();
				attempts++;
				if (document.querySelector('.smartypress-ai-connectors-banner') || attempts > 50) {
					window.clearInterval(interval);
				}
			}, 120);

			var app = document.getElementById('options-connectors-wp-admin-app') || document.getElementById('options-connectors-app');
			if (app && 'MutationObserver' in window) {
				new MutationObserver(insertBanner).observe(app, { childList: true, subtree: true });
			}
		})();
		</script>
		<?php
	}

	/**
	 * Get provider metadata by connector option name.
	 *
	 * @param string $connector_key Connector option key.
	 * @return array<string,string>|null Provider metadata.
	 */
	private function get_provider_by_connector_key( $connector_key ) {
		foreach ( $this->get_provider_map() as $provider ) {
			if ( $provider['connector_key'] === $connector_key ) {
				return $provider;
			}
		}

		return null;
	}

	/**
	 * Check whether a connector is available in the current registry.
	 *
	 * @param string $provider_id Provider ID.
	 * @return bool True when available.
	 */
	private function is_connector_available( $provider_id ) {
		if ( ! function_exists( 'wp_is_connector_registered' ) ) {
			return false;
		}

		return wp_is_connector_registered( $provider_id );
	}

	/**
	 * Get providers that can safely be shown in managed mode.
	 *
	 * @param WP_Connector_Registry $registry Connector registry.
	 * @return array<string,array<string,string>> Available providers.
	 */
	private function get_available_managed_providers( $registry ) {
		$providers = array();

		foreach ( $this->get_provider_map() as $provider_id => $provider ) {
			if ( $registry->is_registered( $provider_id ) ) {
				$providers[ $provider_id ] = $provider;
			}
		}

		return $providers;
	}
}
