<?php
/**
 * OpenAI-Compatible API Client
 *
 * Supports any provider using the OpenAI Chat Completions + Vision API format,
 * including: OpenAI, Groq, Together AI, Ollama, LM Studio, etc.
 *
 * @package PackshotPhotoAI
 */

namespace PackshotPhotoAI\API;

use PackshotPhotoAI\Core\Logger;

defined('ABSPATH') || exit;

/**
 * Handles communication with any OpenAI-compatible API endpoint
 */
class OpenAIClient implements ClientInterface {

    /**
     * Request timeout in seconds
     */
    private const TIMEOUT = 60;

    /**
     * @var string API endpoint base URL
     */
    private string $endpoint;

    /**
     * @var string Model name
     */
    private string $model;

    /**
     * @var string API key (may be empty for local endpoints)
     */
    private string $api_key;

    /**
     * Constructor
     *
     * @param string $endpoint Full URL to the chat/completions endpoint
     * @param string $model    Model name
     * @param string $api_key  API key (optional for local providers)
     */
    public function __construct(string $endpoint = '', string $model = '', string $api_key = '') {
        $this->endpoint = $endpoint ?: get_option('packshotphoto_dev_api_endpoint', '');
        $this->model    = $model    ?: get_option('packshotphoto_dev_model', 'gpt-4o');
        $this->api_key  = $api_key  ?: \PackshotPhotoAI\Core\Encryption::decrypt(get_option('packshotphoto_dev_api_key', ''));

        // Normalize endpoint — ensure it points to /chat/completions
        if (!empty($this->endpoint) && !str_ends_with(rtrim($this->endpoint, '/'), '/chat/completions')) {
            $this->endpoint = rtrim($this->endpoint, '/') . '/chat/completions';
        }
    }

    /**
     * Build request headers
     *
     * @return array
     */
    private function get_headers(): array {
        $headers = ['Content-Type' => 'application/json'];

        if (!empty($this->api_key)) {
            $headers['Authorization'] = 'Bearer ' . $this->api_key;
        }

        return $headers;
    }

    /**
     * Get language from settings
     *
     * @return string
     */
    private function get_language(): string {
        $settings = get_option('packshotphoto_settings', []);
        $lang     = $settings['language'] ?? 'auto';

        if ($lang === 'auto') {
            return get_locale();
        }

        return $lang;
    }

