<?php
/**
 * Controller for handling guest order linking verification.
 *
 * @package Awesomemotive\Rewardswp\Controllers
 * @since 1.2.0
 */

namespace Awesomemotive\Rewardswp\Controllers;

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

use Awesomemotive\Rewardswp\Factories\MailerFactory;
use Awesomemotive\Rewardswp\Factories\ViewsFactory;
use Awesomemotive\Rewardswp\Interfaces\ControllerInterface;
use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use Awesomemotive\Rewardswp\Services\OrderLinkingVerificationService;
use Awesomemotive\Rewardswp\Views\OrderLinkingVerificationView;

/**
 * Class OrderLinkingVerificationController
 *
 * Handles the email verification flow for linking guest orders
 * to newly created user accounts.
 *
 * @since 1.2.0
 */
class OrderLinkingVerificationController implements ControllerInterface {

	/**
	 * AJAX nonce action for resending verification email.
	 *
	 * @var string
	 */
	const AJAX_NONCE = 'rewardswp_resend_order_verification';

	/**
	 * Query var for user ID in verification URL.
	 *
	 * @var string
	 */
	const QUERY_VAR_UID = 'rewardswp_verify_uid';

	/**
	 * Query var for token in verification URL.
	 *
	 * @var string
	 */
	const QUERY_VAR_TOKEN = 'rewardswp_verify_token';

	/**
	 * Order linking verification service instance.
	 *
	 * @var OrderLinkingVerificationService
	 */
	private OrderLinkingVerificationService $verification_service;

	/**
	 * Mailer factory instance.
	 *
	 * @var MailerFactory
	 */
	private MailerFactory $mailer_factory;

	/**
	 * Integration instance.
	 *
	 * @var IntegrationInterface
	 */
	private IntegrationInterface $integration;

	/**
	 * Views factory instance.
	 *
	 * @var ViewsFactory
	 */
	private ViewsFactory $views_factory;

	/**
	 * Constructor.
	 *
	 * @param OrderLinkingVerificationService $verification_service Verification service instance.
	 * @param MailerFactory                   $mailer_factory       Mailer factory instance.
	 * @param IntegrationInterface            $integration          Integration instance.
	 * @param ViewsFactory                    $views_factory        Views factory instance.
	 */
	public function __construct(
		OrderLinkingVerificationService $verification_service,
		MailerFactory $mailer_factory,
		IntegrationInterface $integration,
		ViewsFactory $views_factory
	) {
		$this->verification_service = $verification_service;
		$this->mailer_factory       = $mailer_factory;
		$this->integration          = $integration;
		$this->views_factory        = $views_factory;
	}

	/**
	 * Initialize controller hooks.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function init(): void {
		// Skip if integration is not active.
		if ( ! $this->integration->is_active() ) {
			return;
		}

		// Register rewrite rules - call directly if init already fired, otherwise hook.
		if ( did_action( 'init' ) ) {
			$this->add_rewrite_rules();
		} else {
			add_action( 'init', [ $this, 'add_rewrite_rules' ] );
		}

		// Register query vars.
		add_filter( 'query_vars', [ $this, 'register_query_vars' ] );

		// Handle verification requests.
		add_action( 'template_redirect', [ $this, 'handle_verification_request' ] );

		// Hook into user registration to check for guest orders.
		add_action( 'user_register', [ $this, 'maybe_send_verification_email' ], 100 );

		// Also hook into WooCommerce customer creation specifically.
		add_action( 'woocommerce_created_customer', [ $this, 'maybe_send_verification_email' ], 100 );

		// AJAX handlers for resending verification email.
		add_action( 'wp_ajax_rewardswp_resend_order_verification', [ $this, 'ajax_resend_verification' ] );
	}

	/**
	 * Add rewrite rules for verification URLs.
	 *
	 * Creates a clean URL structure: /rewardswp-verify-orders/{user_id}/{token}
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function add_rewrite_rules(): void {
		add_rewrite_rule(
			'^' . OrderLinkingVerificationService::VERIFICATION_ENDPOINT . '/([0-9]+)/([a-zA-Z0-9]+)/?$',
			'index.php?' . self::QUERY_VAR_UID . '=$matches[1]&' . self::QUERY_VAR_TOKEN . '=$matches[2]',
			'top'
		);

		// Flush rewrite rules if flagged (after activation or upgrade).
		if ( get_transient( 'rewardswp_flush_rewrite_rules' ) ) {
			flush_rewrite_rules();
			delete_transient( 'rewardswp_flush_rewrite_rules' );
		}
	}

	/**
	 * Register custom query vars for verification.
	 *
	 * @since 1.2.0
	 *
	 * @param array $vars Existing query vars.
	 *
	 * @return array Modified query vars.
	 */
	public function register_query_vars( array $vars ): array {
		$vars[] = self::QUERY_VAR_UID;
		$vars[] = self::QUERY_VAR_TOKEN;
		return $vars;
	}

