<?php
/**
 * Costudio_OpenAI_Adapter — OpenAI Chat Completions API adapter.
 *
 * @package Contentosapp_Studio
 * @since   3.0.0
 */

defined( 'ABSPATH' ) || exit;
// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception messages go to PHP error log, never to HTML output.

class Costudio_OpenAI_Adapter implements Costudio_AI_Client {

    const API_URL = 'https://api.openai.com/v1/chat/completions';

    private string $api_key;
    private string $model;

    /** Optional per-agent temperature override (null = provider default). */
    private ?float $temperature = null;

    /** Set to true after one fallback retry to prevent infinite loops. */
    private bool $fallback_attempted = false;

    /** Durable-worker hooks. They are unset for the existing synchronous path. */
    private $before_dispatch_callback = null;
    private $response_received_callback = null;
    private ?int $http_timeout = null;
    private bool $model_fallback_enabled = true;

    /** @var array Last API call usage metadata. */
    private array $last_usage = [ 'input_tokens' => 0, 'output_tokens' => 0 ];

    const DEFAULT_MODEL = 'gpt-4o';

    public function __construct( string $api_key, string $model = '' ) {
        if ( empty( $model ) ) {
            $model = self::DEFAULT_MODEL;
        }
        $this->api_key = $api_key;
        $this->model   = $model;
    }

    public function set_temperature( ?float $temperature ): void {
        $this->temperature = $temperature;
    }

    public function set_durable_transport_hooks( callable $before_dispatch, callable $response_received ): void {
        $this->before_dispatch_callback  = $before_dispatch;
        $this->response_received_callback = $response_received;
    }

    public function set_http_timeout( int $seconds ): void {
        $this->http_timeout = max( 1, $seconds );
    }

    public function disable_model_fallback(): void {
        $this->model_fallback_enabled = false;
    }

    // ── Public interface ─────────────────────────────────────────────────────

    public function generate( string $prompt, array $context = [] ): string {
        return $this->call_api( $prompt, $context );
    }

    public function generate_via_agent( int $agent_id, array $inputs, array $context = [] ): string {
        throw new LogicException( 'Secure agent execution is only available via ContentOS Cloud adapter.' );
    }

    public function generateWithSearch( string $prompt, array $context = [] ): string {
        $search_engine = new Costudio_Search_Engine();
        $query         = mb_substr( wp_strip_all_tags( $prompt ), 0, 160 );
        $results       = $search_engine->search( $query );

        if ( ! empty( $results ) ) {
            $prompt = "=== RESULTADOS DE BUSCA NA WEB ===\n\n{$results}\n\n=== INSTRUÇÃO ===\n\n{$prompt}";
        }

        return $this->call_api( $prompt, $context );
    }

    public function get_model(): string {
        return $this->model;
    }

    /**
     * Get token usage from the last API call.
     *
     * @return array { input_tokens: int, output_tokens: int }
     */
    public function get_last_usage(): array {
        return $this->last_usage;
    }

    // ── Internal ─────────────────────────────────────────────────────────────

    private function call_api( string $prompt, array $context ): string {
        $messages = [];

        foreach ( $context as $turn ) {
            $role       = ( $turn['role'] ?? 'user' ) === 'model' ? 'assistant' : 'user';
            $messages[] = [ 'role' => $role, 'content' => $turn['content'] ];
        }

        $messages[] = [ 'role' => 'user', 'content' => $prompt ];

        $payload = [
            'model'      => $this->model,
            'messages'   => $messages,
            'max_tokens' => 4096,
        ];
        if ( null !== $this->temperature ) {
            $payload['temperature'] = max( 0.0, min( 2.0, $this->temperature ) );
        }

        $this->notify_before_dispatch();
        $response = wp_remote_post( self::API_URL, [
            'timeout' => $this->http_timeout ?? 120,
            'headers' => [
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode( $payload ),
        ] );

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

        $this->notify_response_received();

        $code = (int) wp_remote_retrieve_response_code( $response );
        $data = json_decode( wp_remote_retrieve_body( $response ), true );

        if ( $code !== 200 ) {
            $msg = $data['error']['message'] ?? wp_remote_retrieve_body( $response );

            // One-shot fallback when the configured model is rejected as
            // unknown/deprecated — retry with the adapter's safe default.
            if ( $this->model_fallback_enabled
                 && ! $this->fallback_attempted
                 && $this->model !== self::DEFAULT_MODEL
                 && class_exists( 'Costudio_Model_Fallback' )
                 && Costudio_Model_Fallback::is_deprecated_model_error( $code, (string) $msg ) ) {

                $this->fallback_attempted = true;
                Costudio_Model_Fallback::record( 'openai', $this->model, self::DEFAULT_MODEL, (string) $msg );
                $this->model = self::DEFAULT_MODEL;
                return $this->call_api( $prompt, $context );
            }

            throw new Exception( "OpenAI API HTTP {$code}: {$msg}" );
        }

        // Extract token usage from response.
        if ( ! empty( $data['usage'] ) ) {
            $this->last_usage = [
                'input_tokens'  => (int) ( $data['usage']['prompt_tokens'] ?? 0 ),
                'output_tokens' => (int) ( $data['usage']['completion_tokens'] ?? 0 ),
            ];
        }

        return $data['choices'][0]['message']['content'] ?? '';
    }

    public function generate_streaming( string $prompt, array $context, callable $on_chunk ): string {
        $result = $this->call_api( $prompt, $context );
        $on_chunk( $result );
        return $result;
    }

    private function notify_before_dispatch(): void {
        if ( is_callable( $this->before_dispatch_callback ) ) {
            call_user_func( $this->before_dispatch_callback );
        }
    }

    private function notify_response_received(): void {
        if ( is_callable( $this->response_received_callback ) ) {
            call_user_func( $this->response_received_callback );
        }
    }
}
