<?php
/**
 * Form handler and REST API endpoints.
 *
 * Registers REST API routes and handles subscription forms
 * and unsubscribe requests.
 *
 * @package Waitlytics
 */

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

/**
 * Class WTLY_Form_Handler
 */
class WTLY_Form_Handler {

	/**
	 * REST API namespace.
	 *
	 * @var string
	 */
	const API_NAMESPACE = 'wtly/v1';

	/**
	 * Initialize hooks.
	 */
	public function init() {
		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
		add_action( 'init', array( $this, 'handle_unsubscribe' ) );
	}

	/**
	 * Register REST API routes.
	 */
	public function register_routes() {
		// POST /wtly/v1/subscribe — Public, nonce protected, rate limited.
		register_rest_route( self::API_NAMESPACE, '/subscribe', array(
			'methods'             => 'POST',
			'callback'            => array( $this, 'rest_subscribe' ),
			'permission_callback' => '__return_true',
			'args'                => array(
				'email'        => array(
					'required'          => true,
					'type'              => 'string',
					'sanitize_callback' => 'sanitize_email',
					'validate_callback' => function ( $email ) {
						return is_email( $email );
					},
				),
				'product_id'   => array(
					'required'          => true,
					'type'              => 'integer',
					'sanitize_callback' => 'absint',
					'validate_callback' => function ( $id ) {
						return $id > 0;
					},
				),
				'variation_id' => array(
					'required'          => false,
					'type'              => 'integer',
					'default'           => 0,
					'sanitize_callback' => 'absint',
				),
			),
		) );

		// GET /wtly/v1/subscribers — Admin only.
		register_rest_route( self::API_NAMESPACE, '/subscribers', array(
			'methods'             => 'GET',
			'callback'            => array( $this, 'rest_get_subscribers' ),
			'permission_callback' => array( $this, 'check_admin_permission' ),
			'args'                => array(
				'product_id' => array(
					'required'          => false,
					'type'              => 'integer',
					'default'           => 0,
					'sanitize_callback' => 'absint',
				),
				'status'     => array(
					'required'          => false,
					'type'              => 'string',
					'default'           => '',
					'sanitize_callback' => 'sanitize_key',
				),
				'search'     => array(
					'required'          => false,
					'type'              => 'string',
					'default'           => '',
					'sanitize_callback' => 'sanitize_text_field',
				),
				'page'       => array(
					'required'          => false,
					'type'              => 'integer',
					'default'           => 1,
					'sanitize_callback' => 'absint',
				),
				'per_page'   => array(
					'required'          => false,
					'type'              => 'integer',
					'default'           => 20,
					'sanitize_callback' => 'absint',
				),
			),
		) );

		// DELETE /wtly/v1/subscribers/{id} — Admin only.
		register_rest_route( self::API_NAMESPACE, '/subscribers/(?P<id>\d+)', array(
			'methods'             => 'DELETE',
			'callback'            => array( $this, 'rest_delete_subscriber' ),
			'permission_callback' => array( $this, 'check_admin_permission' ),
			'args'                => array(
				'id' => array(
					'required'          => true,
					'type'              => 'integer',
					'sanitize_callback' => 'absint',
					'validate_callback' => function ( $id ) {
						return $id > 0;
					},
				),
			),
		) );

		// GET /wtly/v1/stats — Admin only.
		register_rest_route( self::API_NAMESPACE, '/stats', array(
			'methods'             => 'GET',
			'callback'            => array( $this, 'rest_get_stats' ),
			'permission_callback' => array( $this, 'check_admin_permission' ),
		) );
	}

	/**
	 * Handle subscription via REST API.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function rest_subscribe( $request ) {
		// Verify nonce.
		$nonce = $request->get_header( 'X-WP-Nonce' );
		if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
			return new WP_Error(
				'invalid_nonce',
				__( 'Invalid security token. Please refresh the page and try again.', 'waitlytics' ),
				array( 'status' => 403 )
			);
		}

		// Rate limiting: max 5 subscriptions per IP per minute.
		$rate_check = $this->check_rate_limit();
		if ( is_wp_error( $rate_check ) ) {
			return $rate_check;
		}

		$email        = $request->get_param( 'email' );
		$product_id   = $request->get_param( 'product_id' );
		$variation_id = $request->get_param( 'variation_id' );

		// Validate product exists.
		$product = wc_get_product( $product_id );
		if ( ! $product ) {
			return new WP_Error(
				'invalid_product',
				__( 'This product does not exist.', 'waitlytics' ),
				array( 'status' => 400 )
			);
		}

		// Check guest subscription setting.
		$settings = wtly_get_settings();
		if ( empty( $settings['guest_subscriptions'] ) && ! is_user_logged_in() ) {
			return new WP_Error(
				'login_required',
				__( 'Please log in to subscribe to notifications.', 'waitlytics' ),
				array( 'status' => 403 )
			);
		}

		// Note: the client IP is deliberately NOT stored with the
		// subscription — it is used only transiently for rate limiting.
		$subscriber = new WTLY_Subscriber();
		$result     = $subscriber->add( array(
			'email'        => $email,
			'product_id'   => $product_id,
			'variation_id' => $variation_id,
			'user_id'      => get_current_user_id(),
			'source'       => 'inline',
		) );

		if ( is_wp_error( $result ) ) {
			$status = 'already_subscribed' === $result->get_error_code() ? 200 : 400;
			return new WP_Error(
				$result->get_error_code(),
				$result->get_error_message(),
				array( 'status' => $status )
			);
		}

		// Send admin notification.
		$notification = new WTLY_Notification();
		$notification->send_admin_new_subscriber_email( array(
			'email'      => $email,
			'product_id' => $product_id,
		) );

		/**
		 * Fires after a subscriber is successfully added.
		 *
		 * @param string $email        Subscriber email.
		 * @param string $product_name Product name.
		 */
		do_action( 'wtly_after_subscriber_added', $email, $product->get_name() );

