<?php
/**
 * This file is part of the Wisewand WordPress plugin.
 *
 * @package Wisewand
 * @since 1.0.0
 * @license GPL-2.0-or-later
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
 */
namespace Wisewand\Security;

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use WP_REST_Request;
use WP_Error;
use Wisewand\Utils\Options;
use Wisewand\Logging\Logger;

// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name stored in class property from $wpdb->prefix in constructor is safe to interpolate

class Security_Manager {

	private $options;
	private $logger;
	private $rate_limit_table;

	public function __construct( Options $options, Logger $logger ) {
		$this->options = $options;
		$this->logger  = $logger;

		global $wpdb;
		$this->rate_limit_table = $wpdb->prefix . 'wisewand_rate_limits';
	}

	public function init_security_checks() {
		// Clean up expired rate limit entries hourly
		if ( ! wp_next_scheduled( 'wisewand_cleanup_rate_limits' ) ) {
			wp_schedule_event( time(), 'hourly', 'wisewand_cleanup_rate_limits' );
		}

		add_action( 'wisewand_cleanup_rate_limits', array( $this, 'cleanup_expired_rate_limits' ) );
	}

	public function authenticate_request( WP_REST_Request $request ) {
		$security_settings = $this->options->get_security_settings();

		// Check IP whitelist first
		if ( ! $this->check_ip_whitelist( $request, $security_settings ) ) {
			return new WP_Error(
				'wisewand_ip_blocked',
				__( 'Access denied: IP address not whitelisted.', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 403 )
			);
		}

		// Verify HMAC signature
		$hmac_result = $this->verify_hmac_signature( $request, $security_settings );
		if ( is_wp_error( $hmac_result ) ) {
			return $hmac_result;
		}

		// Check timestamp to prevent replay attacks
		$timestamp_result = $this->verify_timestamp( $request, $security_settings );
		if ( is_wp_error( $timestamp_result ) ) {
			return $timestamp_result;
		}

		return true;
	}

