<?php
/**
 * Plugin Name:       Kripanov Anticopia
 * Description:       Anti-copy protection for WordPress. Disables right-click, keyboard shortcuts (copy, paste, cut, save, print), content selection, image dragging, mobile long-press and inspection tools (DevTools) to prevent content theft and web scraping.
 * Version:           1.1.0
 * Requires at least: 6.3
 * Tested up to:      7.0
 * Requires PHP:      7.4
 * Author:            Kripanov
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       kripanov-anticopia
 *
 * @package KripanovAnticopia
 *
 * Kripanov Anticopia is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * Kripanov Anticopia is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Kripanov Anticopia. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
 */

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

define( 'KRIPANOV_ANTICOPIA_VERSION', '1.1.0' );
define( 'KRIPANOV_ANTICOPIA_FILE', __FILE__ );
define( 'KRIPANOV_ANTICOPIA_DIR', plugin_dir_path( __FILE__ ) );
define( 'KRIPANOV_ANTICOPIA_URL', plugin_dir_url( __FILE__ ) );
define( 'KRIPANOV_ANTICOPIA_BASENAME', plugin_basename( __FILE__ ) );
define( 'KRIPANOV_ANTICOPIA_OPTION', 'kripanov_anticopia_settings' );

/**
 * Returns the default plugin options.
 *
 * @since 1.0.0
 * @return array<string, mixed>
 */
function kripanov_anticopia_default_options() {
	return array(
		'enabled'                 => 1,
		'disable_rightclick'      => 1,
		'disable_selection'       => 1,
		'disable_copy'            => 1,
		'disable_cut'             => 1,
		'disable_paste'           => 1,
		'disable_shortcuts'       => 1,
		'disable_devtools'        => 1,
		'disable_dragimg'         => 1,
		'disable_print'           => 1,
		'empty_clipboard'         => 1,
		'disable_mobile_gestures' => 1,
		'protect_images'          => 1,
		'exclude_admins'          => 1,
		'exclude_roles'           => '',
		'exclude_urls'            => '',
		'show_message'            => 1,
		'custom_message'          => __( 'Protected content. Copying is not allowed.', 'kripanov-anticopia' ),
		'clipboard_text'          => __( 'Protected content. Copying is not allowed.', 'kripanov-anticopia' ),
	);
}

/**
 * Gets saved options merged with defaults.
 *
 * @since 1.0.0
 * @return array<string, mixed>
 */
function kripanov_anticopia_get_options() {
	$saved = get_option( KRIPANOV_ANTICOPIA_OPTION, array() );
	if ( ! is_array( $saved ) ) {
		$saved = array();
	}
	return wp_parse_args( $saved, kripanov_anticopia_default_options() );
}

/**
 * Parses a multiline / comma-separated exclusion list into a clean array.
 *
 * @since 1.1.0
 * @param string $raw Raw exclusion string.
 * @return array<int, string>
 */
function kripanov_anticopia_parse_list( $raw ) {
	$raw = (string) $raw;
	if ( '' === trim( $raw ) ) {
		return array();
	}

	$parts = preg_split( '/[\r\n,]+/', $raw );
	if ( ! is_array( $parts ) ) {
		return array();
	}

	$items = array();
	foreach ( $parts as $part ) {
		$part = trim( sanitize_text_field( $part ) );
		if ( '' !== $part ) {
			$items[] = $part;
		}
	}

	return array_values( array_unique( $items ) );
}

/**
 * Checks whether the current request path matches an excluded URL pattern.
 *
 * Patterns may be absolute URLs, paths (/contacto/), or simple wildcards (*).
 *
 * @since 1.1.0
 * @param array<int, string> $patterns Exclusion patterns.
 * @return bool
 */
