<?php
/**
 * Settings storage, defaults and sanitization.
 *
 * @package Copyfend
 */

defined( 'ABSPATH' ) || exit;

/**
 * Reads, writes and sanitizes the plugin option array.
 *
 * Each setting is declared once in {@see CShield_Settings::fields()} with a type and
 * a default, so defaults and sanitization stay in sync as the plugin grows.
 */
class CShield_Settings {

	const OPTION = 'cshield_settings';

	/**
	 * Merged options (saved values over defaults).
	 *
	 * @var array
	 */
	private $options;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->options = wp_parse_args( get_option( self::OPTION, array() ), self::defaults() );
	}

	/**
	 * Field schema: key => array( type, default ).
	 *
	 * Types: 'bool' (0/1 checkbox), 'int' (absint), 'email' (sanitize_email),
	 * 'text' (sanitize_text_field).
	 *
	 * Client-side defaults are tuned for the blogs/media/SEO audience: copying is
	 * *allowed* but every copy gets a source attribution appended (a free
	 * backlink), while casual right-click and image-drag are deterred.
	 *
	 * @return array
	 */
	public static function fields() {
		return array(
			// Client-side deterrence (free).
			'enabled'             => array( 'bool', 1 ),
			'disable_right_click' => array( 'bool', 1 ),
			'disable_selection'   => array( 'bool', 0 ),
			'disable_copy_cut'    => array( 'bool', 0 ),
			'disable_shortcuts'   => array( 'bool', 0 ),
			'disable_image_drag'  => array( 'bool', 1 ),
			'block_print'         => array( 'bool', 0 ),
			'append_attribution'  => array( 'bool', 1 ),
			'attribution_text'    => array( 'text', 'Source: {url}' ),
			'notice_message'      => array( 'text', 'Copying is restricted on this site.' ),
			'exclude_admins'      => array( 'bool', 1 ),
			'allow_search_bots'   => array( 'bool', 1 ),

			// Server-side anti-scraping (M3 — will be PRO-gated in M2).
			'block_ai_crawlers'   => array( 'bool', 0 ),
			'block_bad_bots'      => array( 'bool', 0 ),
			'rate_limit'          => array( 'bool', 0 ),
			'truncate_rss'        => array( 'bool', 0 ),
			'rss_words'           => array( 'int', 55 ),

			// Honeypot (PRO).
			'honeypot_enabled'    => array( 'bool', 0 ),
			'honeypot_block'      => array( 'bool', 1 ),
			'honeypot_canary'     => array( 'bool', 0 ),

			// Attribution & copyright / DMCA (M4 — free).
			'copyright_holder'    => array( 'text', '' ),
			'copyright_email'     => array( 'email', '' ),
			'copyright_license'   => array( 'text', 'All rights reserved' ),
			'copyright_meta'      => array( 'bool', 0 ),
			'rss_attribution'     => array( 'bool', 0 ),

			// Targeting (M7 — free post-type targeting; per-post/category/role are PRO).
			'protected_post_types'  => array( 'post_types', null ),
			'protect_exclude_terms' => array( 'terms', array() ),
			'protect_exclude_roles' => array( 'roles', array() ),

			// Forensic watermark (M5 POC — PRO once Freemius lands).
			'forensic_enabled'     => array( 'bool', 0 ),

			// Forensic access log (PRO).
			'forensic_log'              => array( 'bool', 0 ),
			'forensic_log_anonymize_ip' => array( 'bool', 1 ),
			'forensic_log_retention'    => array( 'int', 30 ),

			// Image watermark (M6 — PRO).
			'watermark_enabled'       => array( 'bool', 0 ),
			'watermark_apply_logo'    => array( 'bool', 0 ),
			'watermark_logo'          => array( 'int', 0 ),
			'watermark_apply_text'    => array( 'bool', 0 ),
			'watermark_text'          => array( 'text', '© {year} {site}' ),
			'watermark_position'      => array( 'text', 'bottom-right' ),
			'watermark_opacity'       => array( 'int', 60 ),
			'watermark_text_size'     => array( 'int', 30 ),
			'watermark_keep_original' => array( 'bool', 1 ),

			// Content Locker (M8 — FREE email-gate + password lock; integrations PRO).
			'locker_enabled'     => array( 'bool', 0 ),
			'locker_gate_title'  => array( 'text', 'Keep reading' ),
			'locker_gate_desc'   => array( 'text', 'Enter your email to unlock the rest of this content.' ),
			'locker_gate_button' => array( 'text', 'Unlock' ),
			'locker_lock_title'  => array( 'text', 'Protected content' ),
			'locker_lock_desc'   => array( 'text', 'Enter the password to view this content.' ),
		);
	}

	/**
	 * Default values, derived from the field schema.
	 *
	 * @return array
	 */
	public static function defaults() {
		$defaults = array();
		foreach ( self::fields() as $key => $field ) {
			$defaults[ $key ] = $field[1];
		}
		return $defaults;
	}

	/**
	 * Create the option on first activation only (never overwrites user config).
	 */
	public static function install_defaults() {
		if ( false === get_option( self::OPTION, false ) ) {
			add_option( self::OPTION, self::defaults() );
		}
	}

	/**
	 * Get a single setting.
	 *
	 * @param string $key     Setting key.
	 * @param mixed  $default Fallback.
	 * @return mixed
	 */
	public function get( $key, $default = null ) {
		return array_key_exists( $key, $this->options ) ? $this->options[ $key ] : $default;
	}

	/**
	 * Get all settings.
	 *
	 * @return array
	 */
	public function all() {
		return $this->options;
	}

	/**
	 * Sanitize submitted settings against the field schema.
	 *
	 * @param array $input Raw $_POST values.
	 * @return array
	 */
	public static function sanitize( $input ) {
		$input = is_array( $input ) ? $input : array();
		$clean = array();

		foreach ( self::fields() as $key => $field ) {
			list( $type, $default ) = $field;

			switch ( $type ) {
				case 'bool':
					$clean[ $key ] = empty( $input[ $key ] ) ? 0 : 1;
					break;
				case 'int':
					$clean[ $key ] = isset( $input[ $key ] ) ? absint( $input[ $key ] ) : $default;
					break;
				case 'email':
					$clean[ $key ] = isset( $input[ $key ] ) ? sanitize_email( $input[ $key ] ) : $default;
					break;
				case 'post_types':
					$submitted     = ( isset( $input[ $key ] ) && is_array( $input[ $key ] ) ) ? $input[ $key ] : array();
					$public_types  = array_keys( get_post_types( array( 'public' => true ) ) );
					$clean[ $key ] = array_values( array_intersect( array_map( 'sanitize_key', $submitted ), $public_types ) );
					break;
				case 'terms':
					$submitted     = ( isset( $input[ $key ] ) && is_array( $input[ $key ] ) ) ? $input[ $key ] : array();
					$clean[ $key ] = array_values( array_unique( array_filter( array_map( 'absint', $submitted ) ) ) );
					break;
				case 'roles':
					$submitted     = ( isset( $input[ $key ] ) && is_array( $input[ $key ] ) ) ? $input[ $key ] : array();
					$valid_roles   = array_keys( wp_roles()->roles );
					$clean[ $key ] = array_values( array_intersect( array_map( 'sanitize_key', $submitted ), $valid_roles ) );
					break;
				case 'text':
				default:
					$clean[ $key ] = isset( $input[ $key ] ) ? sanitize_text_field( $input[ $key ] ) : $default;
					break;
			}
		}

		return $clean;
	}
}