    /**
     * Test API connection
     *
     * @return array{success: bool, message: string}
     */
    public function test_connection(): array {
        if (empty($this->endpoint)) {
            return [
                'success' => false,
                'message' => __('Missing API endpoint URL', 'packshotphoto-ai'),
            ];
        }

        $payload = [
            'model'    => $this->model,
            'messages' => [
                ['role' => 'user', 'content' => 'Reply with exactly one word: OK'],
            ],
            'max_tokens' => 10,
        ];

        Logger::debug('OpenAI API: Connection test request', ['url' => $this->endpoint, 'model' => $this->model, 'payload' => $payload]);

        $response = wp_remote_post($this->endpoint, [
            'headers' => $this->get_headers(),
            'body'    => wp_json_encode($payload),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            Logger::error('OpenAI API: WP Error during connection test', ['error' => $response->get_error_message()]);
            return [
                'success' => false,
                'message' => $response->get_error_message(),
            ];
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        
        Logger::debug('OpenAI API: Connection test response', ['code' => $code, 'body' => $body]);

        if ($code !== 200) {
            Logger::error('OpenAI API: Connection test failed', ['code' => $code, 'body' => $body]);
            return [
                'success' => false,
                'message' => 'API Error (' . $code . '): ' . $body,
            ];
        }

        return [
            'success' => true,
            'message' => __('Connection working correctly!', 'packshotphoto-ai'),
        ];
    }

    /**
     * Generate SEO description for media file
     *
     * @param string $file_path Absolute path to file
     * @param string $mime_type MIME type
     * @param int    $post_id   Optional attachment ID (not used in OpenAI client)
     * @param string $language  Optional target language override
     * @return array{success: bool, message?: string, data?: array}
     */
    public function generate_seo_description(string $file_path, string $mime_type, int $post_id = 0, string $language = '', string $image_context = ''): array {
        if (empty($this->endpoint)) {
            return [
                'success' => false,
                'message' => __('Missing API endpoint URL', 'packshotphoto-ai'),
            ];
        }

        $file_data = @file_get_contents($file_path);

        if (false === $file_data) {
            return [
                'success' => false,
                'message' => __('Cannot read file', 'packshotphoto-ai'),
            ];
        }

        $base64_data = base64_encode($file_data);
        $data_uri    = 'data:' . $mime_type . ';base64,' . $base64_data;

        $language  = (!empty($language) && 'auto' !== $language) ? $language : $this->get_language();
        $lang_name = $this->map_language_name($language);
        $prompt    = $this->build_seo_prompt($lang_name, $image_context);

        $payload = [
            'model'      => $this->model,
            'messages'   => [
                [
                    'role'    => 'user',
                    'content' => [
                        ['type' => 'text',      'text'      => $prompt],
                        ['type' => 'image_url', 'image_url' => ['url' => $data_uri]],
                    ],
                ],
            ],
            'max_tokens'      => 512,
            'temperature'     => 0.9,
            'response_format' => ['type' => 'json_object'],
        ];

        Logger::debug('OpenAI API: SEO generation request', ['url' => $this->endpoint, 'model' => $this->model, 'payload' => $payload]);

        $response = wp_remote_post($this->endpoint, [
            'headers' => $this->get_headers(),
            'body'    => wp_json_encode($payload),
            'timeout' => self::TIMEOUT,
        ]);

        if (is_wp_error($response)) {
            Logger::error('OpenAI API: WP Error during generation', ['error' => $response->get_error_message()]);
            return [
                'success' => false,
                'message' => $response->get_error_message(),
                'data'    => [],
            ];
        }
        $code     = wp_remote_retrieve_response_code($response);
        $raw_body = wp_remote_retrieve_body($response);
        $body     = json_decode($raw_body, true);

        Logger::debug('OpenAI API: SEO generation response', ['code' => $code, 'body' => $raw_body]);

        if ($code !== 200) {
            $error = $body['error']['message'] ?? __('API Error', 'packshotphoto-ai');
            Logger::error('OpenAI API: SEO generation failed', ['code' => $code, 'body' => $raw_body]);
            return ['success' => false, 'message' => $error];
        }

        $content = $body['choices'][0]['message']['content'] ?? '';
        $data    = json_decode($content, true);

        if (!$data || !isset($data['alt'])) {
            Logger::error('OpenAI API: Invalid AI response for SEO generation', ['content' => $content, 'raw_body' => $raw_body]);
            return [
                'success' => false,
                'message' => __('Invalid AI response — check if model supports vision and JSON mode', 'packshotphoto-ai'),
            ];
        }

        return [
            'success' => true,
            'message' => __('SEO metadata generated.', 'packshotphoto-ai'),
            'data'    => $data,
        ];
    }

    /**
     * Summarize site context from homepage text
     *
     * @param string $text     Homepage text content
     * @param string $language Optional target language override
     * @return array{success: bool, message?: string, data?: string}
     */
    public function summarize_site_context(string $text, string $language = ''): array {
        if (empty($this->endpoint)) {
            return ['success' => false, 'message' => __('Missing API endpoint URL', 'packshotphoto-ai')];
        }

        $language  = (!empty($language) && 'auto' !== $language) ? $language : $this->get_language();
        $lang_name = $this->map_language_name($language);

        $prompt = sprintf(
            '### STRICT LANGUAGE REQUIREMENT:
- You MUST generate the summary ONLY in %1$s.
- Even if the website content below is in another language, your response MUST be in %1$s.

Analyze the following website content and write a SHORT description (2-3 sentences) explaining what this company/website does.
This description will be used as SEO context.

Website content:
%2$s',
            $lang_name,
            mb_substr($text, 0, 3000)
        );

        $payload = [
            'model'      => $this->model,
            'messages'   => [
                [
                    'role'    => 'system',
                    'content' => sprintf('STRICT LANGUAGE MANDATE: You MUST reply ONLY in %s. No exceptions.', $lang_name)
                ],
                ['role' => 'user', 'content' => $prompt],
            ],
            'max_tokens' => 300,
        ];

        Logger::debug('OpenAI API: Site context summary request', ['url' => $this->endpoint, 'model' => $this->model, 'payload' => $payload]);

        $response = wp_remote_post($this->endpoint, [
            'headers' => $this->get_headers(),
            'body'    => wp_json_encode($payload),
            'timeout' => 30,
        ]);

        if (is_wp_error($response)) {
            Logger::error('OpenAI API: WP Error during site context summary', ['error' => $response->get_error_message()]);
            return ['success' => false, 'message' => $response->get_error_message()];
        }

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

        Logger::debug('OpenAI API: Site context summary response', ['code' => $code, 'body' => $body]);

        if ($code !== 200) {
            $error = $body['error']['message'] ?? __('API Error', 'packshotphoto-ai');
            Logger::error('OpenAI API: Site context summary failed', ['code' => $code, 'body' => $body]);
            return ['success' => false, 'message' => $error];
        }

        $result = $body['choices'][0]['message']['content'] ?? '';

        return [
            'success' => true,
            'message' => __('Site context summarized successfully.', 'packshotphoto-ai'),
            'data'    => trim($result),
        ];
    }

    /**
     * Build SEO prompt — respects custom user prompt if set
     *
     * @param string $language Language code or locale
     * @return string
     */
    private function build_seo_prompt(string $language, string $image_context = ''): string {
        $lang_instruction = "### LANGUAGE MANDATE (CRITICAL):
- All generated fields ('slug', 'alt', 'caption', 'desc') MUST be written in: {$language}
- You must write natively and naturally in that target language. Even if the website context or prompts below are in English, your entire JSON response fields must be in {$language}. Do not translate field names (keys), only the values.";

        $site_context     = get_option('packshotphoto_site_context', '');
        $context_keywords = '';
        if (!empty($site_context)) {
            $context_keywords = '
 
### INDUSTRY CONTEXT (use as inspiration for tone, style, and terminology):
' . $site_context;
        }

        $image_context_section = '';
        if (!empty($image_context)) {
            $image_context_section = '
 
### SPECIFIC IMAGE CONTEXT (user-provided — you MUST incorporate these facts or style directions in the generated metadata):
' . $image_context;
        }

        // Editorial angle rotation — forces model to vary perspective per image
        $angles = [
            'materials, finish and craftsmanship',
            'setting, backdrop and overall atmosphere',
            'practical use and placement in everyday life',
            'design style and what it pairs with',
            'color palette, lighting and composition',
            'proportions, scale and form',
        ];
        $angle_block = sprintf(
            "\n### EDITORIAL ANGLE FOR THIS IMAGE:\nEmphasize: %s. Build the caption and desc around this angle, staying truthful to what is visible.\n",
            $angles[array_rand($angles)]
        );

        // Recent openers memory — prevent repetitive openings
        $recent       = (array) get_option('packshotphoto_recent_openers', []);
        $recent_block = '';
        if (!empty($recent)) {
            $recent_block = "\n### RECENTLY USED OPENINGS — do NOT reuse or closely paraphrase any of them:\n";
            foreach (array_slice($recent, -30) as $opener) {
                $recent_block .= '- ' . $opener . "\n";
            }
        }

        return $lang_instruction . '
 
You are an Image Metadata Specialist. Your task is to perform a detailed visual analysis of the provided image and generate factual, search-optimized metadata.
' . $context_keywords . $image_context_section . $angle_block . $recent_block . '
 
⚠️ BANNED PATTERNS (NEVER use — in ANY language):
- Imperative/promotional phrases: "Elevate your…", "Transform your…", "Enhance your…", "Discover…", "Experience…", "Add a touch of…"
- Template phrases: "featuring…", "with this…", "perfect for…", "ideal for…"
- Photo-referencing phrases: "professional photography", "studio photo", "presents", "shows", "the photo depicts", "the image shows", "product photography"
- Any second-person address ("your", "you") or calls to action
- Any references to the process of taking the photo or the studio
 
⚠️ BALANCED CONTEXT ALIGNMENT (CRITICAL):
- Your absolute priority is to describe the visual elements and objects actually present in the image. Do not invent or force industry terms if they have no visual connection to the image.
- Use the INDUSTRY CONTEXT as a guide for tone, vocabulary style, and to resolve ambiguous visual scenes.
- If the image shows a neutral or unrelated subject (e.g., a cup of coffee, a flower, a book), describe that subject naturally. Do NOT force-feed industry keywords.
- Ensure each image gets unique, natural metadata.
 
### FIELD-SPECIFIC SEO RULES:
 
### 1. SLUG (URL & Physical Filename SEO)
- This slug will be used directly to rename the image file on the server.
- Max 60 characters, only lowercase a-z, 0-9, and hyphens (-).
- Must contain 3 to 5 highly relevant keywords. Never use underscores or spaces.
- No special characters, diacritics, or accents (normalize them, e.g. "ł" -> "l", "ó" -> "o").
- Exclude all stop words and conjunctions (e.g. and, or, in, na, z, do, dla).
- Format: [primary-keyword]-[secondary-keyword]-[context]
 
### 2. ALT (Accessibility & Image Search)
- Max 125 characters. Crucial for screen readers and search engine indexers.
- START DIRECTLY with the specific visible object, color, material, and context (e.g. "Skórzane brązowe buty męskie na drewnianym tle").
- Never use intros like "photo of" or "image showing".
- Describe only what is clearly visible. Do not guess unseen attributes.
 
### 3. CAPTION (Page Content Placement)
- 80-130 characters, exactly 1 sentence.
- Before writing, identify 3 visual details unique to THIS image (e.g. specific color, texture, shape, arrangement). Weave at least 2 of them into the sentence.
- Write a factual, editorial observation — as if a journalist is describing the scene for a print article.
- NEVER start with an article ("A", "The") or a gerund ("Featuring", "Showcasing"). Start with a concrete noun, adjective, or the object name.
- Must NOT overlap with ALT text by more than 3 words.
 
### 4. DESC (Rich Search Snippet / Meta Description)
- 200-320 characters, 2-3 sentences.
- Sentence 1: Describe the primary subject with 2-3 physical attributes not mentioned in ALT or CAPTION (material, dimensions, texture, condition, spatial relationship).
- Sentence 2: State a factual, observable detail about setting, context, or composition.
- Sentence 3 (optional): A neutral, informational note about typical use or category — no promotional language.
- NEVER use imperative mood, second person ("your"), or benefit claims ("perfect for", "ideal for", "adds elegance").
- Must have zero text duplication with the alt or caption fields.
 
### EXPECTED OUTPUT FORMAT:
You must return ONLY a valid JSON object. Do not wrap in markdown code blocks, do not write any pre-text or post-text.
 
JSON structure template:
{
  "slug": "optimized-image-slug",
  "alt": "Alt text in the target language starting with the object",
  "caption": "A single editorial sentence in the target language.",
  "desc": "A detailed 2-3 sentence factual SEO description in the target language."
}';
    }

    /**
     * Map language code or locale to full language name
     *
     * @param string $language Code (e.g., 'pl', 'en_US')
     * @return string
     */
    private function map_language_name(string $language): string {
        $language = strtolower($language);
        
        $map = [
            'pl' => 'Polish',
            'en' => 'English',
            'es' => 'Spanish',
            'de' => 'German',
            'fr' => 'French',
            'it' => 'Italian',
            'pt' => 'Portuguese',
            'nl' => 'Dutch',
            'ru' => 'Russian',
            'zh' => 'Chinese (Simplified)',
            'ja' => 'Japanese',
            'ko' => 'Korean',
        ];

        // Try exact match first
        if (isset($map[$language])) {
            return $map[$language];
        }

        // Try prefix (e.g., 'pl_PL' -> 'pl')
        $prefix = substr($language, 0, 2);
        return $map[$prefix] ?? 'English';
    }
}
