<?php

namespace WeDevs\Wpuf\Pro\AI_Review;

use WeDevs\Wpuf\Pro\AI_Review\Providers\Provider_Factory;

/**
 * AI Service - Main orchestrator for AI API calls
 *
 * @since 4.2.5
 */
class Service {

    /**
     * Configuration constants
     */
    const DEFAULT_TIMEOUT = 30;
    const DEFAULT_MAX_TOKENS = 500;
    const DEFAULT_MAX_RETRIES = 3;
    const DEFAULT_RETRY_DELAY = 1;
    const MAX_CALLS_PER_HOUR = 100;
    const DEFAULT_TEMPERATURE = 0.3;

    /**
     * Global AI settings
     *
     * @var array
     */
    private $settings;

    /**
     * Rate limiting cache
     *
     * @var array
     */
    private static $rate_limit_cache = [];

    /**
     * Constructor
     *
     * @since 4.2.5
     */
    public function __construct() {
        $this->settings = $this->get_validated_settings();
    }


    /**
     * Review a post using AI
     *
     * @since 4.2.5
     * @param string $title Post title
     * @param string $content Post content (can include custom fields and taxonomies)
     * @param string $excerpt Post excerpt
     * @param string $prompt Custom review prompt
     * @return array Review result with status and summary
     * @throws \Exception
     */
    public function review_post( string $title, string $content, string $excerpt, string $prompt ): array {
        // Input validation
        $title = sanitize_text_field( $title );
        $content = wp_kses_post( $content ); // Allow HTML but sanitize
        $excerpt = sanitize_textarea_field( $excerpt );
        $prompt = sanitize_textarea_field( $prompt );

        // Check rate limiting
        if ( ! $this->check_rate_limit() ) {
            return [
                'status' => 'failed',
                'summary' => 'Rate limit exceeded. Please try again later.'
            ];
        }

        $provider_name = ! empty( $this->settings['ai_provider'] ) ? $this->settings['ai_provider'] : '';
        $model         = ! empty( $this->settings['ai_model'] ) ? $this->settings['ai_model'] : '';

        // Get provider configurations from wpuf_ai settings
        $provider_configs = \WeDevs\Wpuf\AI\Config::get_providers();

        if ( ! isset( $provider_configs[ $provider_name ] ) ) {
            throw new \Exception( 'Unsupported AI provider: ' . $provider_name );
        }

        $provider_config = $provider_configs[ $provider_name ];

        // Get provider-specific API key
        $api_key_field = $provider_config['api_key_field'] ?? $provider_name . '_api_key';
        $api_key = ! empty( $this->settings[ $api_key_field ] ) ? $this->settings[ $api_key_field ] : '';

        if ( empty( $api_key ) ) {
            throw new \Exception( 'AI API key not configured for provider: ' . $provider_name );
        }

        try {
            // Make API call based on provider
            $result = $this->make_ai_api_call( $provider_name, $provider_config, $model, $api_key, $title, $content, $excerpt, $prompt );

            // Parse and validate response
            $parsed_result = $this->parse_ai_response( $result );

            return $parsed_result;

        } catch ( \Exception $e ) {
            // Return failed status
            return [
                'status' => 'failed',
                'summary' => 'Review failed: ' . $e->getMessage()
            ];
        }
    }

    /**
     * Make AI API call using provider factory
     *
     * @since 4.2.5
     * @param string $provider_name Provider name
     * @param array  $provider_config Provider configuration
     * @param string $model Model to use
     * @param string $api_key API key for the provider
     * @param string $title Post title
     * @param string $content Post content
     * @param string $excerpt Post excerpt
     * @param string $prompt Custom prompt
     * @return string API response
     * @throws \Exception
     */
    private function make_ai_api_call( $provider_name, $provider_config, $model, $api_key, $title, $content, $excerpt, $prompt ) {
        $endpoint = $provider_config['endpoint'];

        // Prepare the review prompt
        $review_prompt = $this->build_review_prompt( $title, $content, $excerpt, $prompt );

        // Use factory to create provider instance
        $provider = Provider_Factory::create( $provider_name, $endpoint, $api_key, $model );

        // Delegate API call to provider
        return $provider->make_api_call( $endpoint, $api_key, $model, $review_prompt );
    }

