<?php
/**
 * InstagentHelpService — plugin-side AJAX bridge for Help mode.
 *
 * The editor's Help-mode chat panel calls this via the existing AJAX
 * dispatcher (`action=instinctor_help_ask`). We forward to the
 * platform's /api/relay/ai-help endpoint with the install's license
 * key, then return the answer to the editor.
 *
 * Why a server-side bridge: the license key never appears in the
 * browser network tab or DOM. Same model as SectionGeneratorService.
 *
 * Backend lives at platform.instinctor.com — see
 * platform_v2/includes/Controllers/HelpController.php.
 */

namespace Instinctor\Services;

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

class InstagentHelpService
{
    private const RELAY_URL       = 'https://platform.instinctor.com/api/relay/ai-help';
    private const RELAY_CHUNK_URL = 'https://platform.instinctor.com/api/relay/ai-help-chunk';
    private const TIMEOUT_S       = 30;

    private static ?self $instance = null;

    public static function instance(): self
    {
        return self::$instance ??= new self();
    }

    private function __construct()
    {
        $isWordPress = !defined('INSTINCTOR_ENV') || INSTINCTOR_ENV === 'wordpress';
        if ($isWordPress) {
            \add_action('wp_ajax_instinctor_help_ask',   [$this, 'ajaxAsk']);
            \add_action('wp_ajax_instinctor_help_chunk', [$this, 'ajaxChunk']);
        } else {
            \Instinctor\Core\AjaxDispatcher::register('instinctor_help_ask',   [$this, 'ajaxAsk']);
            \Instinctor\Core\AjaxDispatcher::register('instinctor_help_chunk', [$this, 'ajaxChunk']);
        }
    }

    public function ajaxAsk(): void
    {
        // WP.org B1 — nonce + capability check. Without these, any logged-in
        // user could CSRF the relay or burn the site's licensed AI quota.
        if (!\is_user_logged_in() || !\current_user_can('edit_posts')) {
            \wp_send_json(['success' => false, 'error' => 'forbidden'], 403);
            return;
        }
        if (!\check_ajax_referer('instinctor_help_ask', 'nonce', false)) {
            \wp_send_json(['success' => false, 'error' => 'bad_nonce'], 403);
            return;
        }
        // WP.org A2 — platform consent (the relay forwards site_url + key).
        if (!\Instinctor\Core\PlatformConsent::granted()) {
            \wp_send_json(['success' => false, 'error' => 'platform_consent_required'], 403);
            return;
        }
        $question = trim(\sanitize_textarea_field(\wp_unslash((string) ($_POST['question'] ?? ''))));
        if ($question === '') {
            \wp_send_json(['success' => false, 'error' => 'missing_question'], 400);
            return;
        }
        if (mb_strlen($question) > 1000) {
            \wp_send_json(['success' => false, 'error' => 'question_too_long'], 400);
            return;
        }

        // Context: only the keys we want to forward. Everything else is
        // dropped to keep the payload small and predictable.
        $context = [
            'page_mode'        => \sanitize_text_field(\wp_unslash((string) ($_POST['page_mode']        ?? ''))),
            'selected_block'   => \sanitize_text_field(\wp_unslash((string) ($_POST['selected_block']   ?? ''))),
            'active_tab'       => \sanitize_text_field(\wp_unslash((string) ($_POST['active_tab']       ?? ''))),
            'selected_control' => \sanitize_text_field(\wp_unslash((string) ($_POST['selected_control'] ?? ''))),
            'page_slug'        => \sanitize_key(\wp_unslash((string) ($_POST['page_slug'] ?? ''))),
        ];

        // License key sourced same way as AIContentService — primary key,
        // then addon-license fallbacks. Empty is OK — platform will use
        // free-domain auth on instinctor.com itself.
        $licenses   = \Instinctor\Core\Config::get('instinctor_addon_licenses', []);
        $licenseKey = \Instinctor\Core\Config::get('instinctor_license_key', '')
                   ?: ($licenses['ai_content']['key'] ?? $licenses['all_access']['key'] ?? '');
        $siteUrl    = function_exists('home_url') ? \home_url() : '';

        $body = json_encode([
            'question'    => $question,
            'context'     => $context,
            'license_key' => $licenseKey,
            'site_url'    => $siteUrl,
        ], JSON_UNESCAPED_SLASHES);

        $response = \wp_remote_post(self::RELAY_URL, [
            'headers' => ['Content-Type' => 'application/json'],
            'body'    => $body,
            'timeout' => self::TIMEOUT_S,
        ]);

        if (\is_wp_error($response)) {
            // phpcs:disable WordPress.PHP.DevelopmentFunctions -- server-side error capture / one-time migration logging, not debug output.
            \error_log('[Instagent Help] relay error: ' . $response->get_error_message());
            // phpcs:enable WordPress.PHP.DevelopmentFunctions
            \wp_send_json([
                'success' => false,
                'error'   => 'relay_unreachable',
                'message' => "I couldn't reach the help service. Try again in a moment, or open Settings → Contact Support.",
            ], 502);
            return;
        }

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

        if (!is_array($data)) {
            // phpcs:disable WordPress.PHP.DevelopmentFunctions -- server-side error capture / one-time migration logging, not debug output.
            \error_log('[Instagent Help] non-JSON relay response: ' . substr((string) $raw, 0, 300));
            // phpcs:enable WordPress.PHP.DevelopmentFunctions
            \wp_send_json([
                'success' => false,
                'error'   => 'relay_invalid',
                'message' => 'I got an unexpected response from the help service. Try again.',
            ], 502);
            return;
        }

        // Pass through whatever the platform returned. The HelpController
        // produces the same {success, answer, sources} or {success: false,
        // error, message} shape, so the editor can read it directly.
        \wp_send_json($data, $code ?: 200);
    }

