<?php
/**
 * Author: Rymera Web Co.
 *
 * @package SAAI\AI_Agents\Vendors
 */

namespace SAAI\AI_Agents\Vendors;

use SAAI\Abstracts\Abstract_AI_Agent;
use SAAI\Traits\Singleton_Trait;
use SAAI\Helpers\Helper;
use WP_Error;

/**
 * Vendors_Product_Moderator_AI class.
 *
 * This agent moderates products created by vendor users in their accounts.
 * It only runs when the WC Vendors plugin is active.
 *
 * @since 1.0.0
 */
class Vendors_Product_Moderator_AI extends Abstract_AI_Agent {

    use Singleton_Trait;

    /**
     * Holds the class instance object
     *
     * @since 1.0.0
     * @var Vendors_Product_Moderator_AI $instance object
     */
    protected static $instance;

    /**
     * Holds the settings data.
     *
     * @since 1.0.0
     * @var array<string, mixed> Settings data.
     */
    protected array $settings;

    /**
     * WC Vendors plugin basename.
     *
     * @since 1.0.0
     * @var string
     */
    const WC_VENDORS_PLUGIN            = 'wc-vendors/wc-vendors.php';
    const MODERATION_RESULT_META_KEY   = '_saai_vendors_product_moderation_result';
    const MODERATION_DATE_META_KEY     = '_saai_vendors_product_moderation_date';
    const IMAGE_REVIEW_META_KEY        = '_saai_vendors_product_image_review';
    const MODERATION_ACTION_HOOK       = 'saai_vendors_product_moderation';
    const IMAGE_MODERATION_ACTION_HOOK = 'saai_vendors_product_image_moderation';

    /**
     * Constructor.
     *
     * @param mixed $data The prompt data.
     */
    public function __construct( $data = null ) {

        $this->endpoint         = 'wc-vendors-product-moderator';
        $this->ai_id            = "$this->endpoint-ai";
        $this->data             = $data;
        $this->default_settings = array(
            'enabled'                => $this->maybe_enable_by_default(),
            'enable_image_review'    => 'no',
            'vendors_policy_page_id' => '',
        );

        // Initialize settings.
        $this->settings = $this->get_settings();
    }

    /**
     * Check if WC Vendors plugin is active.
     *
     * @since 1.0.0
     * @return bool True if WC Vendors is active, false otherwise.
     */
    public function is_wc_vendors_active() {
        // First check if WC Vendors class/function exists (more reliable).
        if ( class_exists( 'WC_Vendors' ) || function_exists( 'WCV_Vendors' ) || class_exists( 'WCV_Vendors' ) ) {
            return true;
        }

        // Fallback to plugin check.
        if ( ! function_exists( 'is_plugin_active' ) ) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        return is_plugin_active( self::WC_VENDORS_PLUGIN );
    }

    /**
     * Check if the AI agent is enabled and WC Vendors is active.
     *
     * @since 1.0.0
     * @return bool
     */
    public function is_enabled() {
        return parent::is_enabled() && $this->is_wc_vendors_active();
    }

    /**
     * Maybe enable the AI agent by default.
     *
     * @since 1.0.0
     * @return string
     */
    public function maybe_enable_by_default() {
        return 'no';
    }