    /**
     * Build review prompt from post data
     *
     * @since 4.2.5
     * @param string $title Post title
     * @param string $content Post content (can include formatted custom fields and taxonomies)
     * @param string $excerpt Post excerpt
     * @param string $custom_prompt Custom prompt
     * @return string Complete review prompt
     */
    private function build_review_prompt( $title, $content, $excerpt, $custom_prompt ) {
        $default_prompt = "Review the following post against the given criteria. You must respond with ONLY a JSON object containing 'status' and 'summary' fields.

Status options:
- 'full': fully meets all criteria
- 'medium': partially meets criteria
- 'none': does not meet criteria
- 'failed': technical error (only use for API/system failures, not content evaluation)

Summary should be a brief explanation of your assessment.

Example response format:
{\"status\": \"medium\", \"summary\": \"The post partially meets the criteria but needs improvement in some areas.\"}

Post to review:";

        // Use custom prompt if provided, but ensure JSON format requirement is always included
        if ( ! empty( $custom_prompt ) ) {
            $prompt = $custom_prompt . "\n\nIMPORTANT: You must respond with ONLY a JSON object containing 'status' and 'summary' fields.

Status options:
- 'full': fully meets all criteria
- 'medium': partially meets criteria
- 'none': does not meet criteria
- 'failed': technical error (only use for API/system failures, not content evaluation)

Summary should be a brief explanation of your assessment.";
        } else {
            $prompt = $default_prompt;
        }

        return $prompt . "\n\nTitle: " . $title . "\nContent: " . $content . "\nExcerpt: " . $excerpt . "\n\nRespond with JSON only:";
    }

    /**
     * Parse AI response and extract status + summary
     *
     * @since 4.2.5
     * @param string $response Raw AI response
     * @return array Parsed result
     */
    private function parse_ai_response( $response ) {
        // Try to parse as JSON first
        $json_data = json_decode( $response, true );

        if ( $json_data && isset( $json_data['status'] ) && isset( $json_data['summary'] ) ) {
            // Validate status
            $valid_statuses = [ 'full', 'medium', 'none', 'failed' ];
            $status = in_array( $json_data['status'], $valid_statuses ) ? $json_data['status'] : 'failed';

            // Convert "failed" to "none" if the summary indicates content evaluation rather than technical failure
            if ( $status === 'failed' ) {
                $summary_lower = strtolower( $json_data['summary'] );
                $content_evaluation_keywords = [
                    'not a recipe', 'not food', 'does not meet', 'does not match', 'does not fit',
                    'impossible to evaluate', 'cannot evaluate', 'not applicable', 'wrong type',
                    'not relevant', 'does not contain', 'missing', 'lacks'
                ];

                foreach ( $content_evaluation_keywords as $keyword ) {
                    if ( strpos( $summary_lower, $keyword ) !== false ) {
                        $status = 'none';
                        break;
                    }
                }
            }

            return [
                'status' => $status,
                'summary' => sanitize_text_field( $json_data['summary'] )
            ];
        }

        // Fallback: try to extract from text response
        if ( preg_match( '/status["\']?\s*:\s*["\']?([^"\',\s]+)/i', $response, $status_matches ) &&
             preg_match( '/summary["\']?\s*:\s*["\']?([^"\']+)/i', $response, $summary_matches ) ) {

            $status = in_array( $status_matches[1], [ 'full', 'medium', 'none', 'failed' ] ) ? $status_matches[1] : 'failed';

            // Convert "failed" to "none" if the summary indicates content evaluation rather than technical failure
            if ( $status === 'failed' ) {
                $summary_lower = strtolower( $summary_matches[1] );
                $content_evaluation_keywords = [
                    'not a recipe', 'not food', 'does not meet', 'does not match', 'does not fit',
                    'impossible to evaluate', 'cannot evaluate', 'not applicable', 'wrong type',
                    'not relevant', 'does not contain', 'missing', 'lacks'
                ];

                foreach ( $content_evaluation_keywords as $keyword ) {
                    if ( strpos( $summary_lower, $keyword ) !== false ) {
                        $status = 'none';
                        break;
                    }
                }
            }

            return [
                'status' => $status,
                'summary' => sanitize_text_field( $summary_matches[1] )
            ];
        }

        // If all parsing fails, return failed status
        return [
            'status' => 'failed',
            'summary' => 'Unable to parse AI response'
        ];
    }

    /**
     * Get global AI settings
     *
     * @since 4.2.5
     * @return array
     */
    public function get_settings() {
        return $this->settings;
    }

    /**
     * Check if AI is properly configured
     *
     * @since 4.2.5
     * @return bool True if AI service is properly configured
     */
    public function is_configured(): bool {
        $provider = ! empty( $this->settings['ai_provider'] ) ? $this->settings['ai_provider'] : '';

        if ( empty( $provider ) || empty( $this->settings['ai_model'] ) ) {
            return false;
        }

        // Get provider-specific API key field name
        $provider_configs = \WeDevs\Wpuf\AI\Config::get_providers();
        if ( ! isset( $provider_configs[ $provider ] ) ) {
            return false;
        }

        $api_key_field = $provider_configs[ $provider ]['api_key_field'] ?? $provider . '_api_key';
        $api_key = ! empty( $this->settings[ $api_key_field ] ) ? $this->settings[ $api_key_field ] : '';

        return ! empty( $api_key );
    }

    /**
     * Validate review request - shared validation logic
     *
     * @since 4.2.5
     * @param int $post_id Post ID
     * @param array $form_settings Form settings
     * @return array Validation result with 'valid' boolean and 'message' string
     */
    public function validate_review_request( $post_id, $form_settings ) {
        // Check if AI service is configured
        if ( ! $this->is_configured() ) {
            return [
                'valid' => false,
                'message' => 'AI service not configured'
            ];
        }

        // Check if AI review is enabled for this form
        $ai_review_settings = ! empty( $form_settings['ai_review_enabled'] ) ? $form_settings['ai_review_enabled'] : 'off';
        if ( ! wpuf_is_checkbox_or_toggle_on( $ai_review_settings ) ) {
            return [
                'valid'   => false,
                'message' => 'AI Review not enabled for this form',
            ];
        }

        // Check if review already exists (skip if already reviewed successfully)
        $existing_review = get_post_meta( $post_id, 'wpuf_ai_post_review', true );

        // Allow re-review if status is 'failed', 'pending', or 'in_progress' (in case of stuck reviews)
        if ( ! empty( $existing_review ) && ! in_array( $existing_review['status'], [ 'failed', 'pending', 'in_progress' ], true ) ) {
            return [
                'valid'   => false,
                'message' => 'Review already exists with status: ' . $existing_review['status'],
            ];
        }

        return [
            'valid' => true,
            'message' => 'Validation passed'
        ];
    }

    /**
     * Get validated AI settings
     *
     * @since 4.2.5
     * @return array Validated settings
     */
    private function get_validated_settings() {
        $raw_settings = get_option( 'wpuf_ai', [] );
        $validated = [];

        // Validate provider
        if ( ! empty( $raw_settings['ai_provider'] ) ) {
            $provider = sanitize_key( $raw_settings['ai_provider'] );
            $allowed_providers = [ 'openai', 'anthropic', 'google' ];
            if ( in_array( $provider, $allowed_providers, true ) ) {
                $validated['ai_provider'] = $provider;
            }
        }

        // Validate model
        if ( ! empty( $raw_settings['ai_model'] ) ) {
            $validated['ai_model'] = sanitize_text_field( $raw_settings['ai_model'] );
        }

        // Validate API keys based on provider
        if ( ! empty( $validated['ai_provider'] ) ) {
            $api_key_field = $validated['ai_provider'] . '_api_key';
            if ( ! empty( $raw_settings[ $api_key_field ] ) ) {
                $validated[ $api_key_field ] = sanitize_text_field( $raw_settings[ $api_key_field ] );
            }
        }

        return $validated;
    }

    /**
     * Check rate limiting for API calls
     *
     * @since 4.2.5
     * @return bool True if rate limit allows the call
     */
    private function check_rate_limit() {
        $current_time = time();
        $window_key = date( 'Y-m-d-H', $current_time ); // Hourly windows
        $max_calls_per_hour = self::MAX_CALLS_PER_HOUR;

        // Initialize the window if not set (use null coalescing for thread safety)
        $current_count = self::$rate_limit_cache[ $window_key ] ?? 0;
        if ( ! isset( self::$rate_limit_cache[ $window_key ] ) ) {
            self::$rate_limit_cache[ $window_key ] = 0;
        }

        // Clean old entries (older than 2 hours)
        foreach ( array_keys( self::$rate_limit_cache ) as $key ) {
            $key_time = strtotime( $key . ':00:00' );
            if ( $current_time - $key_time > 7200 ) { // 2 hours
                unset( self::$rate_limit_cache[ $key ] );
            }
        }

        // Re-check after cleanup (the key might have been removed)
        if ( ! isset( self::$rate_limit_cache[ $window_key ] ) ) {
            self::$rate_limit_cache[ $window_key ] = 0;
        }

        if ( self::$rate_limit_cache[ $window_key ] >= $max_calls_per_hour ) {
            return false;
        }

        self::$rate_limit_cache[ $window_key ]++;
        return true;
    }
}