    /**
     * Fetch a single KB article by slug — no AI call, just KB lookup.
     * Used by the editor's empty-state browse list when a topic is
     * clicked, expanding the verified KB content inline.
     */
    public function ajaxChunk(): void
    {
        // WP.org B1 — same hardening as ajaxAsk above.
        if (!\is_user_logged_in() || !\current_user_can('edit_posts')) {
            \wp_send_json(['success' => false, 'error' => 'forbidden'], 403);
            return;
        }
        if (!\check_ajax_referer('instinctor_help_chunk', 'nonce', false)) {
            \wp_send_json(['success' => false, 'error' => 'bad_nonce'], 403);
            return;
        }
        if (!\Instinctor\Core\PlatformConsent::granted()) {
            \wp_send_json(['success' => false, 'error' => 'platform_consent_required'], 403);
            return;
        }
        $slug = trim(\sanitize_text_field(\wp_unslash((string) ($_POST['slug'] ?? ''))));
        if ($slug === '' || !preg_match('#^[a-z]+/[a-z0-9\-]+$#', $slug)) {
            \wp_send_json(['success' => false, 'error' => 'invalid_slug'], 400);
            return;
        }

        $licenses   = \Instinctor\Core\Config::get('instinctor_addon_licenses', []);
        $licenseKey = \Instinctor\Core\Config::get('instinctor_license_key', '')
                   ?: ($licenses['ai_content']['key'] ?? $licenses['all_access']['key'] ?? '');
        $siteUrl    = function_exists('home_url') ? \home_url() : '';

        $body = json_encode([
            'slug'        => $slug,
            'license_key' => $licenseKey,
            'site_url'    => $siteUrl,
        ], JSON_UNESCAPED_SLASHES);

        $response = \wp_remote_post(self::RELAY_CHUNK_URL, [
            'headers' => ['Content-Type' => 'application/json'],
            'body'    => $body,
            'timeout' => self::TIMEOUT_S,
        ]);

        if (\is_wp_error($response)) {
            \wp_send_json(['success' => false, 'error' => 'relay_unreachable',
                'message' => "I couldn't reach the help service. Try again."], 502);
            return;
        }

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

        if (!is_array($data)) {
            \wp_send_json(['success' => false, 'error' => 'relay_invalid'], 502);
            return;
        }

        \wp_send_json($data, $code ?: 200);
    }
}
