<?php
/**
 * Lead Form Generator — AJAX Handler
 *
 * Exposes three admin-ajax endpoints consumed by the frontend JS widget:
 *   - niwa_ai_lf_start   : begin (or resume) a session, return first/current question.
 *   - niwa_ai_lf_answer  : submit an answer, get next question / completion flag.
 *   - niwa_ai_lf_status  : read the current question (for resume / reload).
 *
 * All endpoints are nonce-protected, per-IP rate-limited, and sanitize inputs.
 *
 * @package Niwa_AI
 * @since   0.12.0
 */

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

class Niwa_AI_Lead_Form_Ajax_Handler {

	const NONCE_ACTION     = 'niwa_ai_lf';
	const NONCE_FIELD      = 'nonce';
	const RATE_LIMIT_MAX   = 60;  // requests
	const RATE_LIMIT_WINDOW = 60; // seconds

	/**
	 * @var Niwa_AI_Lead_Form_Runtime
	 */
	private $runtime;

	/**
	 * Constructor.
	 *
	 * @param Niwa_AI_Lead_Form_Runtime $runtime Runtime.
	 */
	public function __construct( $runtime ) {
		$this->runtime = $runtime;
	}

	/**
	 * Register admin-ajax endpoints.
	 *
	 * @return void
	 */
	public function register_hooks() {
		add_action( 'wp_ajax_niwa_ai_lf_start',        array( $this, 'handle_start' ) );
		add_action( 'wp_ajax_nopriv_niwa_ai_lf_start', array( $this, 'handle_start' ) );

		add_action( 'wp_ajax_niwa_ai_lf_answer',        array( $this, 'handle_answer' ) );
		add_action( 'wp_ajax_nopriv_niwa_ai_lf_answer', array( $this, 'handle_answer' ) );

		add_action( 'wp_ajax_niwa_ai_lf_status',        array( $this, 'handle_status' ) );
		add_action( 'wp_ajax_nopriv_niwa_ai_lf_status', array( $this, 'handle_status' ) );
	}

	/**
	 * Handler: start / resume a session.
	 *
	 * @return void
	 */
	public function handle_start() {
		if ( ! $this->precheck() ) {
			return; // precheck already sent an error response.
		}

		$session_id = $this->read_session_id();
		if ( '' === $session_id ) {
			wp_send_json_error( array( 'message' => __( 'Nedostaje session_id.', 'niwa-ai' ) ), 400 );
			return;
		}

		$mode_override = isset( $_POST['mode'] ) ? sanitize_key( (string) wp_unslash( $_POST['mode'] ) ) : null;
		if ( null !== $mode_override && ! in_array( $mode_override, array( 'chat', 'card' ), true ) ) {
			$mode_override = null;
		}

		$result = $this->runtime->start( $session_id, $mode_override );

		$payload            = is_array( $result ) ? $result : array();
		$payload['nonce']   = wp_create_nonce( self::NONCE_ACTION );

		if ( isset( $payload['error'] ) && '' !== $payload['error'] ) {
			wp_send_json_error( $payload, 500 );
			return;
		}

		wp_send_json_success( $payload );
	}

	/**
	 * Handler: submit an answer.
	 *
	 * @return void
	 */
	public function handle_answer() {
		if ( ! $this->precheck() ) {
			return;
		}

		$session_id  = $this->read_session_id();
		$question_id = isset( $_POST['question_id'] ) ? Niwa_AI_Lead_Form_Schema::sanitize_slug( (string) wp_unslash( $_POST['question_id'] ), '' ) : '';
		$answer      = $this->read_answer();

		if ( '' === $session_id || '' === $question_id ) {
			wp_send_json_error( array( 'message' => __( 'Nedostaju obavezni parametri.', 'niwa-ai' ) ), 400 );
			return;
		}

		$result = $this->runtime->submit_answer( $session_id, $question_id, $answer );

		$payload          = is_array( $result ) ? $result : array();
		$payload['nonce'] = wp_create_nonce( self::NONCE_ACTION );

		if ( isset( $payload['error'] ) && '' !== $payload['error'] ) {
			// Validation error — HTTP 200 (expected interaction), surface via payload.
			wp_send_json_success( $payload );
			return;
		}

		wp_send_json_success( $payload );
	}

	/**
	 * Handler: return current question (resume without mutating state).
	 *
	 * @return void
	 */
	public function handle_status() {
		if ( ! $this->precheck() ) {
			return;
		}

		$session_id = $this->read_session_id();
		if ( '' === $session_id ) {
			wp_send_json_error( array( 'message' => __( 'Nedostaje session_id.', 'niwa-ai' ) ), 400 );
			return;
		}

		// start() on an existing session is non-destructive (just re-reads current pointer).
		$result           = $this->runtime->start( $session_id, null );
		$payload          = is_array( $result ) ? $result : array();
		$payload['nonce'] = wp_create_nonce( self::NONCE_ACTION );

		wp_send_json_success( $payload );
	}

	/* ================================================================
	 * Helpers
	 * ============================================================= */

	/**
	 * Common precondition check: nonce + payload cap + rate limit.
	 *
	 * Sends an error response and returns false on failure.
	 *
	 * @return bool
	 */
	private function precheck() {
		check_ajax_referer( self::NONCE_ACTION, self::NONCE_FIELD );
		if ( isset( $_SERVER['CONTENT_LENGTH'] ) && (int) $_SERVER['CONTENT_LENGTH'] > 32768 ) {
			wp_send_json_error( array( 'message' => __( 'Zahtev je prevelik.', 'niwa-ai' ) ), 413 );
			return false;
		}

		if ( ! $this->rate_limit_ok() ) {
			wp_send_json_error(
				array( 'message' => __( 'Previše zahteva, sačekajte trenutak.', 'niwa-ai' ) ),
				429
			);
			return false;
		}

		return true;
	}

	/**
	 * Atomic global, IP and verified-session fixed-window limiter.
	 *
	 * @return bool True if under the cap.
	 */
	private function rate_limit_ok() {
		$ip      = $this->client_ip();
		$session = Niwa_AI_Session_Utils::resolve_verified_session( true );
		foreach (
			array(
				array( 'lead_form_global_minute', 'global', 1000 ),
				array( 'lead_form_ip_minute', $ip, self::RATE_LIMIT_MAX ),
				array( 'lead_form_session_minute', $session, self::RATE_LIMIT_MAX ),
			) as $limit
		) {
			if ( ! Niwa_AI_Session_Utils::consume_fixed_window_limit( $limit[0], $limit[1], $limit[2], self::RATE_LIMIT_WINDOW ) ) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Resolve + sanitize the session id.
	 *
	 * @return string
	 */
	private function read_session_id() {
		return substr( Niwa_AI_Session_Utils::resolve_verified_session( true ), 0, 64 );
	}

	/**
	 * Read + sanitize the answer payload.
	 *
	 * Accepts:
	 *   - answer            (scalar string)
	 *   - answer[]          (array of strings for choice_multi)
	 *
	 * @return mixed
	 */
	private function read_answer() {
		if ( ! isset( $_POST['answer'] ) ) {
			return '';
		}
		$raw = wp_unslash( $_POST['answer'] );

		if ( is_array( $raw ) ) {
			$out = array();
			foreach ( $raw as $v ) {
				$out[] = sanitize_text_field( (string) $v );
			}
			return $out;
		}

		return sanitize_textarea_field( (string) $raw );
	}

	/**
	 * Best-effort client IP.
	 *
	 * @return string
	 */
	private function client_ip() {
		return Niwa_AI_Session_Utils::get_client_ip();
	}
}