    /**
     * Run the agent query to moderate a vendor product.
     *
     * @param array $params The parameters for the agent query.
     * @return array|\WP_Error An array of moderation results or WP_Error.
     */
    public function run_agent_query( $params = array() ) {
        $product_id = isset( $params['product_id'] ) ? (int) $params['product_id'] : 0;

        if ( ! $product_id ) {
            return new WP_Error(
                'invalid_product_id',
                __( 'Invalid product ID.', 'storeagent-ai-for-woocommerce' )
            );
        }

        $product = wc_get_product( $product_id );

        if ( ! $product instanceof \WC_Product ) {
            return new WP_Error(
                'invalid_product',
                __( 'Product not found.', 'storeagent-ai-for-woocommerce' )
            );
        }

        // Prepare product data for moderation.
        $product_data = $this->prepare_product_data( $product );

        // Prepare data array with product_data and vendors_policy as separate properties.
        // vendors_policy is always present, set to empty string if no page is configured.
        $data = array(
            'product_data'   => wp_json_encode( $product_data ),
            'vendors_policy' => '',
        );

        // Get vendors policy page content if a page is selected.
        $vendors_policy_page_id = $this->get_settings( 'vendors_policy_page_id' );
        if ( ! empty( $vendors_policy_page_id ) ) {
            $policy_content = $this->get_vendors_policy_content( $vendors_policy_page_id );
            if ( ! empty( $policy_content ) ) {
                $data['vendors_policy'] = $policy_content;
            }
        }

        $this->set_data( $data );
        $ai_response = $this->send_prompt();

        if ( is_wp_error( $ai_response ) ) {
            return $ai_response;
        }

        return $ai_response;
    }

    /**
     * Prepare product data for moderation.
     *
     * @param \WC_Product $product The product object.
     * @return array Product data array.
     */
    protected function prepare_product_data( $product ) {
        $product_data = array(
            'product_id'          => $product->get_id(),
            'product_name'        => $product->get_name(),
            'product_description' => $product->get_description(),
            'short_description'   => $product->get_short_description(),
            'product_url'         => get_permalink( $product->get_id() ),
            'product_status'      => $product->get_status(),
            'product_type'        => $product->get_type(),
            'price'               => $product->get_price(),
            'regular_price'       => $product->get_regular_price(),
            'sale_price'          => $product->get_sale_price(),
            'sku'                 => $product->get_sku(),
            'stock_status'        => $product->get_stock_status(),
            'stock_quantity'      => $product->get_stock_quantity(),
            'categories'          => $this->get_product_categories( $product ),
            'tags'                => $this->get_product_tags( $product ),
        );

        // Get vendor information if available.
        if ( function_exists( 'WCV_Vendors' ) ) {
            $vendor_id = \WCV_Vendors::get_vendor_from_product( $product->get_id() );
            if ( $vendor_id ) {
                $vendor = get_userdata( $vendor_id );
                if ( $vendor ) {
                    $product_data['vendor_id']    = $vendor_id;
                    $product_data['vendor_name']  = $vendor->display_name;
                    $product_data['vendor_email'] = $vendor->user_email;
                }
            }
        }

        // Get product images.
        $image_ids = $product->get_gallery_image_ids();
        if ( $product->get_image_id() ) {
            array_unshift( $image_ids, $product->get_image_id() );
        }

        $product_data['images'] = array();
        foreach ( $image_ids as $image_id ) {
            $image_url = wp_get_attachment_image_url( $image_id, 'full' );
            if ( $image_url ) {
                $product_data['images'][] = $image_url;
            }
        }

        return apply_filters( 'saai_vendors_product_moderator_product_data', $product_data, $product );
    }

    /**
     * Get vendors policy page content.
     *
     * @param string|int $page_id The page ID.
     * @return string The page content, stripped of HTML tags.
     */
    protected function get_vendors_policy_content( $page_id ) {
        $page_id = absint( $page_id );
        if ( ! $page_id ) {
            return '';
        }

        $page = get_post( $page_id );
        if ( ! $page || 'page' !== $page->post_type ) {
            return '';
        }

        // Get the page content.
        $content = $page->post_content;

        // Apply WordPress filters to process shortcodes, etc.
        $content = apply_filters( 'the_content', $content );

        // Strip HTML tags but preserve line breaks and spacing.
        $content = wp_strip_all_tags( $content, true );

        // Clean up extra whitespace.
        $content = preg_replace( '/\s+/', ' ', $content );
        $content = trim( $content );

        return $content;
    }