	/**
	 * Handle verification request from email link.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function handle_verification_request(): void {
		// Get parameters from query vars (set by rewrite rule).
		$user_id = absint( get_query_var( self::QUERY_VAR_UID, 0 ) );
		$token   = sanitize_text_field( get_query_var( self::QUERY_VAR_TOKEN, '' ) );

		// Check if we're on the verification URL.
		if ( empty( $user_id ) || empty( $token ) ) {
			return; // Not a verification request.
		}

		// Require user to be logged in.
		if ( ! is_user_logged_in() ) {
			// Store return URL and redirect to login.
			$return_url = $this->verification_service->get_verification_url( $user_id, $token );

			wp_safe_redirect( wp_login_url( $return_url ) );
			exit;
		}

		// Verify the logged-in user matches the verification user.
		if ( get_current_user_id() !== $user_id ) {
			$this->render_error_page(
				__( 'Please log in with the account that received this verification email.', 'rewardswp' ),
				'wrong_user'
			);
			return;
		}

		// Validate the token.
		$validation = $this->verification_service->validate_token( $user_id, $token );

		if ( ! $validation['valid'] ) {
			$show_resend = in_array( $validation['error'], [ 'expired_token', 'invalid_token' ], true );
			$this->render_error_page( $validation['message'], $validation['error'], $show_resend );
			return;
		}

		// Complete verification and link orders.
		$linked_count = $this->verification_service->complete_verification( $user_id );

		// Render success page.
		$this->render_success_page( $linked_count );
	}

	/**
	 * Check for guest orders and send verification email on user registration.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return void
	 */
	public function maybe_send_verification_email( int $user_id ): void {
		// Check if we should initiate verification.
		if ( ! $this->verification_service->should_initiate_verification( $user_id ) ) {
			return;
		}

		// Prevent duplicate emails within a short time window (e.g., when both
		// user_register and woocommerce_created_customer hooks fire).
		if ( $this->is_verification_email_recently_sent( $user_id ) ) {
			return;
		}

		/**
		 * Filter whether to send order linking verification email.
		 *
		 * @since 1.2.0
		 *
		 * @param bool $should_send Whether to send the verification email.
		 * @param int  $user_id     The WordPress user ID.
		 */
		if ( ! apply_filters( 'rewardswp_should_send_order_linking_verification', true, $user_id ) ) {
			return;
		}

		$this->send_verification_email( $user_id );
	}

	/**
	 * Send the verification email to a user.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return bool True if email was sent successfully.
	 */
	public function send_verification_email( int $user_id ): bool {
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			return false;
		}

		// Generate new token (invalidates any previous token).
		$token            = $this->verification_service->generate_verification_token( $user_id );
		$verification_url = $this->verification_service->get_verification_url( $user_id, $token );
		$order_count      = $this->verification_service->count_guest_orders_for_email( $user->user_email );
		$expiration_hours = $this->verification_service->get_token_expiration_hours();

		// Get the mailer.
		$mailer = $this->mailer_factory->get_mailer( 'order_linking_verification' );
		if ( ! $mailer ) {
			return false;
		}

		// Prepare and send the email.
		$sent = $mailer
			->set_recipient( $user->user_email )
			->set_data(
				[
					'user_name'        => $user->display_name ?: $user->user_email,
					'verification_url' => $verification_url,
					'order_count'      => $order_count,
					'expiration_hours' => $expiration_hours,
					'site_url'         => home_url(),
				]
			)
			->send();

		// Mark email as sent to prevent duplicates within short time window.
		if ( $sent ) {
			$this->mark_verification_email_sent( $user_id );
		}

