<?php
/**
 * REST API Endpoints
 *
 * @package ReplyIt_Chat_Widget
 */

// Exit if accessed directly
if (!defined('ABSPATH')) {
    exit;
}

class ReplyIt_REST_API {

    /**
     * Single instance
     */
    private static $instance = null;

    /**
     * Namespace for REST API
     */
    private $namespace = 'replyit/v1';

    /**
     * Get instance
     */
    public static function instance() {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor
     */
    private function __construct() {
        add_action('rest_api_init', [$this, 'register_routes']);
    }

    /**
     * Verify API Secret from request headers
     *
     * This authenticates requests from the ReplyIt backend server.
     * The API Secret is configured in plugin settings and must match
     * the X-Widget-Secret header sent by the ReplyIt server.
     *
     * @return bool|WP_Error True if authenticated, WP_Error otherwise
     */
    public function verify_api_secret() {
        $api_secret = get_option('replyit_api_secret', '');

        if (empty($api_secret)) {
            return new WP_Error(
                'not_configured',
                __('Plugin is not configured', 'replyit-chat-widget'),
                ['status' => 403]
            );
        }

        // Get the secret from request header
        $request_secret = isset($_SERVER['HTTP_X_WIDGET_SECRET'])
            ? sanitize_text_field(wp_unslash($_SERVER['HTTP_X_WIDGET_SECRET']))
            : '';

        // Use timing-safe comparison to prevent timing attacks
        if (empty($request_secret) || !hash_equals($api_secret, $request_secret)) {
            return new WP_Error(
                'unauthorized',
                __('Invalid API credentials', 'replyit-chat-widget'),
                ['status' => 401]
            );
        }

        return true;
    }

    /**
     * Permission callback for authenticated endpoints
     *
     * Used for endpoints that handle sensitive data (e.g., order information).
     * Requires valid X-Widget-Secret header from ReplyIt backend.
     *
     * @return bool
     */
    public function check_api_secret_permission() {
        $result = $this->verify_api_secret();
        return $result === true;
    }

    /**
     * Register REST API routes
     */
    public function register_routes() {
        // Order lookup endpoint - AUTHENTICATED
        // SECURITY: This endpoint requires X-Widget-Secret header authentication
        // Only the ReplyIt backend server can call this endpoint
        // This protects sensitive order data from unauthorized access
        register_rest_route($this->namespace, '/order/lookup', [
            'methods' => 'POST',
            'callback' => [$this, 'lookup_order'],
            'permission_callback' => [$this, 'check_api_secret_permission'],
            'args' => [
                'order_id' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'order_key' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                    'validate_callback' => function($param) {
                        return !empty($param) && strlen($param) >= 10;
                    },
                ],
                'email' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_email',
                    'validate_callback' => function($param) {
                        return empty($param) || is_email($param);
                    },
                ],
            ],
        ]);

        // Product search endpoint - AUTHENTICATED
        // SECURITY: This endpoint requires X-Widget-Secret header authentication
        // Only the ReplyIt backend server can call this endpoint
        // This protects sensitive product data (SKU, stock quantities) from unauthorized access
        register_rest_route($this->namespace, '/product/search', [
            'methods' => 'POST',
            'callback' => [$this, 'search_products'],
            'permission_callback' => [$this, 'check_api_secret_permission'],
            'args' => [
                'query' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'sku' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'product_id' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'category' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'session_id' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
            ],
        ]);

        // Blog post search endpoint - INTENTIONALLY PUBLIC
        // This endpoint returns only published posts (post_status = 'publish')
        // This is equivalent to using the site's search feature - no private data is exposed
        // Rate limiting protects against abuse
        register_rest_route($this->namespace, '/blog/search', [
            'methods' => 'POST',
            'callback' => [$this, 'search_blog_posts'],
            'permission_callback' => '__return_true', // Public: returns only published posts visible on website
            'args' => [
                'query' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'category' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'tag' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'author' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'post_id' => [
                    'required' => false,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'session_id' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
            ],
        ]);

        // Blog post content endpoint - INTENTIONALLY PUBLIC
        // This endpoint returns content of published posts only (post_status = 'publish')
        // This is equivalent to viewing a blog post - no private data is exposed
        // Rate limiting protects against abuse
        register_rest_route($this->namespace, '/blog/content', [
            'methods' => 'POST',
            'callback' => [$this, 'get_blog_content'],
            'permission_callback' => '__return_true', // Public: returns only published post content visible on website
            'args' => [
                'post_id' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'session_id' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
            ],
        ]);
    }

