<?php

namespace AIPT\Api\OpenAI;

use AIPT\Api\AIClientInterface;
use AIPT\Api\Chatbot\Jobs\IdempotencyKeyContext;
use AIPT\Api\Helpers\VisionImageEncoder;
use AIPT\Core\Services\UsageTracker;
use AIPT\Utils\PromptFormatter;
use AIPT\Core\ErrorHandler;

class OpenAIClient implements AIClientInterface {
    private $api_key;
    private $api_url = 'https://api.openai.com/v1/chat/completions';
    private $models = [];
    private ?IdempotencyKeyContext $idempotency_context;

    public function __construct($api_key, ?IdempotencyKeyContext $idempotency_context = null) {
        $this->api_key = $api_key;
        $this->idempotency_context = $idempotency_context;
        $this->initializeModels();
    }
    
    /**
     * Initialize model configurations using shared base configurations
     * This eliminates code duplication and makes maintenance easier
     */
    private function initializeModels(): void {

        $gpt5Config = [
            'max_completion_tokens' => 128000,
            'use_legacy_params' => false,
            'has_temperature' => false,
            'supports_system_message' => true
        ];
        

        $gpt41Config = [
            'max_completion_tokens' => 32768,
            'temperature' => 0.7,
            'use_legacy_params' => false,
            'has_temperature' => true,
            'supports_system_message' => true
        ];
        

        $reasoningConfig = [
            'max_completion_tokens' => 100000,
            'temperature' => 0.7,
            'use_legacy_params' => false,
            'has_temperature' => false,
            'supports_system_message' => false
        ];
        

        $legacyConfig = [
            'max_tokens' => 4096,
            'temperature' => 0.7,
            'use_legacy_params' => true,
            'has_temperature' => true,
            'supports_system_message' => true
        ];
        
        $this->models = [

            'gpt-5.5' => ['name' => 'gpt-5.5'] + $gpt5Config,
            'gpt-5.5-pro' => ['name' => 'gpt-5.5-pro'] + $gpt5Config,

            'gpt-5.4' => ['name' => 'gpt-5.4'] + $gpt5Config,
            'gpt-5.4-mini' => ['name' => 'gpt-5.4-mini'] + $gpt5Config,
            'gpt-5.4-nano' => ['name' => 'gpt-5.4-nano'] + $gpt5Config,

            'gpt-4.1' => ['name' => 'gpt-4.1'] + $gpt41Config,
            'gpt-4.1-mini' => ['name' => 'gpt-4.1-mini'] + $gpt41Config,
            'gpt-4.1-nano' => ['name' => 'gpt-4.1-nano'] + $gpt41Config,

            'o4-mini' => ['name' => 'o4-mini'] + $reasoningConfig,
            'o3-pro' => ['name' => 'o3-pro'] + $reasoningConfig,
            'o3' => ['name' => 'o3'] + $reasoningConfig,
        ];
    }

    public function generate_description(string $system_prompt, string $user_prompt, string $model = 'gpt-4.1-mini', ?int $maximum_length = null): string {
        if (!isset($this->models[$model])) {
            throw new \Exception('Invalid model selected: ' . esc_html($model));
        }

        $model_config = $this->models[$model];
        $messages = [];

        $system_prompt = $this->sanitize_prompt_content($system_prompt);
        $user_prompt = $this->sanitize_prompt_content($user_prompt);

        if ($model_config['supports_system_message']) {
            $messages[] = [
                'role' => 'system',
                'content' => $system_prompt
            ];
            $messages[] = [
                'role' => 'user',
                'content' => $user_prompt
            ];
        } else {
            $messages[] = [
                'role' => 'user',
                'content' => $system_prompt . "\n\n" . $user_prompt
            ];
        }

        $request_body = [
            'model' => $model_config['name'],
            'messages' => $messages
        ];

        if ($model_config['has_temperature']) {
            $request_body['temperature'] = $model_config['temperature'];
        }
        

        $timeout = 30; 
        if (strpos($model, 'gpt-5') === 0) {
            $timeout = 120; 
        }
        
        $json_body = wp_json_encode($request_body, JSON_UNESCAPED_UNICODE);

        if ( false === $json_body ) {

            throw new \Exception(
                __( 'Failed to encode request body as JSON. The product data may contain invalid characters.', 'ai-product-tools' )
            );

        }

        $response = wp_remote_post($this->api_url, [
            'headers' => $this->withIdempotencyHeader([
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ]),
            'body' => $json_body,
            'timeout' => $timeout,
        ]);

        if ($response instanceof \WP_Error) {
            $error_message = $response->get_error_message();
            throw new \Exception(
                sprintf(
                    /* translators: %s: WP Error message */
                    esc_html__('WP Error: %s', 'ai-product-tools'),
                    esc_html($error_message)
                )
            );
        }

        $response_code = wp_remote_retrieve_response_code($response);
        $content_type = (string) wp_remote_retrieve_header($response, 'content-type');
        $body_str = (string) wp_remote_retrieve_body($response);

        if (strpos($content_type, 'application/json') === false && !empty($body_str) && $body_str[0] === '<') {

            throw new \Exception(__('The API response was not valid JSON. This is often caused by a server firewall or security plugin blocking outbound requests. Please contact your hosting provider or check your security plugin settings.', 'ai-product-tools'));
        }

        $body = json_decode($body_str, true);
        if (json_last_error() !== JSON_ERROR_NONE) {

            throw new \Exception(
                sprintf(
                    /* translators: %s: JSON error message */
                    __('Invalid JSON response from the API: %s', 'ai-product-tools'),
                    json_last_error_msg()
                )
            );

        }

        if (isset($body['error'])) {
            $error_data = $body['error'];

            $detailed_error = ErrorHandler::format_api_response_error($error_data, 'OpenAI', $response_code);

            throw new \Exception($detailed_error);
        }

        if (!isset($body['choices'][0]['message']['content'])) {
            throw new \Exception('Invalid response from OpenAI API: Response structure is not as expected');
        }

        UsageTracker::trackApiCall(
            'openai',
            (string) ( $body['model'] ?? $model_config['name'] ?? $model ),
            (int) ( $body['usage']['prompt_tokens'] ?? 0 ),
            (int) ( $body['usage']['completion_tokens'] ?? 0 )
        );

        return trim($body['choices'][0]['message']['content']);
    }