		return $sent;
	}

	/**
	 * AJAX handler for resending verification email.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function ajax_resend_verification(): void {
		check_ajax_referer( self::AJAX_NONCE );

		// Must be logged in.
		if ( ! is_user_logged_in() ) {
			wp_send_json_error( [ 'message' => __( 'Please log in to resend the verification email.', 'rewardswp' ) ] );
		}

		$user_id = get_current_user_id();
		$user    = get_userdata( $user_id );

		// Check if user has guest orders to link.
		$order_count = $this->verification_service->count_guest_orders_for_email( $user->user_email );
		if ( $order_count < 1 ) {
			wp_send_json_error( [ 'message' => __( 'No guest orders found for your email address.', 'rewardswp' ) ] );
		}

		// Check if already verified.
		if ( $this->verification_service->is_already_verified( $user_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Your orders have already been linked to your account.', 'rewardswp' ) ] );
		}

		// Send verification email.
		$sent = $this->send_verification_email( $user_id );

		if ( $sent ) {
			wp_send_json_success( [ 'message' => __( 'Verification email sent! Please check your inbox.', 'rewardswp' ) ] );
		} else {
			wp_send_json_error( [ 'message' => __( 'Failed to send verification email. Please try again later.', 'rewardswp' ) ] );
		}
	}

	/**
	 * Render the success page after order linking.
	 *
	 * @since 1.2.0
	 *
	 * @param int $linked_count Number of orders linked.
	 *
	 * @return void
	 */
	private function render_success_page( int $linked_count ): void {
		// Get redirect URL (default to WooCommerce orders page or home).
		$redirect_url = home_url();

		if ( function_exists( 'wc_get_account_endpoint_url' ) ) {
			$redirect_url = wc_get_account_endpoint_url( 'orders' );
		}

		/**
		 * Filter the redirect URL after successful order linking.
		 *
		 * @since 1.2.0
		 *
		 * @param string $redirect_url The redirect URL.
		 * @param int    $linked_count Number of orders linked.
		 */
		$redirect_url = apply_filters( 'rewardswp_order_linking_success_redirect', $redirect_url, $linked_count );

		$view = $this->views_factory->get_view( OrderLinkingVerificationView::class );
		$view->render(
			[
				'title'       => __( 'Orders Linked Successfully!', 'rewardswp' ),
				'message'     => sprintf(
					/* translators: %d: Number of orders linked */
					_n(
						'%d order has been linked to your account.',
						'%d orders have been linked to your account.',
						$linked_count,
						'rewardswp'
					),
					$linked_count
				),
				'button_url'  => $redirect_url,
				'button_text' => __( 'View My Orders', 'rewardswp' ),
				'type'        => 'success',
				'show_resend' => false,
			]
		);
		exit;
	}

	/**
	 * Render an error page.
	 *
	 * @since 1.2.0
	 *
	 * @param string $message     Error message to display.
	 * @param string $error_type  Type of error (for styling/logic).
	 * @param bool   $show_resend Whether to show resend button.
	 *
	 * @return void
	 */
	private function render_error_page( string $message, string $error_type = 'generic', bool $show_resend = false ): void {
		$view = $this->views_factory->get_view( OrderLinkingVerificationView::class );
		$view->render(
			[
				'title'       => __( 'Verification Failed', 'rewardswp' ),
				'message'     => $message,
				'button_url'  => home_url(),
				'button_text' => __( 'Return to Homepage', 'rewardswp' ),
				'type'        => 'error',
				'show_resend' => $show_resend,
			]
		);
		exit;
	}

	/**
	 * Check if a verification email was recently sent to prevent duplicates.
	 *
	 * When both user_register and woocommerce_created_customer hooks fire
	 * (e.g., during WooCommerce registration), this prevents sending duplicate
	 * emails and invalidating the first token.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return bool True if email was sent recently (within 10 seconds).
	 */
	private function is_verification_email_recently_sent( int $user_id ): bool {
		$transient_key = 'rewardswp_verification_sent_' . $user_id;
		return false !== get_transient( $transient_key );
	}

	/**
	 * Mark that a verification email was sent to prevent duplicates.
	 *
	 * @since 1.2.0
	 *
	 * @param int $user_id The WordPress user ID.
	 *
	 * @return void
	 */
	private function mark_verification_email_sent( int $user_id ): void {
		$transient_key = 'rewardswp_verification_sent_' . $user_id;
		// Set transient for 10 seconds to prevent duplicate sends.
		set_transient( $transient_key, true, 10 );
	}
}