function kripanov_anticopia_url_is_excluded( $patterns ) {
	if ( empty( $patterns ) ) {
		return false;
	}

	$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '/';
	$request_uri = (string) $request_uri;
	$path        = (string) wp_parse_url( $request_uri, PHP_URL_PATH );
	if ( '' === $path ) {
		$path = '/';
	}

	$current_url = home_url( $request_uri );

	foreach ( $patterns as $pattern ) {
		$pattern = trim( $pattern );
		if ( '' === $pattern ) {
			continue;
		}

		// Exact path match.
		if ( $pattern === $path || $pattern === $request_uri ) {
			return true;
		}

		// Absolute URL containment.
		if ( false !== strpos( $current_url, $pattern ) ) {
			return true;
		}

		// Path containment for plain fragments.
		if ( 0 !== strpos( $pattern, 'http' ) && false !== strpos( $path, $pattern ) ) {
			return true;
		}

		// Simple wildcard (*).
		if ( false !== strpos( $pattern, '*' ) ) {
			$regex = '#^' . str_replace( '\*', '.*', preg_quote( $pattern, '#' ) ) . '$#i';
			if ( preg_match( $regex, $path ) || preg_match( $regex, $current_url ) ) {
				return true;
			}
		}
	}

	return false;
}

/**
 * Checks whether the current user has an excluded role.
 *
 * @since 1.1.0
 * @param array<int, string> $roles Role slugs to exclude.
 * @return bool
 */
function kripanov_anticopia_user_has_excluded_role( $roles ) {
	if ( empty( $roles ) || ! is_user_logged_in() ) {
		return false;
	}

	$user = wp_get_current_user();
	if ( ! $user || empty( $user->roles ) ) {
		return false;
	}

	return (bool) array_intersect( $roles, (array) $user->roles );
}

/**
 * Plugin activation: sets default options if none exist.
 *
 * @since 1.0.0
 * @return void
 */
function kripanov_anticopia_activate() {
	if ( false === get_option( KRIPANOV_ANTICOPIA_OPTION ) ) {
		add_option( KRIPANOV_ANTICOPIA_OPTION, kripanov_anticopia_default_options() );
	}
}
register_activation_hook( __FILE__, 'kripanov_anticopia_activate' );

/**
 * Determines whether the anti-copy protection should be applied to the current request.
 *
 * @since 1.0.0
 * @return bool
 */
function kripanov_anticopia_should_apply() {
	$options = kripanov_anticopia_get_options();

	if ( empty( $options['enabled'] ) ) {
		return false;
	}

	if ( is_admin() ) {
		return false;
	}

	// Keep login / registration usable.
	if ( function_exists( 'is_login' ) && is_login() ) {
		return false;
	}

	if ( ! empty( $options['exclude_admins'] ) && current_user_can( 'manage_options' ) ) {
		return false;
	}

	$excluded_roles = kripanov_anticopia_parse_list( (string) $options['exclude_roles'] );
	if ( kripanov_anticopia_user_has_excluded_role( $excluded_roles ) ) {
		return false;
	}

	$excluded_urls = kripanov_anticopia_parse_list( (string) $options['exclude_urls'] );
	if ( kripanov_anticopia_url_is_excluded( $excluded_urls ) ) {
		return false;
	}

	return true;
}

/**
 * Enqueues frontend scripts and styles.
 *
 * @since 1.0.0
 * @return void
 */
function kripanov_anticopia_enqueue_assets() {
	if ( ! kripanov_anticopia_should_apply() ) {
		return;
	}

	$options = kripanov_anticopia_get_options();

	wp_enqueue_style(
		'kripanov-anticopia',
		KRIPANOV_ANTICOPIA_URL . 'assets/css/anticopia.css',
		array(),
		KRIPANOV_ANTICOPIA_VERSION
	);

	wp_enqueue_script(
		'kripanov-anticopia',
		KRIPANOV_ANTICOPIA_URL . 'assets/js/anticopia.js',
		array(),
		KRIPANOV_ANTICOPIA_VERSION,
		array(
			'in_footer' => true,
			'strategy'  => 'defer',
		)
	);

	$js_options = array(
		'disableRightClick'     => ! empty( $options['disable_rightclick'] ),
		'disableSelection'      => ! empty( $options['disable_selection'] ),
		'disableCopy'           => ! empty( $options['disable_copy'] ),
		'disableCut'            => ! empty( $options['disable_cut'] ),
		'disablePaste'          => ! empty( $options['disable_paste'] ),
		'disableShortcuts'      => ! empty( $options['disable_shortcuts'] ),
		'disableDevtools'       => ! empty( $options['disable_devtools'] ),
		'disableDragImg'        => ! empty( $options['disable_dragimg'] ),
		'disablePrint'          => ! empty( $options['disable_print'] ),
		'emptyClipboard'        => ! empty( $options['empty_clipboard'] ),
		'disableMobileGestures' => ! empty( $options['disable_mobile_gestures'] ),
		'protectImages'         => ! empty( $options['protect_images'] ),
		'showMessage'           => ! empty( $options['show_message'] ),
		'customMessage'         => sanitize_text_field( (string) $options['custom_message'] ),
		'clipboardText'         => sanitize_text_field( (string) $options['clipboard_text'] ),
	);

	wp_add_inline_script(
		'kripanov-anticopia',
		'window.KripanovAnticopia = ' . wp_json_encode( $js_options ) . ';',
		'before'
	);
}
add_action( 'wp_enqueue_scripts', 'kripanov_anticopia_enqueue_assets' );