    /**
     * Get product categories.
     *
     * @param \WC_Product $product The product object.
     * @return array Array of category names.
     */
    protected function get_product_categories( $product ) {
        $categories = wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) );
        if ( is_wp_error( $categories ) || ! is_array( $categories ) ) {
            return array();
        }
        return $categories;
    }

    /**
     * Get product tags.
     *
     * @param \WC_Product $product The product object.
     * @return array Array of tag names.
     */
    protected function get_product_tags( $product ) {
        $tags = wp_get_post_terms( $product->get_id(), 'product_tag', array( 'fields' => 'names' ) );
        if ( is_wp_error( $tags ) || ! is_array( $tags ) ) {
            return array();
        }
        return $tags;
    }

    /**
     * Schedule async moderation for vendor product when saved to pending.
     *
     * @param int $product_id The product ID.
     * @return void
     */
    public function moderate_vendor_product( $product_id ) {
        // Verify this is a vendor product.
        if ( ! class_exists( '\WC_Vendors' ) ) {
            return;
        }

        $vendor_id = \WCV_Vendors::get_vendor_from_product( $product_id );
        if ( ! $vendor_id ) {
            // Not a vendor product, skip moderation.
            return;
        }

        // Verify product exists.
        $product = wc_get_product( $product_id );
        if ( ! $product instanceof \WC_Product ) {
            return;
        }

        // Schedule product moderation action.
        $this->schedule_moderation( $product_id );

        // Schedule image moderation action only if enabled.
        if ( isset( $this->settings['enable_image_review'] ) && 'yes' === $this->settings['enable_image_review'] ) {
            $this->schedule_image_moderation( $product_id );
        }
    }

    /**
     * Process moderation for a vendor product (called by Action Scheduler).
     *
     * @param int $product_id The product ID.
     * @return void
     */
    public function process_moderation( $product_id ) {
        // Verify product still exists.
        $product = wc_get_product( $product_id );
        if ( ! $product instanceof \WC_Product ) {
            return;
        }

        // Verify this is still a vendor product.
        if ( ! class_exists( '\WC_Vendors' ) ) {
            return;
        }

        $vendor_id = \WCV_Vendors::get_vendor_from_product( $product_id );
        if ( ! $vendor_id ) {
            // Not a vendor product anymore, skip moderation.
            return;
        }

        // Run moderation query.
        $result = $this->run_agent_query(
            array(
                'product_id' => $product_id,
            )
        );

        if ( is_wp_error( $result ) ) {
            // Log error using WooCommerce logger.
            if ( function_exists( 'wc_get_logger' ) ) {
                wc_get_logger()->error(
                    sprintf( 'Vendor Product Moderator AI Error: %s', $result->get_error_message() ),
                    array( 'source' => 'storeagent-ai-for-woocommerce' )
                );
            }
            return;
        }

        // Handle moderation result.
        $this->handle_moderation_result( $product_id, $result );
    }

    /**
     * Handle moderation result from AI.
     *
     * @param int   $product_id The product ID.
     * @param array $result The moderation result.
     * @return void
     */
    protected function handle_moderation_result( $product_id, $result ) {
        // Store moderation result as product meta.
        // Preserve any image review failures that may have been set by image moderation.
        $existing_result = get_post_meta( $product_id, self::MODERATION_RESULT_META_KEY, true );
        if ( is_array( $existing_result ) && isset( $existing_result['needs_review'] ) && $existing_result['needs_review'] ) {
            // Preserve needs_review flag if it was set by image moderation.
            $result['needs_review'] = true;
            // Preserve status if it indicates review needed.
            if ( isset( $existing_result['status'] ) && 'needs_review' === $existing_result['status'] ) {
                $result['status'] = 'needs_review';
            }
        }

        update_post_meta( $product_id, self::MODERATION_RESULT_META_KEY, $result );
        update_post_meta( $product_id, self::MODERATION_DATE_META_KEY, current_time( 'mysql' ) );

        // Allow other code to hook into moderation result.
        do_action( 'saai_vendors_product_moderated', $product_id, $result );
    }



    /**
     * Schedule an async action for moderation.
     *
     * @param int    $product_id The product ID.
     * @param string $hook_name  The action hook name.
     * @return void
     */
    protected function schedule_async_action( $product_id, $hook_name ) {
        // Schedule async action (non-blocking).
        if ( function_exists( 'as_enqueue_async_action' ) ) {
            as_enqueue_async_action(
                $hook_name,
                array( $product_id ),
                'saai'
            );
        } elseif ( function_exists( 'as_schedule_single_action' ) ) {
            // Fallback to scheduled action if async is not available.
            as_schedule_single_action(
                time(),
                $hook_name,
                array( $product_id ),
                'saai'
            );
        }
    }

    /**
     * Schedule moderation action for a product.
     *
     * @param int $product_id The product ID.
     * @return void
     */
    protected function schedule_moderation( $product_id ) {
        $this->schedule_async_action( $product_id, self::MODERATION_ACTION_HOOK );
    }

    /**
     * Schedule image moderation action for a product.
     *
     * @param int $product_id The product ID.
     * @return void
     */
    protected function schedule_image_moderation( $product_id ) {
        $this->schedule_async_action( $product_id, self::IMAGE_MODERATION_ACTION_HOOK );
    }

    /**
     * Process image moderation for a vendor product (called by Action Scheduler).
     *
     * @param int $product_id The product ID.
     * @return void
     */
    public function process_image_moderation( $product_id ) {
        // Verify product still exists.
        $product = wc_get_product( $product_id );
        if ( ! $product instanceof \WC_Product ) {
            return;
        }

        // Verify this is still a vendor product.
        if ( ! class_exists( '\WC_Vendors' ) ) {
            return;
        }

        $vendor_id = \WCV_Vendors::get_vendor_from_product( $product_id );
        if ( ! $vendor_id ) {
            // Not a vendor product anymore, skip moderation.
            return;
        }

        // Get product images.
        $image_ids = $product->get_gallery_image_ids();
        if ( $product->get_image_id() ) {
            array_unshift( $image_ids, $product->get_image_id() );
        }

        // Get existing image reviews to check which images have already been reviewed.
        $existing_reviews = get_post_meta( $product_id, self::IMAGE_REVIEW_META_KEY, true );
        if ( ! is_array( $existing_reviews ) ) {
            $existing_reviews = array();
        }

        // Early return if there are no images and no existing reviews.
        if ( empty( $image_ids ) && empty( $existing_reviews ) ) {
            return;
        }

        // Prepare product data only if we have images to process.
        $product_data = $this->prepare_product_data( $product );

        // Create a map of already reviewed image IDs for quick lookup.
        // Consider an image reviewed if it has a review entry (for backward compatibility).
        $reviewed_image_ids = array();
        foreach ( $existing_reviews as $index => $existing_review ) {
            if ( isset( $existing_review['image_id'] ) ) {
                // If reviewed flag is explicitly set to true, or if review exists (backward compatibility).
                if ( ( isset( $existing_review['reviewed'] ) && $existing_review['reviewed'] ) || isset( $existing_review['review'] ) ) {
                    $reviewed_image_ids[ $existing_review['image_id'] ] = true;
                    // Ensure reviewed flag is set for backward compatibility.
                    if ( ! isset( $existing_review['reviewed'] ) ) {
                        $existing_reviews[ $index ]['reviewed'] = true;
                    }
                }
            }
        }

        // Collect new image review results.
        $new_image_reviews = array();

        // Loop through images and moderate only new ones.
        foreach ( $image_ids as $image_id ) {
            // Skip if this image has already been reviewed.
            if ( isset( $reviewed_image_ids[ $image_id ] ) ) {
                continue;
            }

            $image_url = wp_get_attachment_image_url( $image_id, 'full' );
            if ( ! $image_url ) {
                continue;
            }

            // Moderate this image.
            $review_result = $this->moderate_product_image( $image_id, $image_url, $product_data );

            // Store review result if not an error.
            if ( ! is_wp_error( $review_result ) ) {
                $new_image_reviews[] = array(
                    'image_id' => $image_id,
                    'review'   => $review_result,
                    'reviewed' => true,
                );
            }
        }

        // Merge new reviews with existing reviews and save.
        // Always save to ensure reviewed flags are updated even if no new reviews.
        if ( ! empty( $new_image_reviews ) || ! empty( $existing_reviews ) ) {
            // Create a map of existing reviews by image_id to avoid duplicates.
            $existing_reviews_map = array();
            foreach ( $existing_reviews as $existing_review ) {
                if ( isset( $existing_review['image_id'] ) ) {
                    $existing_reviews_map[ $existing_review['image_id'] ] = $existing_review;
                }
            }

            // Merge new reviews, replacing existing ones for the same image_id.
            foreach ( $new_image_reviews as $new_review ) {
                $existing_reviews_map[ $new_review['image_id'] ] = $new_review;
            }

            // Ensure all existing reviews have the reviewed flag set for backward compatibility.
            foreach ( $existing_reviews_map as $image_id => $review ) {
                if ( ! isset( $review['reviewed'] ) && isset( $review['review'] ) ) {
                    $existing_reviews_map[ $image_id ]['reviewed'] = true;
                }
            }

            // Convert back to array and save.
            $all_reviews = array_values( $existing_reviews_map );
            update_post_meta( $product_id, self::IMAGE_REVIEW_META_KEY, $all_reviews );

            // Check if any image review has a non-passing status and update main moderation result.
            // Use the reviews we just prepared, not re-reading from database to avoid race conditions.
            $has_non_passing_image = $this->has_non_passing_image_review( $all_reviews );

            if ( $has_non_passing_image ) {
                $this->update_moderation_result_with_image_failures( $product_id, $all_reviews );
            }
        }
    }

    /**
     * Check if any image review has a non-passing status.
     *
     * @param array $image_reviews Array of image review results.
     * @return bool True if any image review has a non-passing status, false otherwise.
     */
    protected function has_non_passing_image_review( $image_reviews ) {
        foreach ( $image_reviews as $image_review ) {
            $review = $image_review['review'];
            if ( ! is_array( $review ) ) {
                continue;
            }

            // Check for non-passing status indicators.
            // Common patterns: status !== 'pass', needs_review === true, approved === false, status === 'fail' or 'rejected'.
            $status       = isset( $review['status'] ) ? strtolower( $review['status'] ) : '';
            $needs_review = isset( $review['needs_review'] ) ? (bool) $review['needs_review'] : false;
            $approved     = isset( $review['approved'] ) ? (bool) $review['approved'] : true;

            if ( 'pass' !== $status && 'approved' !== $status && 'passing' !== $status ) {
                // Status indicates non-passing.
                return true;
            }

            if ( $needs_review ) {
                // Explicitly needs review.
                return true;
            }

            if ( false === $approved ) {
                // Explicitly not approved.
                return true;
            }
        }

        return false;
    }

    /**
     * Update moderation result metadata to reflect failed image reviews.
     * Uses a safer approach that reads current value, updates it, and saves to avoid race conditions.
     *
     * @param int   $product_id The product ID.
     * @param array $all_reviews All image reviews (existing + new).
     * @return void
     */
    protected function update_moderation_result_with_image_failures( $product_id, $all_reviews ) {
        // Read current moderation result.
        $moderation_result = get_post_meta( $product_id, self::MODERATION_RESULT_META_KEY, true );
        if ( ! is_array( $moderation_result ) ) {
            $moderation_result = array();
        }

        // Update with image failure flags.
        $moderation_result = $this->update_moderation_result_for_failed_images( $moderation_result );

        // Save updated moderation result.
        update_post_meta( $product_id, self::MODERATION_RESULT_META_KEY, $moderation_result );
    }

    /**
     * Update moderation result metadata to reflect failed image reviews.
     *
     * @param array $moderation_result The current moderation result.
     * @return array The updated moderation result.
     */
    protected function update_moderation_result_for_failed_images( $moderation_result ) {
        $moderation_result['needs_review'] = true;

        // Ensure status is set to indicate review needed.
        if ( ! isset( $moderation_result['status'] ) || 'pass' === $moderation_result['status'] ) {
            $moderation_result['status'] = 'needs_review';
        }

        return $moderation_result;
    }

    /**
     * Moderate a single product image.
     *
     * @param int    $image_id The image attachment ID.
     * @param string $image_url The image URL.
     * @param array  $product_data The product data.
     * @return array|\WP_Error The moderation result or WP_Error.
     */
    protected function moderate_product_image( $image_id, $image_url, $product_data ) {
        // Prepare data for image moderation endpoint.
        $data = array(
            'image_url'    => $image_url,
            'product_data' => $product_data,
        );

        // Set the endpoint for image moderation.
        $original_endpoint = $this->endpoint;
        $this->endpoint    = 'wc-vendors-product-image-moderator';

        // Set the data and send the prompt.
        $this->set_data( $data );
        $result = $this->send_prompt();

        // Restore original endpoint.
        $this->endpoint = $original_endpoint;

        if ( is_wp_error( $result ) ) {
            // Log error using WooCommerce logger but don't block.
            if ( function_exists( 'wc_get_logger' ) ) {
                wc_get_logger()->error(
                    sprintf( 'Vendor Product Image Moderator AI Error: %s', $result->get_error_message() ),
                    array( 'source' => 'storeagent-ai-for-woocommerce' )
                );
            }
            return $result;
        }

        // Allow other code to hook into image moderation result.
        do_action( 'saai_vendors_product_image_moderated', $image_url, $product_data, $result );

        return $result;
    }

    /**
     * Handle product change request from admin.
     *
     * @param int    $product_id  The product ID.
     * @param int    $vendor_id   The vendor ID.
     * @param string $message     The change request message.
     * @param array  $suggestions  Array of suggestions for changes.
     * @return void
     */
    public function handle_product_change_request( $product_id, $vendor_id, $message, $suggestions ) {
        // Verify this is a vendor product.
        if ( ! class_exists( '\WC_Vendors' ) ) {
            return;
        }

        // Verify product exists.
        $product = wc_get_product( $product_id );
        if ( ! $product instanceof \WC_Product ) {
            return;
        }

        // Get current moderation result.
        $moderation_result = get_post_meta( $product_id, self::MODERATION_RESULT_META_KEY, true );
        if ( ! is_array( $moderation_result ) ) {
            $moderation_result = array();
        }

        // Update moderation result to reflect that admin has requested changes.
        $moderation_result['admin_change_request']       = true;
        $moderation_result['change_request_message']     = sanitize_text_field( $message );
        $moderation_result['change_request_suggestions'] = is_array( $suggestions ) ? $suggestions : array();
        $moderation_result['change_request_date']        = current_time( 'mysql' );

        // Update moderation result meta.
        update_post_meta( $product_id, self::MODERATION_RESULT_META_KEY, $moderation_result );

        // Allow other code to hook into change request.
        do_action( 'saai_vendors_product_change_requested', $product_id, $vendor_id, $message, $suggestions );
    }

    /**
     * Run hooks related to the AI agent.
     *
     * @return void
     */
    public function run() {
        // Only run if WC Vendors is active.
        if ( ! $this->is_wc_vendors_active() ) {
            return;
        }

        if ( ! $this->is_enabled() ) {
            return;
        }

        // Register Action Scheduler handler for product moderation.
        add_action( self::MODERATION_ACTION_HOOK, array( $this, 'process_moderation' ), 10, 1 );

        // Register Action Scheduler handler for image moderation.
        add_action( self::IMAGE_MODERATION_ACTION_HOOK, array( $this, 'process_image_moderation' ), 10, 1 );

        // Hook into vendor product saved to pending status to schedule async moderation.
        add_action( 'wcvendors_product_saved_to_pending', array( $this, 'moderate_vendor_product' ), 10, 1 );

        // Hook into product change request from admin.
        add_action( 'wcvendors_request_product_change', array( $this, 'handle_product_change_request' ), 10, 4 );
    }
}
