<?php

/**
 * AI Method Trait
 *
 * Provides standardized error handling, configuration checks, and response formatting
 * for all AI-powered REST API methods. This trait ensures consistency across all
 * AI features and prevents common bugs.
 *
 * @package ProRank\SEO\Core\RestApi\Traits
 * @since   0.1.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\RestApi\Traits;

use WP_Error;
use WP_REST_Response;

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


/**
 * AI Method Trait
 *
 * Usage:
 * ```php
 * class AiController extends BaseController {
 *     use AiMethodTrait;
 *
 *     public function my_ai_method( WP_REST_Request $request ) {
 *         return $this->execute_ai_method(
 *             'my_feature_name',
 *             'pro', // Required tier
 *             function() use ( $request ) {
 *                 // Your AI logic here
 *                 $result = $this->ai_manager->handle_ai_operation(...);
 *                 return [ 'suggestions' => $result ];
 *             }
 *         );
 *     }
 * }
 * ```
 */


trait AiMethodTrait {

	/**
	 * Execute an AI method with standardized error handling
	 *
	 * This method provides:
	 * - Automatic license tier checking
	 * - AI configuration validation
	 * - Try-catch error handling
	 * - Debug logging (when WP_DEBUG is enabled)
	 * - Consistent response format
	 *
	 * @param string   $operation_name Name of the AI operation (for logging)
	 * @param string   $required_tier  Minimum license tier required (core, pro, business, agency)
	 * @param callable $callback       The AI operation to execute
	 * @param array    $context        Optional context data for error messages
	 * @return WP_REST_Response|WP_Error
	 */
	protected function execute_ai_method(
		string $operation_name,
		string $required_tier,
		callable $callback,
		array $context = []
	) {
		try {
			// Step 1: Check license tier
			if ( ! $this->has_tier_access( $required_tier ) ) {
				return new WP_Error(
					'feature_locked',
					sprintf(
						/* translators: %1$s: operation name, %2$s: required tier */
						__( '%1$s requires %2$s license or higher.', 'prorank-seo' ),
						ucfirst( str_replace( '_', ' ', $operation_name ) ),
						ucfirst( $required_tier ) . '+'
					),
					[ 'status' => 403 ]
				);
			}

			// Step 2: Check AI configuration
			$config_check = $this->check_ai_configuration();
			if ( is_wp_error( $config_check ) ) {
				return $config_check;
			}

			// Step 3: Execute the callback
			$result = $callback();

			// Step 4: Return standardized response
			if ( is_wp_error( $result ) ) {
				return $this->normalize_ai_error( $result );
			}

			return new WP_REST_Response(
				[
					'success' => true,
					'data'    => $result,
				],
				200
			);

		} catch ( \Exception $e ) {
			// Log error in debug mode
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				prorank_log(
					sprintf(
						'ProRank AI Error [%s]: %s',
						$operation_name,
						$e->getMessage()
					)
				);

				// Log context if provided
				if ( ! empty( $context ) ) {
					prorank_log(
						sprintf(
							'ProRank AI Context [%s]: %s',
							$operation_name,
							wp_json_encode( $context )
						)
					);
				}
			}

			return $this->normalize_ai_error(
				new WP_Error(
					'ai_error',
					$e->getMessage(),
					[ 'status' => 500 ]
				)
			);
		}
	}

	/**
	 * Normalize raw provider errors into user-facing AI messages.
	 *
	 * @param WP_Error $error Original error.
	 * @return WP_Error
	 */
	protected function normalize_ai_error( WP_Error $error ): WP_Error {
		$error_code = (string) $error->get_error_code();
		$message    = trim( (string) $error->get_error_message() );
		$lower      = strtolower( $message );
		$data       = is_array( $error->get_error_data() ) ? $error->get_error_data() : [];
		$status     = (int) ( $data['status'] ?? 500 );

		if ( in_array( $error_code, [ 'feature_locked', 'content_too_short', 'content_too_long', 'post_not_found', 'forbidden' ], true ) ) {
			return $error;
		}

		if (
			str_contains( $lower, 'quota' )
			|| str_contains( $lower, 'resource exhausted' )
			|| str_contains( $lower, 'rate limit' )
			|| str_contains( $lower, 'free_tier' )
			|| str_contains( $lower, 'please retry in' )
			|| str_contains( $lower, 'exceeded your current quota' )
		) {
			return new WP_Error(
				'ai_quota_exhausted',
				__(
					'AI suggestions are temporarily unavailable because the configured AI provider has no remaining quota. Add billing, switch provider, or try again later.',
					'prorank-seo'
				),
				[
					'status'           => 503,
					'reason'           => 'quota_exhausted',
					'original_message' => $message,
				]
			);
		}

		if (
			str_contains( $lower, 'temporarily unavailable' )
			|| str_contains( $lower, 'high demand' )
			|| str_contains( $lower, 'timeout' )
			|| str_contains( $lower, 'timed out' )
			|| str_contains( $lower, 'deadline exceeded' )
			|| str_contains( $lower, 'connection error' )
		) {
			return new WP_Error(
				'ai_temporarily_unavailable',
				__(
					'AI suggestions are temporarily unavailable. Please try again shortly.',
					'prorank-seo'
				),
				[
					'status'           => 503,
					'reason'           => 'temporary_unavailable',
					'original_message' => $message,
				]
			);
		}

		if (
			str_contains( $lower, 'invalid api key' )
			|| ( str_contains( $lower, 'api key' ) && str_contains( $lower, 'invalid' ) )
			|| str_contains( $lower, 'access forbidden' )
		) {
			return new WP_Error(
				'ai_invalid_credentials',
				__(
					'The configured AI provider credentials are invalid. Update them in AI Tools > AI Configuration.',
					'prorank-seo'
				),
				[
					'status'           => 400,
					'reason'           => 'invalid_credentials',
					'original_message' => $message,
				]
			);
		}

		if ( $error_code === 'ai_not_configured' || str_contains( $lower, 'not configured' ) ) {
			return new WP_Error(
				'ai_not_configured',
				__(
					'AI provider is not configured. Configure it in AI Tools > AI Configuration.',
					'prorank-seo'
				),
				[
					'status'           => 400,
					'reason'           => 'not_configured',
					'original_message' => $message,
				]
			);
		}

		return new WP_Error(
			$error_code ?: 'ai_error',
			__(
				'AI suggestions could not be generated right now. Please try again shortly.',
				'prorank-seo'
			),
			[
				'status'           => max( 400, $status ),
				'reason'           => 'provider_error',
				'original_message' => $message,
			]
		);
	}

	/**
	 * Execute an AI method that accepts direct content (not post_id)
	 *
	 * Same as execute_ai_method() but includes content validation.
	 *
	 * @param string   $operation_name Name of the AI operation
	 * @param string   $required_tier  Minimum license tier required
	 * @param string   $content        Content to process
	 * @param callable $callback       The AI operation to execute
	 * @param array    $validation     Content validation rules
	 * @return WP_REST_Response|WP_Error
	 */
	protected function execute_ai_content_method(
		string $operation_name,
		string $required_tier,
		string $content,
		callable $callback,
		array $validation = []
	) {
		// Validate content length
		$min_length = $validation['min_length'] ?? 50;
		$max_length = $validation['max_length'] ?? 5000;

		$content_length = strlen( $content );

		if ( $content_length < $min_length ) {
			return new WP_Error(
				'content_too_short',
				sprintf(
					/* translators: %d: minimum length */
					__( 'Content must be at least %1$d characters.', 'prorank-seo' ),
					$min_length
				),
				[ 'status' => 400 ]
			);
		}

		if ( $content_length > $max_length ) {
			return new WP_Error(
				'content_too_long',
				sprintf(
					/* translators: %d: maximum length */
					__( 'Content must be less than %1$d characters.', 'prorank-seo' ),
					$max_length
				),
				[ 'status' => 400 ]
			);
		}

		// Execute with standard method
		return $this->execute_ai_method(
			$operation_name,
			$required_tier,
			$callback,
			[ 'content_length' => $content_length ]
		);
	}

	/**
	 * Execute an AI method that operates on a post
	 *
	 * Same as execute_ai_method() but includes post validation.
	 *
	 * @param string   $operation_name Name of the AI operation
	 * @param string   $required_tier  Minimum license tier required
	 * @param int      $post_id        Post ID to process
	 * @param callable $callback       The AI operation to execute
	 * @return WP_REST_Response|WP_Error
	 */
	protected function execute_ai_post_method(
		string $operation_name,
		string $required_tier,
		int $post_id,
		callable $callback
	) {
		// Validate post exists
		$post = get_post( $post_id );

		if ( ! $post ) {
			return new WP_Error(
				'post_not_found',
				__( 'Post not found.', 'prorank-seo' ),
				[ 'status' => 404 ]
			);
		}

		// Execute with standard method
		return $this->execute_ai_method(
			$operation_name,
			$required_tier,
			function() use ( $callback, $post ) {
				return $callback( $post );
			},
			[
				'post_id'    => $post->ID,
				'post_type'  => $post->post_type,
				'post_title' => $post->post_title,
			]
		);
	}

	/**
	 * Parse AI response as JSON with fallback
	 *
	 * Attempts to parse AI response as JSON. If parsing fails, returns a fallback value.
	 *
	 * @param string $text         AI response text
	 * @param mixed  $fallback     Fallback value if parsing fails
	 * @param bool   $return_error Whether to return WP_Error on failure
	 * @return mixed Parsed JSON or fallback value
	 */
	protected function parse_ai_json_response( string $text, $fallback = [], bool $return_error = false ) {
		$parsed = $this->parse_json_response( $text );

		if ( is_wp_error( $parsed ) ) {
			if ( $return_error ) {
				return $parsed;
			}

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				prorank_log( 'ProRank AI: Failed to parse JSON response, using fallback' );
			}

			return $fallback;
		}

		return $parsed;
	}

	/**
	 * Parse AI response as array with fallback
	 *
	 * @param string $text     AI response text
	 * @param array  $fallback Fallback array if parsing fails
	 * @return array
	 */
	protected function parse_ai_array_response( string $text, array $fallback = [] ): array {
		$parsed = $this->parse_array_response( $text );

		if ( empty( $parsed ) ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				prorank_log( 'ProRank AI: Failed to parse array response, using fallback' );
			}

			return $fallback;
		}

		return $parsed;
	}

	/**
	 * Truncate content for AI processing
	 *
	 * @param string $content    Content to truncate
	 * @param int    $max_length Maximum length
	 * @param string $suffix     Suffix to add if truncated
	 * @return string Truncated content
	 */
	protected function truncate_content_for_ai( string $content, int $max_length = 3000, string $suffix = '...' ): string {
		if ( strlen( $content ) <= $max_length ) {
			return $content;
		}

		return substr( $content, 0, $max_length ) . $suffix;
	}

	/**
	 * Build AI prompt with template
	 *
	 * @param string $template Template with placeholders
	 * @param array  $data     Data to replace placeholders
	 * @return string
	 */
	protected function build_ai_prompt( string $template, array $data ): string {
		$prompt = $template;

		foreach ( $data as $key => $value ) {
			$prompt = str_replace( '{' . $key . '}', (string) $value, $prompt );
		}

		return $prompt;
	}

	/**
	 * Get AI operation parameters with defaults
	 *
	 * @param string $operation_type Type of operation (creative, analytical, etc)
	 * @param array  $overrides      Parameter overrides
	 * @return array
	 */
	protected function get_ai_params( string $operation_type, array $overrides = [] ): array {
		$defaults = [
			'creative'   => [
				'max_tokens'  => 500,
				'temperature' => 0.8,
			],
			'analytical' => [
				'max_tokens'  => 300,
				'temperature' => 0.5,
			],
			'balanced'   => [
				'max_tokens'  => 400,
				'temperature' => 0.7,
			],
			'precise'    => [
				'max_tokens'  => 200,
				'temperature' => 0.3,
			],
		];

		$params = $defaults[ $operation_type ] ?? $defaults['balanced'];

		return array_merge( $params, $overrides );
	}
}