    public function generate_faq_content(string $system_prompt, string $user_prompt, ?string $model = null): string {
        $resolved_model = $model ?: 'gpt-4.1-mini';

        return $this->generate_description($system_prompt, $user_prompt, $resolved_model);
    }

    public function generate_from_vision(string $system_prompt, string $user_prompt, string $image_url, ?string $model = null): string {
        $resolved_model = $model ?: 'gpt-4.1-mini';

        $ai_gate = \AIPT\Core\Services\AiSupportGate::guard();
        if ( $ai_gate instanceof \WP_Error ) {
            throw new \Exception( esc_html( $ai_gate->get_error_message() ) );
        }

        try {
            $encoded_image = VisionImageEncoder::fetchAndEncode( $image_url );
            $image_data_uri = 'data:' . $encoded_image['mime_type'] . ';base64,' . $encoded_image['data'];
        } catch ( \RuntimeException $e ) {
            $detailed_error = ErrorHandler::format_api_response_error(
                [
                    'code'    => 'aipt_vision_fetch_failed',
                    'message' => esc_html( $e->getMessage() ),
                    'type'    => 'vision_image_fetch_failed',
                ],
                'OpenAI',
                0
            );

            throw new \Exception(
                $detailed_error
            );

        }

        $messages = [
            [
                'role'    => 'system',
                'content' => $this->sanitize_prompt_content( $system_prompt ),
            ],
            [
                'role'    => 'user',
                'content' => [
                    [
                        'type' => 'text',
                        'text' => $this->sanitize_prompt_content( $user_prompt ),
                    ],
                    [
                        'type'      => 'image_url',
                        'image_url' => [ 'url' => $image_data_uri ],
                    ],
                ],
            ],
        ];

        $model_config = $this->models[ $resolved_model ] ?? array(
            'name'          => $resolved_model,
            'max_tokens'    => 150,
            'temperature'   => 0.3,
            'has_temperature' => true,
            'use_legacy_params' => true,
        );

        $request_body = [
            'model'    => $model_config['name'],
            'messages' => $messages,
        ];

        if ( ! empty( $model_config['has_temperature'] ) ) {
            $request_body['temperature'] = (float) ( $model_config['temperature'] ?? 0.3 );
        }

        if ( ! empty( $model_config['use_legacy_params'] ) ) {
            $request_body['max_tokens'] = 150;
        } else {
            $request_body['max_completion_tokens'] = 150;
        }

        $json_body = wp_json_encode( $request_body, JSON_UNESCAPED_UNICODE );
        if ( false === $json_body ) {
            throw new \Exception( esc_html__( 'Failed to encode vision request body as JSON.', 'ai-product-tools' ) );
        }

        $response = wp_remote_post( $this->api_url, [
            'headers' => $this->withIdempotencyHeader( [
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type'  => 'application/json',
            ] ),
            'body'    => $json_body,
            'timeout' => 60,
        ] );

        if ( $response instanceof \WP_Error ) {
            throw new \Exception(
                sprintf(
                    /* translators: %s: error message */
                    esc_html__( 'WP Error: %s', 'ai-product-tools' ),
                    esc_html( $response->get_error_message() )
                )
            );
        }

        $response_code = wp_remote_retrieve_response_code( $response );
        $body_str      = (string) wp_remote_retrieve_body( $response );
        $body          = json_decode( $body_str, true );

        if ( json_last_error() !== JSON_ERROR_NONE ) {
            throw new \Exception(
                sprintf(
                    /* translators: %s: JSON error message */
                    esc_html__( 'Invalid JSON response from the API: %s', 'ai-product-tools' ),
                    esc_html( json_last_error_msg() )
                )
            );
        }

        if ( isset( $body['error'] ) ) {
            $detailed_error = ErrorHandler::format_api_response_error( $body['error'], 'OpenAI', $response_code );

            throw new \Exception( $detailed_error );
        }

        if ( ! isset( $body['choices'][0]['message']['content'] ) ) {
            throw new \Exception( 'Invalid response from OpenAI vision API: unexpected structure' );
        }

        UsageTracker::trackApiCall(
            'openai',
            (string) ( $body['model'] ?? $resolved_model ),
            (int) ( $body['usage']['prompt_tokens'] ?? 0 ),
            (int) ( $body['usage']['completion_tokens'] ?? 0 )
        );

        return trim( (string) $body['choices'][0]['message']['content'] );
    }

