<?php
/**
 * Frontend protection: SEO-safe, accessibility-aware enqueue of the engine.
 *
 * @package Copyfend
 */

defined( 'ABSPATH' ) || exit;

/**
 * Decides when to protect and hands a config object to the client engine.
 */
class CShield_Frontend {

	/**
	 * Settings handler.
	 *
	 * @var CShield_Settings
	 */
	private $settings;

	/**
	 * Constructor.
	 *
	 * @param CShield_Settings $settings Settings handler.
	 */
	public function __construct( CShield_Settings $settings ) {
		$this->settings = $settings;
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ) );
		add_action( 'wp_ajax_cshield_copy_ping', array( __CLASS__, 'copy_ping' ) );
		add_action( 'wp_ajax_nopriv_cshield_copy_ping', array( __CLASS__, 'copy_ping' ) );
	}

	/**
	 * Anonymous counter: a visitor copied text and got the attribution appended.
	 *
	 * Reads nothing from the request and stores no visitor data — it only
	 * increments a weekly counter, so there is no state a CSRF could abuse and
	 * no nonce (which would break on cached pages anyway). A short, hashed,
	 * self-expiring transient throttles repeat pings from the same address.
	 */
	public static function copy_ping() {
		$addr     = CShield_IP::client_ip();
		$throttle = 'cshield_cp_' . md5( (string) $addr );
		if ( false === get_transient( $throttle ) ) {
			set_transient( $throttle, 1, 15 );

			$stats = get_option( 'cshield_copy_stats', array() );
			$week  = gmdate( 'oW' );
			if ( ! isset( $stats['week'] ) || $stats['week'] !== $week ) {
				$stats['week']       = $week;
				$stats['week_count'] = 0;
			}
			$stats['week_count'] = (int) $stats['week_count'] + 1;
			$stats['total']      = (int) ( isset( $stats['total'] ) ? $stats['total'] : 0 ) + 1;
			update_option( 'cshield_copy_stats', $stats, false );
		}
		wp_send_json_success();
	}

	/**
	 * Enqueue the deterrence engine when appropriate.
	 */
	public function enqueue() {
		if ( ! $this->should_protect() ) {
			return;
		}

		wp_register_script(
			'cshield-engine',
			CSHIELD_URL . 'assets/js/copyfend.js',
			array(),
			CSHIELD_VERSION,
			array(
				'strategy'  => 'defer',
				'in_footer' => true,
			)
		);

		wp_add_inline_script(
			'cshield-engine',
			'window.cshieldConfig = ' . wp_json_encode( $this->config() ) . ';',
			'before'
		);

		wp_enqueue_script( 'cshield-engine' );
	}

	/**
	 * Whether the current request should receive protection.
	 *
	 * @return bool
	 */
	private function should_protect() {
		$s = $this->settings;

		if ( ! $s->get( 'enabled' ) || is_admin() || is_feed() ) {
			return false;
		}

		if ( $s->get( 'exclude_admins' ) && current_user_can( 'manage_options' ) ) {
			return false;
		}

		// SEO-safe by design: never interfere with search-engine indexing.
		if ( $s->get( 'allow_search_bots' ) && $this->is_search_bot() ) {
			return false;
		}

		// Per-role exclusion (e.g. leave editors / members unprotected).
		if ( $this->role_excluded() ) {
			return false;
		}

		if ( is_singular() ) {
			$override = get_post_meta( (int) get_the_ID(), '_cshield_protect', true );

			if ( 'off' === $override ) {
				return false;
			}

			// 'on' forces protection; otherwise apply post-type + category targeting.
			if ( 'on' !== $override ) {
				$types = $s->get( 'protected_post_types', null );
				if ( is_array( $types ) && ! in_array( get_post_type(), $types, true ) ) {
					return false;
				}
				if ( $this->term_excluded( (int) get_the_ID() ) ) {
					return false;
				}
			}
		}

		return true;
	}

	/**
	 * Whether the current user has a role excluded from protection.
	 *
	 * @return bool
	 */
	private function role_excluded() {
		$roles = (array) $this->settings->get( 'protect_exclude_roles', array() );
		if ( empty( $roles ) || ! is_user_logged_in() ) {
			return false;
		}
		return (bool) array_intersect( (array) wp_get_current_user()->roles, $roles );
	}

	/**
	 * Whether the post belongs to an excluded category.
	 *
	 * @param int $post_id Post ID.
	 * @return bool
	 */
	private function term_excluded( $post_id ) {
		$terms = (array) $this->settings->get( 'protect_exclude_terms', array() );
		if ( empty( $terms ) ) {
			return false;
		}
		return has_category( array_map( 'intval', $terms ), $post_id );
	}

	/**
	 * Basic search-engine bot detection (user-agent whitelist).
	 *
	 * @return bool
	 */
	private function is_search_bot() {
		$ua = isset( $_SERVER['HTTP_USER_AGENT'] )
			? strtolower( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) )
			: '';

		if ( '' === $ua ) {
			return false;
		}

		$bots = array( 'googlebot', 'bingbot', 'slurp', 'duckduckbot', 'baiduspider', 'yandex', 'sogou', 'exabot', 'facebot', 'ia_archiver', 'applebot' );

		foreach ( $bots as $bot ) {
			if ( false !== strpos( $ua, $bot ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Build the client config object.
	 *
	 * @return array
	 */
	private function config() {
		$s = $this->settings;

		return array(
			'rightClick'      => (bool) $s->get( 'disable_right_click' ),
			'selection'       => (bool) $s->get( 'disable_selection' ),
			'copyCut'         => (bool) $s->get( 'disable_copy_cut' ),
			'shortcuts'       => (bool) $s->get( 'disable_shortcuts' ),
			'imageDrag'       => (bool) $s->get( 'disable_image_drag' ),
			'blockPrint'      => (bool) $s->get( 'block_print' ),
			'attribution'     => (bool) $s->get( 'append_attribution' ),
			'attributionText' => (string) $s->get( 'attribution_text' ),
			'message'         => (string) $s->get( 'notice_message' ),
			'pingUrl'         => $s->get( 'append_attribution' ) ? admin_url( 'admin-ajax.php' ) : '',
		);
	}
}