		// Send subscription confirmation to user.
		$email_templates = new WTLY_Email_Templates();
		$sub_row         = $subscriber->get_by_id( $result );
		if ( $sub_row ) {
			$email_templates->send_subscription_confirmed( $sub_row, $product );
		}

		return rest_ensure_response( array(
			'success' => true,
			'message' => __( "You'll be notified when this product is back in stock.", 'waitlytics' ),
		) );
	}

	/**
	 * Get subscribers list via REST API.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response
	 */
	public function rest_get_subscribers( $request ) {
		$subscriber = new WTLY_Subscriber();
		$result     = $subscriber->get_all( array(
			'product_id' => $request->get_param( 'product_id' ),
			'status'     => $request->get_param( 'status' ),
			'search'     => $request->get_param( 'search' ),
			'page'       => $request->get_param( 'page' ),
			'per_page'   => $request->get_param( 'per_page' ),
		) );

		return rest_ensure_response( $result );
	}

	/**
	 * Delete a subscriber via REST API.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function rest_delete_subscriber( $request ) {
		$id         = $request->get_param( 'id' );
		$subscriber = new WTLY_Subscriber();
		$result     = $subscriber->delete( $id );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return rest_ensure_response( array(
			'success' => true,
			'message' => __( 'Subscriber deleted.', 'waitlytics' ),
		) );
	}

	/**
	 * Get dashboard stats via REST API.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response
	 */
	public function rest_get_stats( $request ) {
		$dashboard = new WTLY_Dashboard();
		return rest_ensure_response( $dashboard->get_stats() );
	}

	/**
	 * Handle unsubscribe requests via URL query params.
	 *
	 * URL format: site.com/?wtly_unsubscribe=1&token={hash}&sub_id={id}
	 */
	public function handle_unsubscribe() {
		// One-click unsubscribe links are authenticated by a hashed token verified
		// with hash_equals() below, not by a WP nonce (which is unsuitable for
		// links in emails), so nonce verification does not apply to these reads.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_GET['wtly_unsubscribe'] ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$sub_id = isset( $_GET['sub_id'] ) ? absint( $_GET['sub_id'] ) : 0;
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$token  = isset( $_GET['token'] ) ? sanitize_text_field( wp_unslash( $_GET['token'] ) ) : '';

		if ( 0 === $sub_id || empty( $token ) ) {
			wp_die( esc_html__( 'Invalid unsubscribe link.', 'waitlytics' ) );
		}

		$subscriber = new WTLY_Subscriber();
		$sub        = $subscriber->get_by_id( $sub_id );

		if ( ! $sub ) {
			wp_die( esc_html__( 'Invalid unsubscribe link.', 'waitlytics' ) );
		}

		$expected_token = wp_hash( $sub->id . $sub->email . $sub->product_id );

		if ( ! hash_equals( $expected_token, $token ) ) {
			wp_die( esc_html__( 'Invalid unsubscribe link.', 'waitlytics' ) );
		}

		$subscriber->update_status( $sub_id, 'unsubscribed' );

		wp_safe_redirect( add_query_arg( 'wtly_unsubscribed', '1', home_url() ) );
		exit;
	}

	/**
	 * Check if the current user has admin permission.
	 *
	 * @return bool
	 */
	public function check_admin_permission() {
		return current_user_can( 'manage_woocommerce' );
	}

	/**
	 * Check rate limit for subscription requests.
	 *
	 * Max 5 subscriptions per IP per minute.
	 *
	 * @return true|WP_Error True if allowed, WP_Error if rate limited.
	 */
	private function check_rate_limit() {
		$ip            = $this->get_client_ip();
		$transient_key = 'wtly_rate_' . md5( $ip );
		$count         = (int) get_transient( $transient_key );

		if ( $count >= 5 ) {
			return new WP_Error(
				'rate_limited',
				__( 'Too many requests. Please try again in a minute.', 'waitlytics' ),
				array( 'status' => 429 )
			);
		}

		set_transient( $transient_key, $count + 1, MINUTE_IN_SECONDS );

		return true;
	}

	/**
	 * Get client IP address.
	 *
	 * Used only for transient rate limiting (hashed transient key with a
	 * one-minute TTL); the IP is never persisted to the database.
	 *
	 * @return string
	 */
	private function get_client_ip() {
		$ip = '';

		if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
			$ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_CLIENT_IP'] ) );
		} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
			// May contain multiple IPs; take the first one.
			$ips = explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) );
			$ip  = trim( $ips[0] );
		} 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';
	}
}
