<?php
/**
 * Indexing Backend Client (Option B).
 *
 * Routes indexing-time AI calls through the backend so the WP plugin
 * never stores or exposes vendor API keys (Gemini/OpenAI/Voyage).
 *
 * @package BoostBot
 * @since 1.7.0
 */

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

class BoostBot_Indexing_Backend_Client {

    /**
     * @var BoostBot_Credits_Backend_Client
     */
    private $credits_client;

    public function __construct() {
        $this->credits_client = new BoostBot_Credits_Backend_Client();
    }

    /**
     * Summarize content via backend (Gemini).
     *
     * @param array $content_data {title, content, type}
     * @return array|WP_Error {summary, keywords, intent} on success
     */
    public function summarize( array $content_data ) {
        $site_id = $this->credits_client->ensure_site_id();
        if ( is_wp_error( $site_id ) ) {
            return $site_id;
        }

        $payload = array(
            'title'   => (string) ( $content_data['title'] ?? '' ),
            'content' => (string) ( $content_data['content'] ?? '' ),
            'type'    => (string) ( $content_data['type'] ?? 'page' ),
        );

        $path = '/api/v1/sites/' . rawurlencode( (string) $site_id ) . '/indexing/summarize';

        $resp = $this->raw_request( 'POST', $path, $payload );
        if ( is_wp_error( $resp ) ) {
            return $resp;
        }

        $result = $resp['result'] ?? null;
        if ( ! is_array( $result ) ) {
            return new WP_Error( 'boostbot_backend_bad_response', 'Invalid summarize response (missing result).' );
        }

        return array(
            'summary'  => (string) ( $result['summary'] ?? '' ),
            'keywords' => is_array( $result['keywords'] ?? null ) ? $result['keywords'] : array(),
            'intent'   => (string) ( $result['intent'] ?? 'general' ),
        );
    }

    /**
     * Batch-summarize multiple content items via backend (Gemini).
     *
     * Sends up to 10 items in a single request. The backend processes them
     * in parallel using Promise.allSettled(), which is significantly faster
     * than sequential single-item calls.
     *
     * @since 1.17.0
     * @param array $items Array of { id, title, content, type } arrays.
     * @return array|WP_Error Keyed array of { summary, keywords, intent } per item id, or WP_Error.
     */
    public function summarize_batch( array $items ) {
        $site_id = $this->credits_client->ensure_site_id();
        if ( is_wp_error( $site_id ) ) {
            return $site_id;
        }

        if ( empty( $items ) ) {
            return array();
        }

        // Normalize items for the API.
        $api_items = array();
        foreach ( $items as $item ) {
            $api_items[] = array(
                'id'      => (string) ( $item['id'] ?? '' ),
                'title'   => (string) ( $item['title'] ?? '' ),
                'content' => (string) ( $item['content'] ?? '' ),
                'type'    => (string) ( $item['type'] ?? 'page' ),
            );
        }

        $payload = array( 'items' => $api_items );

        $path = '/api/v1/sites/' . rawurlencode( (string) $site_id ) . '/indexing/summarize-batch';

        $resp = $this->raw_request( 'POST', $path, $payload, 45 );
        if ( is_wp_error( $resp ) ) {
            return $resp;
        }

        $results = $resp['results'] ?? null;
        if ( ! is_array( $results ) ) {
            return new WP_Error( 'boostbot_backend_bad_response', 'Invalid summarize-batch response (missing results).' );
        }

        // Normalize each result.
        $output = array();
        foreach ( $results as $id => $result ) {
            if ( ! is_array( $result ) || isset( $result['error'] ) ) {
                continue; // Skip failed items; caller will fall back to single summarize.
            }
            $output[ (string) $id ] = array(
                'summary'  => (string) ( $result['summary'] ?? '' ),
                'keywords' => is_array( $result['keywords'] ?? null ) ? $result['keywords'] : array(),
                'intent'   => (string) ( $result['intent'] ?? 'general' ),
            );
        }

        return $output;
    }

    /**
     * Embed texts via backend (OpenAI embeddings).
     *
     * @param array $texts Array of strings.
     * @param string $model
     * @return array|WP_Error Array of embeddings (arrays) in same order.
     */
    public function embed( array $texts, string $model = 'text-embedding-3-small' ) {
        $site_id = $this->credits_client->ensure_site_id();
        if ( is_wp_error( $site_id ) ) {
            return $site_id;
        }

        $payload = array(
            'texts' => array_values( array_map( 'strval', $texts ) ),
            'model' => $model,
        );

        $path = '/api/v1/sites/' . rawurlencode( (string) $site_id ) . '/indexing/embed';

        $resp = $this->raw_request( 'POST', $path, $payload );
        if ( is_wp_error( $resp ) ) {
            return $resp;
        }

        $embeddings = $resp['embeddings'] ?? null;
        if ( ! is_array( $embeddings ) ) {
            return new WP_Error( 'boostbot_backend_bad_response', 'Invalid embed response (missing embeddings).' );
        }

        return $embeddings;
    }

    /**
     * Backend request helper (same envelope as credits backend).
     *
     * @param string $method
     * @param string $path
     * @param array|null $body
     * @param int $timeout Optional timeout in seconds (default: 15).
     * @return array|WP_Error
     */
    private function raw_request( string $method, string $path, ?array $body = null, int $timeout = 15 ) {
        $base = $this->credits_client->get_base_url();
        if ( empty( $base ) ) {
            return new WP_Error( 'boostbot_backend_not_configured', 'Backend not configured (missing backend_base_url).' );
        }

        $url = $base . $path;

        $raw_body = '';
        if ( null !== $body ) {
            $raw_body = wp_json_encode( $body );
            if ( false === $raw_body ) {
                return new WP_Error( 'boostbot_backend_invalid_json', 'Failed to encode request body.' );
            }
        }

        $headers = array(
            'Accept'       => 'application/json',
            'Content-Type' => 'application/json',
        );

        $site_key = (string) BoostBot_Settings::get( 'backend_site_api_key', '' );
        $site_key = trim( $site_key );
        if ( ! empty( $site_key ) ) {
            $headers['Authorization'] = 'Bearer ' . $site_key;
        }

        $args = array(
            'method'  => $method,
            'timeout' => max( 5, $timeout ),
            'headers' => $headers,
        );
        if ( null !== $body ) {
            $args['body'] = $raw_body;
        }

        $res = wp_remote_request( $url, $args );
        if ( is_wp_error( $res ) ) {
            return $res;
        }

        $status = (int) wp_remote_retrieve_response_code( $res );
        $text   = (string) wp_remote_retrieve_body( $res );

        $json = json_decode( $text, true );
        if ( ! is_array( $json ) ) {
            return new WP_Error( 'boostbot_backend_bad_response', 'Invalid backend response.' );
        }

        if ( isset( $json['success'] ) && false === $json['success'] ) {
            $msg = isset( $json['error']['message'] ) ? (string) $json['error']['message'] : 'Backend error';
            return new WP_Error( 'boostbot_backend_error', $msg, array( 'http_status' => $status, 'raw' => $json ) );
        }

        return $json['data'] ?? $json;
    }
}


