<?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\API;

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

use Wisewand\Utils\Options;
use Wisewand\Logging\Logger;
use Wisewand\Calendar\Calendar_Entry_Manager;

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Direct database calls required for webhook processing and custom plugin table queries

/**
 * Webhook Receiver for Wisewand content generation callbacks
 * Eliminates polling by receiving instant notifications when content is ready
 */
class Webhook_Receiver {

	private $options;
	private $logger;
	private $entry_manager;

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

	/**
	 * Register webhook REST endpoint
	 */
	public function register_routes() {
		register_rest_route(
			'wisewand/v1',
			'/webhook/content-ready',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'handle_content_ready' ),
				'permission_callback' => array( $this, 'verify_webhook_signature' ),
			)
		);
	}

	/**
	 * Verify webhook comes from Wisewand with HMAC signature validation
	 */
	public function verify_webhook_signature( $request ) {
		$user_agent = $request->get_header( 'user-agent' );

		// Safely get and validate IP address
		$ip_address = $this->get_client_ip();

		// Log all webhook attempts with sanitized data
		$this->logger->info(
			'Webhook received',
			array(
				'user_agent' => sanitize_text_field( $user_agent ),
				'ip'         => $ip_address,
			)
		);

		// Get HMAC signature from header
		$signature      = $request->get_header( 'X-Wisewand-Webhook-Signature' );
		$webhook_secret = $this->options->get( 'wisewand_webhook_secret' );

		// If webhook secret is configured, require HMAC validation
		if ( ! empty( $webhook_secret ) ) {
			if ( empty( $signature ) ) {
				$this->logger->warning(
					'Webhook rejected - missing signature',
					array(
						'ip' => $ip_address,
					)
				);
				return new \WP_Error( 'unauthorized', __( 'Missing webhook signature', 'wisewand-ai-content-writer-seo-content-generator' ), array( 'status' => 403 ) );
			}

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

			// Use timing-safe comparison
			if ( ! hash_equals( $expected_signature, $signature ) ) {
				$this->logger->warning(
					'Webhook rejected - invalid signature',
					array(
						'ip'                 => $ip_address,
						'received_signature' => substr( $signature, 0, 20 ) . '...',
					)
				);
				return new \WP_Error( 'unauthorized', __( 'Invalid webhook signature', 'wisewand-ai-content-writer-seo-content-generator' ), array( 'status' => 403 ) );
			}

			$this->logger->debug( 'Webhook signature validated successfully' );
		} else {
			// Webhook secret is required for security - reject if not configured
			$this->logger->error(
				'Webhook rejected - no webhook secret configured',
				array(
					'ip' => $ip_address,
				)
			);
			return new \WP_Error(
				'no_secret_configured',
				__( 'Webhook authentication requires a configured secret. Please configure webhook secret in plugin settings.', 'wisewand-ai-content-writer-seo-content-generator' ),
				array( 'status' => 403 )
			);
		}

		return true;
	}

	/**
	 * Safely get client IP address with validation
	 *
	 * @return string Validated IP address or 'unknown'
	 */
	private function get_client_ip() {
		// Check common proxy headers first
		$ip_keys = array(
			'HTTP_CF_CONNECTING_IP',     // Cloudflare
			'HTTP_X_FORWARDED_FOR',      // Standard proxy header
			'HTTP_X_REAL_IP',            // Nginx proxy
			'HTTP_X_FORWARDED',
			'HTTP_FORWARDED_FOR',
			'HTTP_FORWARDED',
			'REMOTE_ADDR',               // Fallback to direct connection
		);

		foreach ( $ip_keys as $key ) {
			if ( isset( $_SERVER[ $key ] ) && ! empty( $_SERVER[ $key ] ) ) {
				// Handle comma-separated list (X-Forwarded-For can contain multiple IPs)
				$server_value = sanitize_text_field( wp_unslash( $_SERVER[ $key ] ) );
				$ip_list      = explode( ',', $server_value );
				foreach ( $ip_list as $ip ) {
					$ip = trim( $ip );
					// Validate IP address and exclude private/reserved ranges for security
					$validated_ip = filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE );
					if ( $validated_ip !== false ) {
						return $validated_ip;
					}
				}
			}
		}

		// Fallback: try to get any valid IP (including private ranges for local dev)
		if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
			$remote_addr  = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
			$validated_ip = filter_var( $remote_addr, FILTER_VALIDATE_IP );
			if ( $validated_ip !== false ) {
				return $validated_ip;
			}
		}

		return 'unknown';
	}

	/**
	 * Handle webhook callback when content is ready
	 */
	public function handle_content_ready( $request ) {
		try {
			$body = $request->get_json_params();

			$this->logger->info(
				'Webhook content ready received',
				array(
					'body' => $body,
				)
			);

			$article_id = $body['id'] ?? $body['article_id'] ?? null;
			$status     = $body['status'] ?? 'unknown';

			if ( ! $article_id ) {
				throw new \Exception( 'No article ID in webhook payload' );
			}

			// Initialize entry manager
			if ( ! $this->entry_manager ) {
				if ( ! class_exists( 'Wisewand\\Calendar\\Calendar_Entry_Manager' ) ) {
					require_once WISEWAND_PLUGIN_DIR . 'includes/Calendar/Calendar_Entry_Manager.php';
				}
				if ( ! class_exists( 'Wisewand\\Utils\\Wisewand_Payload_Builder' ) ) {
					require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Wisewand_Payload_Builder.php';
				}
				$payload_builder     = new \Wisewand\Utils\Wisewand_Payload_Builder( $this->options, $this->logger );
				$this->entry_manager = new \Wisewand\Calendar\Calendar_Entry_Manager( $this->options, $this->logger, $payload_builder );
			}

			// Find entry by wisewand_article_id
			global $wpdb;
			$table_name = $wpdb->prefix . 'wisewand_editorial_calendar';

			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$entry_id = $wpdb->get_var(
				$wpdb->prepare(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table name is derived from $wpdb->prefix and is safe to interpolate, custom plugin table query
					"SELECT id FROM {$table_name} WHERE wisewand_article_id = %s",
					$article_id
				)
			);

			if ( ! $entry_id ) {
				$this->logger->warning(
					'Webhook received for unknown article',
					array(
						'article_id' => $article_id,
					)
				);
				return new \WP_REST_Response( array( 'message' => 'Article not found in calendar' ), 404 );
			}

			// Update entry based on status
			if ( 'success' === $status ) {
				$this->entry_manager->update_entry(
					$entry_id,
					array(
						'wisewand_status'         => 'ready',
						'wordpress_status'        => 'scheduled', // Ensure ready for import
						'generation_completed_at' => current_time( 'mysql' ),
					)
				);

				$this->logger->info(
					'Webhook: Article ready',
					array(
						'entry_id'   => $entry_id,
						'article_id' => $article_id,
					)
				);

				// Trigger immediate import if publication date is today or past
				$entry            = $this->entry_manager->get_entry( $entry_id );
				$publication_date = $entry['publication_date'] ?? '';

				if ( strtotime( $publication_date ) <= strtotime( gmdate( 'Y-m-d' ) ) ) {
					// Schedule immediate import via async action
					wp_schedule_single_event( time() + 10, 'wisewand_import_ready_content', array( $entry_id ) );

					$this->logger->info(
						'Scheduled immediate import',
						array(
							'entry_id' => $entry_id,
						)
					);
				}
			} elseif ( $status === 'error' ) {
				$error_message = $body['error'] ?? $body['status_detail'] ?? 'Generation failed';

				$this->entry_manager->update_entry(
					$entry_id,
					array(
						'wisewand_status' => 'failed',
						'wisewand_error'  => $error_message,
					)
				);

				$this->logger->error(
					'Webhook: Article failed',
					array(
						'entry_id'   => $entry_id,
						'article_id' => $article_id,
						'error'      => $error_message,
					)
				);
			}

			return new \WP_REST_Response( array( 'message' => 'Webhook processed successfully' ), 200 );

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Webhook processing failed',
				array(
					'error' => $e->getMessage(),
				)
			);

			return new \WP_REST_Response( array( 'error' => $e->getMessage() ), 500 );
		}
	}

	/**
	 * Get webhook URL for this site
	 */
	public static function get_webhook_url() {
		return rest_url( 'wisewand/v1/webhook/content-ready' );
	}

	/**
	 * Get webhook ID stored in options
	 */
	public static function get_webhook_id() {
		return get_option( 'wisewand_webhook_id', '' );
	}

	/**
	 * Store webhook ID
	 */
	public static function set_webhook_id( $webhook_id ) {
		update_option( 'wisewand_webhook_id', $webhook_id );
	}
}