	public function check_rate_limit( WP_REST_Request $request ) {
		$security_settings = $this->options->get_security_settings();

		if ( ! $security_settings['rate_limit_enabled'] ) {
			return true;
		}

		$ip_address     = $this->get_client_ip( $request );
		$endpoint       = $request->get_route();
		$window_seconds = $security_settings['rate_limit_window'];
		$max_requests   = $security_settings['rate_limit_requests'];

		global $wpdb;

		// Calculate window boundaries
		$window_start = gmdate( 'Y-m-d H:i:s', time() - $window_seconds );
		$window_end   = gmdate( 'Y-m-d H:i:s' );

		// Check current request count in window
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom plugin table query for rate limit check
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$current_count = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT requests_count FROM {$this->rate_limit_table}
             WHERE ip_address = %s AND endpoint = %s
             AND window_start <= %s AND window_end > %s",
				$ip_address,
				$endpoint,
				$window_end,
				$window_start
			)
		);

		if ( $current_count === null ) {
			// First request in this window
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Custom plugin table insert for new rate limit entry
			$wpdb->insert(
				$this->rate_limit_table,
				array(
					'ip_address'     => $ip_address,
					'endpoint'       => $endpoint,
					'requests_count' => 1,
					'window_start'   => $window_start,
					'window_end'     => $window_end,
				)
			);
			return true;
		}

		if ( $current_count >= $max_requests ) {
			$this->logger->warning(
				'Rate limit exceeded',
				array(
					'ip_address'    => $ip_address,
					'endpoint'      => $endpoint,
					'current_count' => $current_count,
					'max_requests'  => $max_requests,
				)
			);

			return new WP_Error(
				'wisewand_rate_limit_exceeded',
				sprintf(
					/* translators: 1: Maximum number of requests allowed, 2: Time window in seconds */
					__( 'Rate limit exceeded. Maximum %1$d requests per %2$d seconds allowed.', 'wisewand-ai-content-writer-seo-content-generator' ),
					$max_requests,
					$window_seconds
				),
				array(
					'status'      => 429,
					'retry_after' => $window_seconds,
				)
			);
		}

		// Increment request count
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom plugin table update for incrementing rate limit counter
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$wpdb->query(
			$wpdb->prepare(
				"UPDATE {$this->rate_limit_table}
             SET requests_count = requests_count + 1
             WHERE ip_address = %s AND endpoint = %s
             AND window_start <= %s AND window_end > %s",
				$ip_address,
				$endpoint,
				$window_end,
				$window_start
			)
		);

		return true;
	}

	private function check_ip_whitelist( WP_REST_Request $request, array $security_settings ) {
		$ip_whitelist = trim( $security_settings['ip_whitelist'] );

		// If no whitelist is configured, allow all IPs
		if ( empty( $ip_whitelist ) ) {
			return true;
		}

		$client_ip   = $this->get_client_ip( $request );
		$allowed_ips = array_map( 'trim', explode( "\n", $ip_whitelist ) );

		foreach ( $allowed_ips as $allowed_ip ) {
			if ( empty( $allowed_ip ) ) {
				continue;
			}

			// Check for CIDR notation
			if ( strpos( $allowed_ip, '/' ) !== false ) {
				if ( $this->ip_in_range( $client_ip, $allowed_ip ) ) {
					return true;
				}
			} else {
				// Exact IP match
				if ( $client_ip === $allowed_ip ) {
					return true;
				}
			}
		}

		$this->logger->warning(
			'IP address blocked by whitelist',
			array(
				'client_ip' => $client_ip,
				'whitelist' => $ip_whitelist,
			)
		);

		return false;
	}

	private function verify_hmac_signature( WP_REST_Request $request, array $security_settings ) {
		$signature_header = $request->get_header( 'X-Wisewand-Signature' );
		$secret           = $security_settings['hmac_secret'];

		if ( empty( $signature_header ) ) {
			return new WP_Error(
				'wisewand_missing_signature',
				__( 'Missing X-Wisewand-Signature header.', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 401 )
			);
		}

		if ( empty( $secret ) ) {
			$this->logger->error( 'HMAC secret not configured' );
			return new WP_Error(
				'wisewand_configuration_error',
				__( 'HMAC secret not configured.', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 500 )
			);
		}

		// Parse signature header (format: sha256=<hex>)
		if ( ! preg_match( '/^sha256=([a-f0-9]+)$/', $signature_header, $matches ) ) {
			return new WP_Error(
				'wisewand_invalid_signature_format',
				__( 'Invalid signature format. Expected: sha256=<hex>', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 401 )
			);
		}

		$provided_signature = $matches[1];

		// Get raw body for signature verification
		$raw_body           = $request->get_body();
		$expected_signature = hash_hmac( 'sha256', $raw_body, $secret );

		// Use hash_equals to prevent timing attacks
		if ( ! hash_equals( $expected_signature, $provided_signature ) ) {
			$this->logger->warning(
				'HMAC signature verification failed',
				array(
					'provided_signature' => substr( $provided_signature, 0, 8 ) . '...',
					'expected_signature' => substr( $expected_signature, 0, 8 ) . '...',
				)
			);

			return new WP_Error(
				'wisewand_invalid_signature',
				__( 'Invalid HMAC signature.', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 401 )
			);
		}

		return true;
	}

	private function verify_timestamp( WP_REST_Request $request, array $security_settings ) {
		$timestamp_header = $request->get_header( 'X-Wisewand-Timestamp' );
		$allowed_skew     = $security_settings['timestamp_skew'];

		if ( empty( $timestamp_header ) ) {
			return new WP_Error(
				'wisewand_missing_timestamp',
				__( 'Missing X-Wisewand-Timestamp header.', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 401 )
			);
		}

		if ( ! is_numeric( $timestamp_header ) ) {
			return new WP_Error(
				'wisewand_invalid_timestamp',
				__( 'Invalid timestamp format.', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 401 )
			);
		}

		$request_timestamp = (int) $timestamp_header;
		$current_timestamp = time();
		$time_diff         = abs( $current_timestamp - $request_timestamp );

		if ( $time_diff > $allowed_skew ) {
			$this->logger->warning(
				'Timestamp skew exceeded',
				array(
					'request_timestamp' => $request_timestamp,
					'current_timestamp' => $current_timestamp,
					'time_diff'         => $time_diff,
					'allowed_skew'      => $allowed_skew,
				)
			);

			return new WP_Error(
				'wisewand_timestamp_skew',
				sprintf(
					/* translators: %d: Maximum allowed time difference in seconds */
					__( 'Request timestamp outside allowed window. Maximum skew: %d seconds.', 'wisewand-ai-content-writer-seo-content-generator' ),
					$allowed_skew
				),
				array( 'status' => 401 )
			);
		}

		return true;
	}

	private function get_client_ip( WP_REST_Request $request ) {
		// Try various headers for IP detection (Cloudflare, load balancers, etc.)
		$ip_headers = array(
			'HTTP_CF_CONNECTING_IP',
			'HTTP_X_FORWARDED_FOR',
			'HTTP_X_FORWARDED',
			'HTTP_FORWARDED_FOR',
			'HTTP_FORWARDED',
			'REMOTE_ADDR',
		);

		foreach ( $ip_headers as $header ) {
			if ( ! empty( $_SERVER[ $header ] ) ) {
				$ip_list = explode( ',', sanitize_text_field( wp_unslash( $_SERVER[ $header ] ) ) );
				foreach ( $ip_list as $ip ) {
					$ip = trim( $ip );
					if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
						return $ip;
					}
				}
			}
		}

		return isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '127.0.0.1';
	}

	private function ip_in_range( $ip, $cidr ) {
		list($subnet, $mask) = explode( '/', $cidr );

		if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) || ! filter_var( $subnet, FILTER_VALIDATE_IP ) ) {
			return false;
		}

		$ip_long     = ip2long( $ip );
		$subnet_long = ip2long( $subnet );
		$mask_long   = -1 << ( 32 - (int) $mask );

		return ( $ip_long & $mask_long ) === ( $subnet_long & $mask_long );
	}

	public function cleanup_expired_rate_limits() {
		global $wpdb;

		// Remove rate limit entries older than 24 hours
		$expired_time = gmdate( 'Y-m-d H:i:s', time() - DAY_IN_SECONDS );

        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom plugin table cleanup for expired rate limit entries
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$deleted = $wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$this->rate_limit_table} WHERE window_end < %s",
				$expired_time
			)
		);

		if ( $deleted > 0 ) {
			$this->logger->debug(
				'Cleaned up expired rate limit entries',
				array(
					'deleted_count' => $deleted,
				)
			);
		}
	}

	public function generate_hmac_secret() {
		$secret = wp_generate_password( 64, false );
		$this->options->set( 'wisewand_hmac_secret', $secret );

		$this->logger->info( 'New HMAC secret generated' );

		return $secret;
	}

	public function get_masked_secret() {
		$secret = $this->options->get( 'wisewand_hmac_secret', '' );
		if ( empty( $secret ) ) {
			return '';
		}

		return substr( $secret, 0, 8 ) . str_repeat( '*', max( 0, strlen( $secret ) - 8 ) );
	}

	public function test_signature( $test_payload ) {
		$secret = $this->options->get( 'wisewand_hmac_secret', '' );

		if ( empty( $secret ) ) {
			return new WP_Error(
				'wisewand_no_secret',
				__( 'HMAC secret not configured.', 'wisewand-ai-content-writer-seo-content-generator' )
			);
		}

		$body      = wp_json_encode( $test_payload );
		$signature = 'sha256=' . hash_hmac( 'sha256', $body, $secret );
		$timestamp = time();

		return array(
			'test_payload' => $test_payload,
			'signature'    => $signature,
			'timestamp'    => $timestamp,
			'headers'      => array(
				'X-Wisewand-Signature' => $signature,
				'X-Wisewand-Timestamp' => $timestamp,
				'Content-Type'         => 'application/json',
			),
		);
	}
}
