<?php
/**
 * File Path: includes/admin/tabs/class-mbs-tab-affiliate.php
 *
 * Earn More Tab
 *
 * Displays and saves affiliate codes for 10 known retailers plus up to 3 custom
 * stores. Codes are appended to buy link URLs at render time only by
 * MBS_Affiliate_Rewriter (PRO only — AP-2: stored URLs are never modified).
 *
 * Known retailer fields (10):
 *   amazon      → ?tag={CODE}             Amazon Associates
 *   bn          → ?sid={CODE}             Barnes & Noble via Rakuten/LinkShare
 *   kobo        → ?utm_source={CODE}      Kobo Affiliate Program
 *   audible     → ?source_code={CODE}     Audible affiliate source tag
 *   bam         → ?aff={CODE}             Books-A-Million affiliate
 *   bookshop    → ?a_aid={CODE}           Bookshop.org affiliate ID
 *   waterstones → ?aff={CODE}             Waterstones affiliate
 *   apple       → NOT REWRITTEN           Apple Performance Partners (server-side)
 *   google      → NOT REWRITTEN           Google Play Affiliate (server-side)
 *   powells     → ?affiliate={CODE}       Powell's Books affiliate program
 *
 * Custom stores: up to 3. Stored as JSON in mbs_setting_affiliate_custom_stores.
 * Rewritten at render time by MBS_Affiliate_Rewriter via {CODE} substitution.
 *
 * Save flow (retailer codes + custom stores JSON): custom admin_init handler (save_settings()).
 * Settings group: mbs_affiliate (register()-only; actual save bypasses options.php).
 * Favicon fetch: wp_ajax_mbs_fetch_affiliate_favicon (AJAX, PRO only).
 *
 * @package MyBookShowroom
 * @since   1.0.0
 */

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

// phpcs:disable WordPress.Security.ValidatedSanitizedInput -- input sanitized at point of use (sanitize_text_field, absint, wp_kses_post, etc.); false positives from PHPCS not tracing sanitization helpers called before this method
class MBS_Tab_Affiliate {

	public function __construct() {
		// Boot-time: register AJAX action so it fires regardless of which tab is active.
		add_action( 'wp_ajax_mbs_fetch_affiliate_favicon', array( $this, 'handle_ajax_fetch_affiliate_favicon' ) );
		add_action( 'admin_init', array( $this, 'save_settings' ) );
	}

	/**
	 * Handle affiliate settings form save via admin_init.
	 *
	 * Uses the same PRG pattern as MBS_Tab_Dashboard::save_settings().
	 * Bypasses options.php entirely — update_option() is called directly.
	 * Nonce: mbs_affiliate_save / mbs_affiliate_nonce.
	 *
	 * @return void
	 */
	public function save_settings(): void {
		if ( ! isset( $_POST['mbs_affiliate_nonce'] ) ) {
			return;
		}
		check_admin_referer( 'mbs_affiliate_save', 'mbs_affiliate_nonce' );
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		// Known retailer codes — each sanitized as plain text.
		$retailer_keys = array( 'amazon', 'bn', 'kobo', 'audible', 'bam', 'bookshop', 'waterstones', 'apple', 'google', 'powells' );
		foreach ( $retailer_keys as $key ) {
			$option = 'mbs_setting_affiliate_' . $key;
			update_option( $option, sanitize_text_field( wp_unslash( $_POST[ $option ] ?? '' ) ) );
		}

		// Custom stores JSON (up to 3 entries) — sanitize per field.
		$stores_raw = wp_unslash( $_POST['mbs_setting_affiliate_custom_stores'] ?? '[]' );
		$stores     = json_decode( $stores_raw, true );
		if ( ! is_array( $stores ) ) {
			$stores = array();
		}
		$clean_stores = array();
		foreach ( array_slice( $stores, 0, 3 ) as $store ) {
			if ( ! is_array( $store ) ) {
				continue;
			}
			$clean = array();
			foreach ( $store as $k => $v ) {
				$k          = sanitize_key( $k );
				$clean[ $k ] = in_array( $k, array( 'url', 'favicon' ), true )
					? esc_url_raw( (string) $v )
					: sanitize_text_field( (string) $v );
			}
			$clean_stores[] = $clean;
		}
		MBS_Affiliate_Store_DAO::save_all( $clean_stores ); // RF-012.

		// PRG redirect back to the Promote tab.
		wp_safe_redirect(
			add_query_arg(
				array( 'page' => 'my-book-showroom-settings', 'tab' => 'promote', 'updated' => '1' ),
				admin_url( 'admin.php' )
			)
		);
		exit;
	}

