<?php

namespace WeDevs\Wpuf\Pro\AI_Review;

/**
 * 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 based on provider
     *
     * @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 );

        // Make API call based on provider
        switch ( $provider_name ) {
            case 'openai':
                return $this->call_openai_api( $endpoint, $api_key, $model, $review_prompt );
            case 'anthropic':
                return $this->call_anthropic_api( $endpoint, $api_key, $model, $review_prompt );
            case 'google':
                return $this->call_google_api( $endpoint, $api_key, $model, $review_prompt );
            default:
                throw new \Exception( 'Unsupported AI provider: ' . $provider_name );
        }
    }

    /**
     * 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:";
    }

    /**
     * Call OpenAI API
     *
     * @since 4.2.5
     * @param string $endpoint API endpoint
     * @param string $api_key API key
     * @param string $model Model to use
     * @param string $prompt Review prompt
     * @return string API response
     * @throws \Exception
     */
    private function call_openai_api( $endpoint, $api_key, $model, $prompt ) {
        $headers = [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type' => 'application/json',
        ];

        $data = [
            'model' => $model,
            'messages' => [
                [
                    'role' => 'user',
                    'content' => $prompt
                ]
            ],
            'max_tokens' => 500,
            'temperature' => 0.3
        ];

        $response = wp_remote_post( $endpoint, [
            'headers' => $headers,
            'body' => json_encode( $data ),
            'timeout' => 30
        ]);

        if ( is_wp_error( $response ) ) {
            throw new \Exception( 'OpenAI API error: ' . $response->get_error_message() );
        }

        $body = wp_remote_retrieve_body( $response );
        $decoded = json_decode( $body, true );

        if ( isset( $decoded['choices'][0]['message']['content'] ) ) {
            return $decoded['choices'][0]['message']['content'];
        }

        throw new \Exception( 'Invalid OpenAI API response' );
    }

    /**
     * Call Anthropic API
     *
     * @since 4.2.5
     * @param string $endpoint API endpoint
     * @param string $api_key API key
     * @param string $model Model to use
     * @param string $prompt Review prompt
     * @return string API response
     * @throws \Exception
     */
    private function call_anthropic_api( $endpoint, $api_key, $model, $prompt ) {
        $headers = [
            'x-api-key' => $api_key,
            'Content-Type' => 'application/json',
            'anthropic-version' => '2023-06-01'
        ];

        $data = [
            'model' => $model,
            'max_tokens' => 500,
            'messages' => [
                [
                    'role' => 'user',
                    'content' => $prompt
                ]
            ]
        ];

        $response = wp_remote_post( $endpoint, [
            'headers' => $headers,
            'body' => json_encode( $data ),
            'timeout' => 30
        ]);

        if ( is_wp_error( $response ) ) {
            throw new \Exception( 'Anthropic API error: ' . $response->get_error_message() );
        }

        $body = wp_remote_retrieve_body( $response );
        $decoded = json_decode( $body, true );

        if ( isset( $decoded['content'][0]['text'] ) ) {
            return $decoded['content'][0]['text'];
        }

        throw new \Exception( 'Invalid Anthropic API response' );
    }

    /**
     * Call Google API
     *
     * @since 4.2.5
     * @param string $endpoint API endpoint
     * @param string $api_key API key
     * @param string $model Model to use
     * @param string $prompt Review prompt
     * @return string API response
     * @throws \Exception
     */
    private function call_google_api( $endpoint, $api_key, $model, $prompt ) {
        $endpoint = str_replace( '{model}', $model, $endpoint );

        $data = [
            'contents' => [
                [
                    'parts' => [
                        [
                            'text' => $prompt
                        ]
                    ]
                ]
            ],
            'generationConfig' => [
                'maxOutputTokens' => 2048,
                'temperature' => 0.3
            ]
        ];

        $response = wp_remote_post( $endpoint, [
            'headers' => [
                'Content-Type' => 'application/json',
                'x-goog-api-key' => $api_key
            ],
            'body'    => json_encode( $data ),
            'timeout' => 30,
        ]);

        if ( is_wp_error( $response ) ) {
            throw new \Exception( 'Google API error: ' . $response->get_error_message() );
        }

        $body    = wp_remote_retrieve_body( $response );
        $decoded = json_decode( $body, true );

        // Check if we have a valid response with content
        if ( isset( $decoded['candidates'][0]['content']['parts'][0]['text'] ) ) {
            return $decoded['candidates'][0]['content']['parts'][0]['text'];
        }

        // Check for finish reason and handle accordingly
        if ( isset( $decoded['candidates'][0]['finishReason'] ) ) {
            $finish_reason = $decoded['candidates'][0]['finishReason'];

            if ( $finish_reason === 'MAX_TOKENS' ) {
                throw new \Exception( 'Google API response truncated due to max tokens limit. Try reducing the prompt length or increasing maxOutputTokens.' );
            } elseif ( $finish_reason === 'SAFETY' ) {
                throw new \Exception( 'Google API response blocked due to safety concerns' );
            } elseif ( $finish_reason === 'RECITATION' ) {
                throw new \Exception( 'Google API response blocked due to recitation concerns' );
            } else {
                throw new \Exception( 'Google API response finished with reason: ' . $finish_reason );
            }
        }

        // Check if there's an error in the response
        if ( isset( $decoded['error'] ) ) {
            $error_message = isset( $decoded['error']['message'] ) ? $decoded['error']['message'] : 'Unknown error';
            throw new \Exception( 'Google API error: ' . $error_message );
        }

        throw new \Exception( 'Invalid Google API response - no content found' );
    }

    /**
     * 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;

        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 ] );
            }
        }

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

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