/**
 * Prints a tiny early inline guard to reduce the unprotected window before deferred JS loads.
 *
 * @since 1.1.0
 * @return void
 */
function kripanov_anticopia_early_guard() {
	if ( ! kripanov_anticopia_should_apply() ) {
		return;
	}

	$options = kripanov_anticopia_get_options();
	if ( empty( $options['disable_rightclick'] ) && empty( $options['disable_selection'] ) ) {
		return;
	}

	echo "<script id=\"kripanov-anticopia-early\">(function(){try{";
	if ( ! empty( $options['disable_rightclick'] ) ) {
		echo "document.addEventListener('contextmenu',function(e){e.preventDefault();},true);";
	}
	if ( ! empty( $options['disable_selection'] ) ) {
		echo "document.addEventListener('selectstart',function(e){var t=e.target;if(t&&(t.tagName==='INPUT'||t.tagName==='TEXTAREA'||t.isContentEditable))return;e.preventDefault();},true);";
	}
	echo "}catch(e){}})();</script>\n";
}
add_action( 'wp_head', 'kripanov_anticopia_early_guard', 1 );

/**
 * Adds body classes so the CSS selectors can react to configuration.
 *
 * @since 1.0.0
 * @param array<int, string> $classes Existing body classes.
 * @return array<int, string>
 */
function kripanov_anticopia_body_class( $classes ) {
	if ( ! kripanov_anticopia_should_apply() ) {
		return $classes;
	}

	$options = kripanov_anticopia_get_options();

	if ( ! empty( $options['disable_selection'] ) ) {
		$classes[] = 'anticopia-no-select';
	}
	if ( ! empty( $options['disable_dragimg'] ) || ! empty( $options['protect_images'] ) ) {
		$classes[] = 'anticopia-no-drag';
	}
	if ( ! empty( $options['protect_images'] ) ) {
		$classes[] = 'anticopia-protect-images';
	}
	if ( ! empty( $options['disable_mobile_gestures'] ) ) {
		$classes[] = 'anticopia-no-touchcallout';
	}
	if ( ! empty( $options['disable_print'] ) ) {
		$classes[] = 'anticopia-no-print';
	}

	return $classes;
}
add_filter( 'body_class', 'kripanov_anticopia_body_class' );

/**
 * Loads the admin settings page.
 */
if ( is_admin() ) {
	require_once KRIPANOV_ANTICOPIA_DIR . 'admin/settings.php';
}

/**
 * Adds a "Settings" link in the plugins list.
 *
 * @since 1.0.0
 * @param array<string, string> $links Existing plugin action links.
 * @return array<string, string>
 */
function kripanov_anticopia_action_links( $links ) {
	$settings_link = '<a href="' . esc_url( admin_url( 'options-general.php?page=kripanov-anticopia' ) ) . '">' . esc_html__( 'Settings', 'kripanov-anticopia' ) . '</a>';
	array_unshift( $links, $settings_link );
	return $links;
}
add_filter( 'plugin_action_links_' . KRIPANOV_ANTICOPIA_BASENAME, 'kripanov_anticopia_action_links' );
