<?php

namespace AIPT\Api\BulkGenerator\FAQ;

use AIPT\Api\BulkGenerator\BaseBulkGeneratorController;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

class FAQGeneratorController extends BaseBulkGeneratorController {

	protected const RATE_LIMIT_SCOPE = 'faq';

	/**
	 * @var FAQGeneratorService
	 */
	private $service;

	public function __construct() {
		$this->service = new FAQGeneratorService();
	}

	public function register_routes(): void {
		register_rest_route(
			'aipt/v1',
			'/faq-generator/bulk-generate',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'bulk_generate' ),
				'permission_callback' => array( $this, 'check_permissions_write' ),
				'args'                => array(
					'productIds' => array(
						'type'     => 'array',
						'required' => true,
					),
					'options'    => array(
						'type'     => 'object',
						'required' => false,
					),
				),
			)
		);

		register_rest_route(
			'aipt/v1',
			'/faq-generator/bulk-apply',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'bulk_apply' ),
				'permission_callback' => array( $this, 'check_permissions_write' ),
				'args'                => array(
					'results' => array(
						'type'     => 'array',
						'required' => true,
					),
					'options' => array(
						'type'     => 'object',
						'required' => false,
					),
				),
			)
		);

		register_rest_route(
			'aipt/v1',
			'/faq-generator/bulk-reject',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'bulk_reject' ),
				'permission_callback' => array( $this, 'check_permissions_write' ),
				'args'                => array(
					'productIds' => array(
						'type'     => 'array',
						'required' => true,
					),
				),
			)
		);
	}

	/**
	 * Apply free-plan defaults to FAQ options.
	 *
	 * Overrides premium-only fields with free defaults, then exposes
	 * the result via a filter so the Premium module can restore the
	 * original values when a valid licence is present.
	 *
	 * @since 2.6.0
	 *
	 * @param array $options Raw request options.
	 * @return array Options with free defaults applied (or premium values restored via filter).
	 */
	private function applyFreeDefaults( array $options ): array {
		$free_defaults = array(
			'faqMode'     => 'both',
			'writeMode'   => 'append',
			'targetField' => 'post_content',
			'faqSources'  => array( 'product_description' ),
		);

		$controlled = array_merge( $options, $free_defaults );

		unset( $controlled['additionalPrompt'] );

		/**
		 * Filter FAQ generator validated options.
		 *
		 * Premium module can hook here to restore premium option values
		 * when a valid license is present.
		 *
		 * @since 2.6.0
		 *
		 * @param array $controlled Options with free defaults applied.
		 * @param array $options    Original request options.
		 */
		return apply_filters( 'aipt_faq_validate_options', $controlled, $options );
	}

	/**
	 * Check for premium-only parameters and return a WP_Error if a free user
	 * has submitted any of them.
	 *
	 * This replaces the previous silent-strip behaviour (PR-07 / PR-11) so that
	 * free users receive a clear 403 instead of degraded output without notice.
	 *
	 * Premium parameters detected here:
	 *  - additionalPrompt  – custom prompt injection
	 *  - faqMode           – values other than 'both' (questions/answers only)
	 *  - writeMode         – 'replace' mode
	 *  - targetField       – fields other than post_content / description
	 *  - faqSources        – sources other than ['product_description']
	 *
	 * @since 2.6.0
	 *
	 * @param array $options Sanitized request options.
	 * @return WP_Error|null WP_Error on premium violation, null when clear.
	 */
	private function checkPremiumParameters( array $options ): ?WP_Error {
		if ( \AIPT\Utils\PremiumFeatureManager::getInstance()->canUsePremiumFeatures() ) {
			return null;
		}

		$premium_features_used = array();

		if ( ! empty( $options['additionalPrompt'] ) ) {
			$premium_features_used[] = 'additionalPrompt';
		}

		if ( isset( $options['faqMode'] ) && 'both' !== $options['faqMode'] ) {
			$premium_features_used[] = 'faqMode';
		}

		if ( isset( $options['writeMode'] ) && 'append' !== $options['writeMode'] ) {
			$premium_features_used[] = 'writeMode';
		}

		if ( isset( $options['targetField'] ) ) {
			$free_fields = array( 'post_content', 'description' );
			if ( ! in_array( $options['targetField'], $free_fields, true ) ) {
				$premium_features_used[] = 'targetField';
			}
		}

		if ( isset( $options['faqSources'] ) && is_array( $options['faqSources'] ) ) {
			$free_sources  = array( 'product_description' );
			$extra_sources = array_diff( $options['faqSources'], $free_sources );
			if ( ! empty( $extra_sources ) || count( $options['faqSources'] ) > 1 ) {
				$premium_features_used[] = 'faqSources';
			}
		}

		if ( empty( $premium_features_used ) ) {
			return null;
		}

		return new WP_Error(
			'premium_required',
			__( 'The following FAQ options require a Pro plan.', 'ai-product-tools' ),
			array(
				'status'           => 403,
				'premium_features' => $premium_features_used,
			)
		);
	}

	/**
	 * Sanitize FAQ generator options from REST requests.
	 *
	 * @param array $options Raw request options.
	 * @return array Sanitized options.
	 */
	private function sanitizeOptions( array $options ): array {
		$sanitized = array();

		foreach ( array( 'faqFormat', 'faqMode', 'writeMode', 'targetField', 'language', 'provider', 'model' ) as $key ) {
			if ( isset( $options[ $key ] ) ) {
				$sanitized[ $key ] = sanitize_text_field( (string) $options[ $key ] );
			}
		}

		if ( isset( $options['faqCount'] ) ) {
			$sanitized['faqCount'] = absint( $options['faqCount'] );
		}

		if ( isset( $options['additionalPrompt'] ) ) {
			$sanitized['additionalPrompt'] = sanitize_textarea_field( (string) $options['additionalPrompt'] );
		}

		if ( isset( $options['faqSources'] ) && is_array( $options['faqSources'] ) ) {
			$sanitized['faqSources'] = array_values(
				array_unique(
					array_filter(
						array_map(
							static function ( $source ): string {
								return sanitize_text_field( (string) $source );
							},
							$options['faqSources']
						)
					)
				)
			);
		}

		return $sanitized;
	}

	public function bulk_generate( WP_REST_Request $request ) {

		$ai_gate = \AIPT\Core\Services\AiSupportGate::guard();
		if ( $ai_gate instanceof \WP_Error ) {
			return $ai_gate;
		}
		$rate_check = $this->checkRateLimit( (int) get_current_user_id(), static::RATE_LIMIT_SCOPE );
		if ( ! $rate_check['allowed'] ) {
			return $this->rateLimitResponse( $rate_check );
		}

		$product_ids = array_values( array_filter( array_map( 'absint', (array) $request->get_param( 'productIds' ) ) ) );
		$options     = $request->get_param( 'options' );

		if ( ! is_array( $product_ids ) || empty( $product_ids ) ) {
			return new WP_Error(
				'aipt_faq_invalid_products',
				__( 'Please select at least one product to generate FAQs.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$options = is_array( $options ) ? $this->sanitizeOptions( $options ) : array();

		$premium_error = $this->checkPremiumParameters( $options );
		if ( $premium_error instanceof WP_Error ) {
			return $premium_error;
		}

		$options = $this->applyFreeDefaults( $options );

		try {
			$results = $this->service->bulk_generate( $product_ids, $options );

			\AIPT\Core\Services\UsageTracker::record_event(
				'faq',
				'generated',
				is_array( $results ) ? count( $results ) : count( $product_ids ),
				array(
					'product_id' => is_array( $product_ids ) && ! empty( $product_ids )
						? (int) $product_ids[ array_key_first( $product_ids ) ]
						: 0,
				)
			);

			return new WP_REST_Response(
				array(
					'success' => true,
					'message' => __( 'FAQ generation completed.', 'ai-product-tools' ),
					'results' => $results,
				),
				200
			);
		} catch ( \Exception $exception ) {
			return new WP_Error(
				'aipt_faq_generation_failed',
				$exception->getMessage(),
				array( 'status' => 500 )
			);
		}
	}

	public function bulk_apply( WP_REST_Request $request ) {
		$payloads = $request->get_param( 'results' );

		if ( ! is_array( $payloads ) || empty( $payloads ) ) {
			return new WP_Error(
				'aipt_faq_invalid_results',
				__( 'No FAQ results were provided for applying content.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$apply_options = $request->get_param( 'options' );
		$apply_options = is_array( $apply_options ) ? $this->sanitizeOptions( $apply_options ) : array();

		$apply_options = $this->applyFreeDefaults( $apply_options );

		$applied   = 0;
		$failed    = 0;
		$responses = array();

		foreach ( $payloads as $result ) {
			if ( ! is_array( $result ) ) {
				continue;
			}

			$product_id = isset( $result['product_id'] ) ? absint( $result['product_id'] ) : 0;
			if ( $product_id <= 0 ) {
				$failed++;
				$responses[] = array(
					'product_id' => 0,
					'status'     => 'error',
					'message'    => __( 'Invalid product identifier.', 'ai-product-tools' ),
				);
				continue;
			}

			$product = wc_get_product( $product_id );
			if ( ! $product ) {
				$failed++;
				$responses[] = array(
					'product_id' => $product_id,
					'status'     => 'error',
					'message'    => __( 'Product could not be loaded.', 'ai-product-tools' ),
				);
				continue;
			}

			try {
				$write_mode = isset( $result['writeMode'] ) && is_string( $result['writeMode'] )
					? strtolower( $result['writeMode'] )
					: ( isset( $apply_options['writeMode'] ) ? strtolower( (string) $apply_options['writeMode'] ) : 'append' );

				$write_mode = in_array( $write_mode, array( 'append', 'replace' ), true ) ? $write_mode : 'replace';

				if ( empty( $result['faqs'] ) || ! is_array( $result['faqs'] ) ) {
					$failed++;
					$responses[] = array(
						'product_id' => $product_id,
						'status'     => 'error',
						'message'    => __( 'No FAQ items provided.', 'ai-product-tools' ),
					);
					continue;
				}

				$structured_faqs = array();
				foreach ( $result['faqs'] as $faq ) {
					if ( is_array( $faq ) && isset( $faq['question'], $faq['answer'] ) ) {
						$structured_faqs[] = array(
							'question' => sanitize_text_field( (string) $faq['question'] ),
							'answer'   => wp_kses_post( (string) $faq['answer'] ),
						);
					}
				}

				if ( empty( $structured_faqs ) ) {
					$failed++;
					$responses[] = array(
						'product_id' => $product_id,
						'status'     => 'error',
						'message'    => __( 'FAQ items could not be validated.', 'ai-product-tools' ),
					);
					continue;
				}

				if ( 'append' === $write_mode ) {
					$existing_json = get_post_meta( $product_id, '_aipt_faq_data', true );
					$existing_faqs = ! empty( $existing_json ) ? json_decode( $existing_json, true ) : array();
					if ( is_array( $existing_faqs ) ) {
						$structured_faqs = array_merge( $existing_faqs, $structured_faqs );
					}
				}

				$json_to_save = wp_json_encode( $structured_faqs );
				if ( false === $json_to_save ) {
					$failed++;
					$responses[] = array(
						'product_id' => $product_id,
						'status'     => 'error',
						'message'    => __( 'Failed to encode FAQ data.', 'ai-product-tools' ),
					);
					continue;
				}

				update_post_meta( $product_id, '_aipt_faq_data', wp_slash( $json_to_save ) );

				$applied++;
				$responses[] = array(
					'product_id' => $product_id,
					'status'     => 'success',
					'message'    => __( 'FAQ content applied successfully.', 'ai-product-tools' ),
				);
			} catch ( \Exception $exception ) {
				$failed++;
				$responses[] = array(
					'product_id' => $product_id,
					'status'     => 'error',
					'message'    => $exception->getMessage(),
				);
			}
		}

		if ( $applied > 0 ) {
			$first_applied_pid = 0;
			foreach ( $responses as $resp ) {
				if ( isset( $resp['status'], $resp['product_id'] ) && 'success' === $resp['status'] ) {
					$first_applied_pid = (int) $resp['product_id'];
					break;
				}
			}
			\AIPT\Core\Services\UsageTracker::record_event(
				'faq',
				'applied',
				$applied,
				array( 'product_id' => $first_applied_pid )
			);
		}

		return new WP_REST_Response(
			array(
				'success'       => 0 === $failed,
				'applied_count' => $applied,
				'failed_count'  => $failed,
				'results'       => $responses,
			),
			200
		);
	}

	public function bulk_reject( WP_REST_Request $request ) {
		$product_ids = array_values( array_filter( array_map( 'absint', (array) $request->get_param( 'productIds' ) ) ) );

		if ( empty( $product_ids ) ) {
			return new WP_Error(
				'aipt_faq_invalid_reject_payload',
				__( 'A valid product list is required to reject FAQ results.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$total = count( $product_ids );

		if ( $total > 0 ) {
			\AIPT\Core\Services\UsageTracker::record_event(
				'faq',
				'rejected',
				$total,
				array(
					'product_id' => is_array( $product_ids ) && ! empty( $product_ids )
						? (int) $product_ids[ array_key_first( $product_ids ) ]
						: 0,
				)
			);
		}

		return new WP_REST_Response(
			array(
				'success'       => true,
				'rejectedCount' => $total,
				'message'       => sprintf(
					/* translators: %d: rejected product count */
					__( 'Rejected FAQ results for %d products.', 'ai-product-tools' ),
					$total
				),
			),
			200
		);
	}

	/**
	 * Register _aipt_faq_data post meta for REST exposure.
	 *
	 * Called from Init::register_hooks() so the meta is available in the REST API.
	 */
	public static function register_meta(): void {
		\register_post_meta(
			'product',
			'_aipt_faq_data',
			array(
				'type'              => 'string',
				'description'       => 'Structured FAQ data for accordion rendering',
				'single'            => true,
				'show_in_rest'      => true,
				'sanitize_callback' => array( __CLASS__, 'sanitize_faq_json' ),
				'auth_callback'     => function () {
					return current_user_can( 'manage_woocommerce' );
				},
			)
		);
	}

	/**
	 * Sanitize FAQ JSON meta value.
	 *
	 * Validates JSON structure (array of question/answer pairs).
	 * Returns the original JSON string only if valid, empty string otherwise.
	 *
	 * @param string $value Raw meta value.
	 * @return string Sanitized JSON string or empty string.
	 */
	public static function sanitize_faq_json( $value ): string {
		if ( ! is_string( $value ) || '' === $value ) {
			return '';
		}

		$decoded = json_decode( $value, true );

		if ( ! is_array( $decoded ) ) {
			return '';
		}

		$sanitized = array();
		foreach ( $decoded as $faq ) {
			if ( ! is_array( $faq ) || ! isset( $faq['question'], $faq['answer'] ) ) {
				continue;
			}
			$sanitized[] = array(
				'question' => sanitize_text_field( (string) $faq['question'] ),
				'answer'   => wp_kses_post( (string) $faq['answer'] ),
			);
		}

		if ( empty( $sanitized ) ) {
			return '';
		}

		$json = wp_json_encode( $sanitized );

		return is_string( $json ) ? $json : '';
	}
}