	/**
	 * Register all Affiliate/Earn-More-tab settings with the WordPress Settings API.
	 *
	 * Called by MBS_Settings::register_settings() via the delegate pattern (REF-012,
	 * Session 136). Option names match mbs_get_setting( 'affiliate', $key ) reads
	 * in MBS_Affiliate_Rewriter.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register(): void {
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_amazon',      array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_bn',          array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_kobo',        array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_audible',     array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_bam',         array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_bookshop',    array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_waterstones', array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_apple',       array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_google',      array( 'sanitize_callback' => 'sanitize_text_field' ) );
		register_setting( 'mbs_affiliate', 'mbs_setting_affiliate_powells',     array( 'sanitize_callback' => 'sanitize_text_field' ) );
	}

	/**
	 * Enqueue tab-specific assets when this tab is active.
	 *
	 * @param string $current_tab The currently active settings tab slug.
	 */
	public function enqueue( string $current_tab ): void {
		if ( 'affiliate' !== $current_tab ) {
			return;
		}

		wp_enqueue_script(
			'mbs-admin-earn-more',
			plugins_url( 'assets/js/adminjs/admin-earn-more.js', MBS_DIR . 'bookshowroom.php' ),
			array( 'jquery' ),
			MBS_VERSION,
			true
		);

		$custom_stores = MBS_Affiliate_Store_DAO::get_all(); // RF-012.

		wp_localize_script( 'mbs-admin-earn-more', 'MBS_EarnMore', array(
			'ajaxUrl'      => admin_url( 'admin-ajax.php' ),
			'nonce'        => wp_create_nonce( 'mbs_fetch_affiliate_favicon' ),
			'customStores' => $custom_stores,
			'maxStores'    => 3,
			'strings'      => array(
				'fetchingFavicon' => __( 'Fetching icon…', 'my-book-showroom' ),
				'removeBtnLabel'  => __( 'Remove', 'my-book-showroom' ),
				'noIconLabel'     => __( 'No icon found', 'my-book-showroom' ),
				'maxStoresMsg'    => __( 'Maximum stores reached (3/3). Remove a store to add another.', 'my-book-showroom' ),
				'storeName'       => __( 'Store Name', 'my-book-showroom' ),
				'buyNow'          => __( 'BUY NOW', 'my-book-showroom' ),
				'nameRequired'    => __( 'Store name is required.', 'my-book-showroom' ),
				'urlRequired'     => __( 'Store URL is required.', 'my-book-showroom' ),
				'patternInvalid'  => __( 'URL pattern must include {CODE}.', 'my-book-showroom' ),
			),
		) );

		wp_enqueue_style(
			'mbs-admin-settings-affiliate',
			plugins_url( 'assets/css/mbs-admin-settings-affiliate.css', MBS_DIR . 'bookshowroom.php' ),
			array( 'mbs-admin-settings' ),
			MBS_VERSION
		);
	}

	/**
	 * Render the Earn More tab.
	 *
	 * Delegates to MBS_Tab_Affiliate_Render::render().
	 * Kept as a thin instance method so class-mbs-settings.php can call
	 * $tab->render() without modification (R-17 delegate pattern).
	 *
	 * @return void
	 */
	public function render(): void {
		MBS_Tab_Affiliate_Render::render();
	}

	/**
	 * AJAX handler: fetch a favicon URL for a custom store domain.
	 *
	 * PRO only. Nonce: mbs_fetch_affiliate_favicon.
	 *
	 * @return void  Sends JSON response and exits.
	 */
	public function handle_ajax_fetch_affiliate_favicon(): void {
		check_ajax_referer( 'mbs_fetch_affiliate_favicon', 'nonce' );

		if ( ! mbs_is_pro() || ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'PRO required' ), 403 );
		}

		$domain = sanitize_text_field( wp_unslash( $_POST['domain'] ?? '' ) );
		if ( empty( $domain ) ) {
			wp_send_json_error( array( 'message' => 'No domain provided' ), 400 );
		}

		$favicon_url = 'https://www.google.com/s2/favicons?sz=32&domain=' . rawurlencode( $domain );
		wp_send_json_success( array( 'favicon_url' => esc_url_raw( $favicon_url ) ) );
	}
}