    /**
     * Prompt içeriğini temizler ve API için hazırlar
     * 
     * @param string $content Temizlenecek prompt içeriği 
     * @return string Temizlenmiş içerik
     */
    private function sanitize_prompt_content(string $content): string {
        return PromptFormatter::cleanContent($content);
    }

    private function withIdempotencyHeader( array $headers ): array {
        if ( null === $this->idempotency_context || ! $this->isIdempotencyAllowed() ) {
            return $headers;
        }

        $key = IdempotencyKeyContext::getIdempotencyKey();

        if ( null !== $key ) {
            $headers['Idempotency-Key'] = $key;
        }

        return $headers;
    }

    private function isIdempotencyAllowed(): bool {
        $providers = \apply_filters( 'aipt_chatbot_idempotency_providers', [ 'openai', 'anthropic' ] );

        if ( ! is_array( $providers ) ) {
            return false;
        }

        $providers = array_map( '\sanitize_key', $providers );

        return in_array( 'openai', $providers, true );
    }

    public static function validate_api_key(string $api_key): bool {
        $validation_timeout = 20;

        try {

            $response = wp_remote_get('https://api.openai.com/v1/models', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $api_key,
                ],
                'timeout' => $validation_timeout
            ]);

            if (is_wp_error($response)) {
                /** @var \WP_Error $response */
                throw new \Exception(
                    sprintf(
                        /* translators: %s: Error message from API request */
                        __( 'OpenAI API request failed: %s', 'ai-product-tools' ),
                        $response->get_error_message()
                    )
                );
            }

            $response_code = wp_remote_retrieve_response_code($response);
            $body_str = (string) wp_remote_retrieve_body($response);
            $body = json_decode($body_str, true);

            if (json_last_error() !== JSON_ERROR_NONE || !is_array($body)) {
                throw new \Exception(
                    sprintf(
                        /* translators: %s: JSON error message */
                        __('Invalid response from OpenAI API: %s', 'ai-product-tools'),
                        json_last_error_msg()
                    )
                );
            }

            if ($response_code === 200 && !empty($body['data'])) {
                return true;
            }

            if (isset($body['error']['message'])) {
                throw new \Exception($body['error']['message']);
            }

            throw new \Exception(__('Invalid OpenAI API key', 'ai-product-tools'));

        } catch (\Exception $e) {
            throw $e;
        }
    }

    public function get_available_models(): array {
        return $this->models;
    }
    
    /**
     * API istekleri için parametre ayarlarını yapar
     * 
     * @param array $parameters Ayarlanacak parametreler
     * @return void
     */
    public function setParameters(array $parameters): void {
        if (empty($parameters)) {
            return;
        }
        
        try {

            foreach ($this->models as $model_id => &$model_config) {

                if (isset($parameters['temperature']) && is_numeric($parameters['temperature'])) {
                    $temperature = floatval($parameters['temperature']);

                    if ($temperature >= 0.0 && $temperature <= 2.0) {
                        if (!isset($model_config['has_temperature']) || $model_config['has_temperature'] === true) {
                            $model_config['temperature'] = $temperature;
                        }
                    } else {
                    }
                }
                

                if (isset($parameters['top_p']) && is_numeric($parameters['top_p'])) {
                    $topP = floatval($parameters['top_p']);

                    if ($topP >= 0.0 && $topP <= 1.0) {
                        $model_config['top_p'] = $topP;
                    } else {
                    }
                }
                

                if (isset($model_config['use_legacy_params']) && $model_config['use_legacy_params']) {
                    if (isset($parameters['frequency_penalty']) && is_numeric($parameters['frequency_penalty'])) {
                        $freqPenalty = floatval($parameters['frequency_penalty']);

                        if ($freqPenalty >= -2.0 && $freqPenalty <= 2.0) {
                            $model_config['frequency_penalty'] = $freqPenalty;
                        } else {
                        }
                    }
                    
                    if (isset($parameters['presence_penalty']) && is_numeric($parameters['presence_penalty'])) {
                        $presPenalty = floatval($parameters['presence_penalty']);

                        if ($presPenalty >= -2.0 && $presPenalty <= 2.0) {
                            $model_config['presence_penalty'] = $presPenalty;
                        } else {
                        }
                    }
                }
                

            }
        } catch (\Exception $e) {

        }
    }
} 