    /**
     * Lookup order with secure authentication
     *
     * SECURITY: This endpoint is protected by two layers:
     * 1. API Secret verification (X-Widget-Secret header) - authenticates ReplyIt backend
     * 2. WooCommerce order_key verification - ensures customer owns the order
     *
     * Only the ReplyIt backend server can call this endpoint, and it must provide
     * the customer's order_key (from their confirmation email) for additional verification.
     */
    public function lookup_order($request) {
        // Verify WooCommerce is active
        if (!class_exists('WooCommerce')) {
            return new WP_Error(
                'woocommerce_inactive',
                __('WooCommerce is not active', 'replyit-chat-widget'),
                ['status' => 400]
            );
        }

        // Check if order tracking is enabled
        if (!get_option('replyit_order_tracking_enabled', true)) {
            return new WP_Error(
                'order_tracking_disabled',
                __('Order tracking is disabled', 'replyit-chat-widget'),
                ['status' => 403]
            );
        }

        $order_id = $request->get_param('order_id');
        $order_key = $request->get_param('order_key');
        $email = $request->get_param('email');

        // SECURITY: Both order_id and order_key are required
        if (empty($order_id) || empty($order_key)) {
            return new WP_Error(
                'missing_params',
                __('Order ID and order key are required', 'replyit-chat-widget'),
                ['status' => 400]
            );
        }

        // Get order data with order_key verification
        $order_data = ReplyIt_WooCommerce::get_order_data_secure($order_id, $order_key, $email);

        if (!$order_data) {
            return new WP_Error(
                'order_not_found',
                __('Order not found or invalid credentials', 'replyit-chat-widget'),
                ['status' => 404]
            );
        }

        return new WP_REST_Response([
            'success' => true,
            'message' => __('Order found', 'replyit-chat-widget'),
            'order' => $order_data,
        ], 200);
    }

    /**
     * Search products
     *
     * SECURITY: This endpoint is protected by X-Widget-Secret header authentication.
     * Only the ReplyIt backend server can call this endpoint.
     * The underlying WooCommerce query uses 'status' => 'publish' and individual
     * product lookups verify get_status() === 'publish' before returning data.
     */
    public function search_products($request) {
        // Verify WooCommerce is active
        if (!class_exists('WooCommerce')) {
            return new WP_Error(
                'woocommerce_inactive',
                __('WooCommerce is not active', 'replyit-chat-widget'),
                ['status' => 400]
            );
        }

        // Check if product search is enabled
        if (!get_option('replyit_product_search_enabled', true)) {
            return new WP_Error(
                'product_search_disabled',
                __('Product search is disabled', 'replyit-chat-widget'),
                ['status' => 403]
            );
        }

        $session_id = $request->get_param('session_id');
        $query = $request->get_param('query');
        $sku = $request->get_param('sku');
        $product_id = $request->get_param('product_id');
        $category = $request->get_param('category');

        // Validate that at least one search parameter is provided
        if (empty($query) && empty($sku) && empty($product_id) && empty($category)) {
            return new WP_Error(
                'missing_params',
                __('Please provide at least one search parameter (query, sku, product_id, or category)', 'replyit-chat-widget'),
                ['status' => 400]
            );
        }

        // Rate limiting: Check if this session has made too many requests
        $rate_limit_key = 'replyit_product_search_' . md5($session_id);
        $request_count = get_transient($rate_limit_key);

        if ($request_count && $request_count > 10) {
            return new WP_Error(
                'rate_limit_exceeded',
                __('Too many requests. Please try again later.', 'replyit-chat-widget'),
                ['status' => 429]
            );
        }

        // Increment request count (5 minutes window)
        set_transient($rate_limit_key, ($request_count ?: 0) + 1, 300);

        // Search products (helper already filters by 'status' => 'publish')
        $results = ReplyIt_WooCommerce::search_products([
            'query' => $query,
            'sku' => $sku,
            'product_id' => $product_id,
            'category' => $category,
        ]);

        // SECURITY: Double-check that only published, publicly visible products are returned.
        // This ensures no private/draft products leak even if the helper method changes.
        if (!empty($results['products'])) {
            $verified_products = [];
            foreach ($results['products'] as $product_data) {
                $product = wc_get_product($product_data['id']);
                if ($product && $product->get_status() === 'publish' && $product->get_catalog_visibility() !== 'hidden') {
                    $verified_products[] = $product_data;
                }
            }
            $results['products'] = $verified_products;
            $results['total_found'] = count($verified_products);
        }

        return new WP_REST_Response([
            'success' => true,
            'message' => $results['total_found'] > 0
                ? sprintf(__('%d product(s) found', 'replyit-chat-widget'), $results['total_found'])
                : __('No products found', 'replyit-chat-widget'),
            'data' => $results,
        ], 200);
    }

