<?php
/**
 * Rate Limiter class for AskAny plugin.
 *
 * Handles per-role and per-IP rate limiting for chat requests.
 *
 * @package Askany
 * @since 1.0.0
 */
namespace Askany;

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

class RateLimiter {

	/**
	 * WordPress roles supported for rate limiting.
	 */
	const ROLES = array(
		'administrator',
		'editor',
		'author',
		'contributor',
		'subscriber',
		'customer',
		'shop_manager',
	);

	/**
	 * Timeframe seconds map.
	 */
	const TIMEFRAMES = array(
		'per_hour'  => 3600,
		'per_day'   => 86400,
		'per_week'  => 604800,
		'per_month' => 2592000,
	);

	/**
	 * Check if rate limiting is enabled.
	 *
	 * @return bool
	 */
	public static function is_enabled() {
		return (bool) get_option( 'askany_rate_limit_enabled', false );
	}

	/**
	 * Get rate limit settings for all roles.
	 *
	 * @return array
	 */
	public static function get_role_settings() {
		$saved = get_option( 'askany_rate_limit_roles', array() );
		$defaults = array();

		foreach ( self::ROLES as $role ) {
			$defaults[ $role ] = array(
				'limit'            => 100,
				'timeframe'        => 'per_day',
				'custom_message'   => __( 'Rate limit exceeded. Please try again later.', 'askany' ),
				'use_fallback'     => false,
			);
		}

		// Visitor/logged-out
		$defaults['visitor'] = array(
			'limit'          => 100,
			'timeframe'      => 'per_day',
			'custom_message' => __( 'Rate limit exceeded. Please try again later.', 'askany' ),
			'use_fallback'   => false,
		);

		if ( ! is_array( $saved ) ) {
			return $defaults;
		}

		return array_merge( $defaults, $saved );
	}

	/**
	 * Get the visitor's real IP address.
	 *
	 * @return string
	 */
	public static function get_client_ip() {
		$ip = '';

		if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
			$parts = explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) );
			$ip    = trim( $parts[0] );
		} elseif ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
			$ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_CLIENT_IP'] ) );
		} elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
			$ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
		}

		return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : '0.0.0.0';
	}

	/**
	 * Get the role key for the current user.
	 *
	 * @param int $user_id
	 * @return string  role slug or 'visitor'
	 */
	public static function get_user_role( $user_id ) {
		if ( ! $user_id ) {
			return 'visitor';
		}

		$user = get_userdata( $user_id );
		if ( ! $user || empty( $user->roles ) ) {
			return 'visitor';
		}

		// Priority order
		$priority = array( 'administrator', 'shop_manager', 'editor', 'author', 'contributor', 'customer', 'subscriber' );
		foreach ( $priority as $role ) {
			if ( in_array( $role, $user->roles, true ) ) {
				return $role;
			}
		}

		return 'subscriber';
	}

	/**
	 * Build the transient key for a user/IP.
	 *
	 * @param int    $user_id
	 * @param string $role
	 * @param string $timeframe
	 * @return string
	 */
	private static function transient_key( $user_id, $role, $timeframe ) {
		if ( $role === 'visitor' ) {
			$ip  = self::get_client_ip();
			$key = 'askany_rl_ip_' . md5( $ip ) . '_' . $timeframe;
		} else {
			$key = 'askany_rl_u' . intval( $user_id ) . '_' . $role . '_' . $timeframe;
		}
		// Transient keys max 172 chars; ours are well within that.
		return $key;
	}

	/**
	 * Check whether the current request is within the rate limit.
	 * Returns true if allowed, false if blocked.
	 *
	 * @param int $user_id  0 for visitors.
	 * @return array { allowed: bool, message: string, use_fallback: bool }
	 */
	public static function check( $user_id ) {
		if ( ! self::is_enabled() ) {
			return array( 'allowed' => true );
		}

		// Rate limiting is a Pro feature - bypass if Pro is not active
		$plugin = \Askany\Plugin::get_instance();
		if ( ! $plugin->is_pro_active() ) {
			return array( 'allowed' => true );
		}

		$role          = self::get_user_role( $user_id );
		$role_settings = self::get_role_settings();
		$config        = isset( $role_settings[ $role ] ) ? $role_settings[ $role ] : $role_settings['visitor'];

		$limit     = intval( $config['limit'] );
		$timeframe = sanitize_key( $config['timeframe'] );
		$message   = isset( $config['custom_message'] ) ? $config['custom_message'] : __( 'Rate limit exceeded. Please try again later.', 'askany' );
		$use_fb    = ! empty( $config['use_fallback'] );

		// Unlimited
		if ( $limit <= 0 ) {
			return array( 'allowed' => true );
		}

		$ttl = isset( self::TIMEFRAMES[ $timeframe ] ) ? self::TIMEFRAMES[ $timeframe ] : 86400;
		$key = self::transient_key( $user_id, $role, $timeframe );

		$count = (int) get_transient( $key );

		if ( $count >= $limit ) {
			return array(
				'allowed'      => false,
				'message'      => $message,
				'use_fallback' => $use_fb,
			);
		}

		// Increment counter
		if ( $count === 0 ) {
			set_transient( $key, 1, $ttl );
		} else {
			set_transient( $key, $count + 1, $ttl );
		}

		return array( 'allowed' => true );
	}

	/**
	 * Save rate limit settings.
	 *
	 * @param bool  $enabled
	 * @param array $roles
	 */
	public static function save_settings( $enabled, $roles ) {
		update_option( 'askany_rate_limit_enabled', (bool) $enabled );

		$sanitized = array();
		$valid_limits     = array( 1, 3, 5, 10, 15, 20, 50, 100, 0 ); // 0 = unlimited
		$valid_timeframes = array_keys( self::TIMEFRAMES );
		$all_roles        = array_merge( self::ROLES, array( 'visitor' ) );

		foreach ( $all_roles as $role ) {
			if ( ! isset( $roles[ $role ] ) ) {
				continue;
			}
			$r = $roles[ $role ];

			$limit     = intval( $r['limit'] ?? 100 );
			$timeframe = sanitize_key( $r['timeframe'] ?? 'per_day' );
			$message   = sanitize_textarea_field( $r['custom_message'] ?? '' );
			$use_fb    = ! empty( $r['use_fallback'] );

			if ( ! in_array( $limit, $valid_limits, true ) ) {
				$limit = 100;
			}
			if ( ! in_array( $timeframe, $valid_timeframes, true ) ) {
				$timeframe = 'per_day';
			}
			if ( empty( $message ) ) {
				$message = __( 'Rate limit exceeded. Please try again later.', 'askany' );
			}

			$sanitized[ $role ] = array(
				'limit'          => $limit,
				'timeframe'      => $timeframe,
				'custom_message' => $message,
				'use_fallback'   => $use_fb,
			);
		}

		update_option( 'askany_rate_limit_roles', $sanitized );
	}
}