    /**
     * Search blog posts
     *
     * SECURITY: This endpoint is intentionally public (__return_true) because it only
     * returns published posts (post_status = 'publish') that are already visible on the website.
     * This is equivalent to using the site's built-in search feature.
     * Results are verified here to ensure only published posts are returned.
     */
    public function search_blog_posts($request) {
        // Check if blog search is enabled
        if (!get_option('replyit_blog_search_enabled', true)) {
            return new WP_Error(
                'blog_search_disabled',
                __('Blog search is disabled', 'replyit-chat-widget'),
                ['status' => 403]
            );
        }

        $session_id = $request->get_param('session_id');
        $query = $request->get_param('query');
        $category = $request->get_param('category');
        $tag = $request->get_param('tag');
        $author = $request->get_param('author');
        $post_id = $request->get_param('post_id');

        // Validate that at least one search parameter is provided
        if (empty($query) && empty($category) && empty($tag) && empty($author) && empty($post_id)) {
            return new WP_Error(
                'missing_params',
                __('Please provide at least one search parameter (query, category, tag, author, or post_id)', 'replyit-chat-widget'),
                ['status' => 400]
            );
        }

        // Rate limiting
        $rate_limit_key = 'replyit_blog_search_' . md5($session_id);
        $request_count = get_transient($rate_limit_key);

        if ($request_count && $request_count > 10) {
            return new WP_Error(
                'rate_limit_exceeded',
                __('Too many requests. Please try again later.', 'replyit-chat-widget'),
                ['status' => 429]
            );
        }

        // Increment request count (5 minutes window)
        set_transient($rate_limit_key, ($request_count ?: 0) + 1, 300);

        // Search posts (helper already filters by post_status = 'publish')
        $results = ReplyIt_Blog::search_posts([
            'query' => $query,
            'category' => $category,
            'tag' => $tag,
            'author' => $author,
            'post_id' => $post_id,
        ]);

        // SECURITY: Double-check that only published posts are returned.
        // This ensures no draft/private posts leak even if the helper method changes.
        if (!empty($results['posts'])) {
            $verified_posts = [];
            foreach ($results['posts'] as $post_data) {
                $post = get_post($post_data['id']);
                if ($post && $post->post_status === 'publish' && $post->post_type === 'post') {
                    $verified_posts[] = $post_data;
                }
            }
            $results['posts'] = $verified_posts;
            $results['total_found'] = count($verified_posts);
        }

        return new WP_REST_Response([
            'success' => true,
            'message' => $results['total_found'] > 0
                ? sprintf(__('%d post(s) found', 'replyit-chat-widget'), $results['total_found'])
                : __('No posts found', 'replyit-chat-widget'),
            'data' => $results,
        ], 200);
    }

    /**
     * Get blog post content
     *
     * SECURITY: This endpoint is intentionally public (__return_true) because it only
     * returns content of published posts (post_status = 'publish') that are already
     * visible on the website. This is equivalent to viewing a blog post page.
     */
    public function get_blog_content($request) {
        // Check if blog content retrieval is enabled
        if (!get_option('replyit_blog_content_enabled', true)) {
            return new WP_Error(
                'blog_content_disabled',
                __('Blog content retrieval is disabled', 'replyit-chat-widget'),
                ['status' => 403]
            );
        }

        $session_id = $request->get_param('session_id');
        $post_id = $request->get_param('post_id');

        if (empty($post_id)) {
            return new WP_Error(
                'missing_params',
                __('Post ID is required', 'replyit-chat-widget'),
                ['status' => 400]
            );
        }

        // Rate limiting
        $rate_limit_key = 'replyit_blog_content_' . md5($session_id);
        $request_count = get_transient($rate_limit_key);

        if ($request_count && $request_count > 15) {
            return new WP_Error(
                'rate_limit_exceeded',
                __('Too many requests. Please try again later.', 'replyit-chat-widget'),
                ['status' => 429]
            );
        }

        // Increment request count (5 minutes window)
        set_transient($rate_limit_key, ($request_count ?: 0) + 1, 300);

        // Get post content (helper already checks post_status = 'publish')
        $post_data = ReplyIt_Blog::get_post_content($post_id);

        if (!$post_data) {
            return new WP_Error(
                'post_not_found',
                __('Post not found', 'replyit-chat-widget'),
                ['status' => 404]
            );
        }

        // SECURITY: Double-check that the post is published and public
        $post = get_post(intval($post_id));
        if (!$post || $post->post_status !== 'publish' || $post->post_type !== 'post') {
            return new WP_Error(
                'post_not_found',
                __('Post not found', 'replyit-chat-widget'),
                ['status' => 404]
            );
        }

        return new WP_REST_Response([
            'success' => true,
            'message' => __('Post content retrieved', 'replyit-chat-widget'),
            'post' => $post_data,
        ], 200);
    }
}
