<?php
/**
 * Bulk Run Agent Class
 *
 * @package SAAI\Classes
 */

namespace SAAI\Classes;

use SAAI\Traits\Singleton_Trait;
use SAAI\AI_Agents\Product_Description_AI;
use SAAI\AI_Agents\Product_Tags_Generator_AI;
use SAAI\AI_Agents\Product_Summaries_AI;
use SAAI\AI_Agents\Review_Summaries_AI;
use SAAI\Abstracts\Abstract_AI_Agent;
use SAAI\Factories\Vite_App;
use SAAI\Interfaces\Bulk_Run_Agent_Interface;

/**
 * Bulk Run Agent Class
 *
 * @since 1.0.0
 */
class Bulk_Run_Agent {

    use Singleton_Trait;

    const BULK_RUN_AGENT_ACTION   = 'saai_bulk_run_agent';
    const ADMIN_NOTICES_TRANSIENT = 'saai_bulk_run_notices';

    /**
     * Schedule the bulk run agent.
     *
     * @param string $agent_name The agent name.
     * @param array  $product_ids The product IDs.
     */
    public function schedule_bulk_run_agent( $agent_name, $product_ids = array() ) {
        if ( empty( $agent_name ) || empty( $product_ids ) ) {
            return new \WP_Error( 'invalid_data', __( 'Invalid data.', 'storeagent-ai-for-woocommerce' ) );
        }

        $product_ids = array_map( 'absint', $product_ids );
        $agent_label = $this->get_agent_label( $agent_name );

        // Log start of async action.
        $this->log(
            sprintf(
            /* translators: %1$s: agent name, %2$d: total products */
                __( 'Bulk run scheduled for the "%1$s" for %2$d product(s).', 'storeagent-ai-for-woocommerce' ),
                $agent_label,
                count( $product_ids )
            ),
            $agent_name,
            'info'
        );

        // Divide the product ids into chunks of 10.
        $chunk_size  = apply_filters( 'saai_bulk_run_agent_chunk_size', 10 );
        $product_ids = array_chunk( $product_ids, $chunk_size );

        foreach ( $product_ids as $product_ids_chunk ) {
            as_enqueue_async_action( self::BULK_RUN_AGENT_ACTION, array( $agent_name, $product_ids_chunk ) );
        }

        $log_file_url = self::get_bulk_run_log_file_url( $agent_name );

        // Set admin notice for successful scheduling.
        $this->set_admin_notice(
            sprintf(
            /* translators: %1$s: agent name, %2$d: total products, %3$s: log file URL */
                __( 'Bulk action in progress for the %1$s for %2$d products. This process may take some time to complete. You can monitor the progress and view detailed logs %3$shere%4$s.', 'storeagent-ai-for-woocommerce' ),
                '<strong>' . $this->get_agent_label( $agent_name ) . '</strong>',
                count( array_merge( ...$product_ids ) ),
                '<a href="' . $log_file_url . '">',
                '</a>'
            )
        );
    }

    /**
     * Bulk run the agent.
     *
     * @param string $agent_name The agent name.
     * @param array  $product_ids The product IDs.
     */
    public function bulk_run_agent( $agent_name, $product_ids = array() ) {
        if ( empty( $agent_name ) || empty( $product_ids ) ) {
            return new \WP_Error( 'invalid_data', __( 'Invalid data.', 'storeagent-ai-for-woocommerce' ) );
        }

        $agent = self::get_agent( $agent_name );

        if ( ! $agent || ! $agent instanceof Bulk_Run_Agent_Interface ) {
            return new \WP_Error( 'invalid_agent', __( 'Invalid agent.', 'storeagent-ai-for-woocommerce' ) );
        }

        if ( is_wp_error( $agent ) ) {
            /**
             * $agent is a \WP_Error object.
             *
             * @var \WP_Error $agent
             */
            $this->maybe_error_cli_output( $agent->get_error_message() );
            return $agent;
        }

        $is_cli     = defined( 'WP_CLI' ) && WP_CLI;
        $had_errors = 0;
        $skipped    = 0;

        if ( $is_cli ) {
            /* translators: %s: The number of products */
            $progress = \WP_CLI\Utils\make_progress_bar( sprintf( _n( 'Running AI agent for %d product', 'Running AI agent for %d products', count( $product_ids ), 'storeagent-ai-for-woocommerce' ), count( $product_ids ) ), count( $product_ids ) );
        }

        foreach ( $product_ids as $product_id ) {
            $ai_response = $agent->run_agent_query( array( 'product_id' => $product_id ) );

            if ( $is_cli ) {
                $progress->tick();
            }

            if ( is_wp_error( $ai_response ) ) {
                ++$had_errors;
                $this->log( $ai_response->get_error_message(), $agent_name, 'warning' );
                continue;
            }

            if ( ! is_array( $ai_response ) || ! isset( $ai_response['completion'] ) ) {
                ++$skipped;
                $this->log(
                    sprintf(
                        /* translators: %s: product title */
                        __( 'Product "%s" skipped — AI agent returned an unexpected response.', 'storeagent-ai-for-woocommerce' ),
                        get_the_title( $product_id )
                    ),
                    $agent_name,
                    'warning'
                );
                continue;
            }

            $check_save = $agent->save_object_data( $product_id, $ai_response['completion'] );

            if ( is_wp_error( $check_save ) ) {
                ++$had_errors;
                $this->log( $check_save->get_error_message(), $agent_name, 'warning' );
                continue;
            }

            $this->log(
                sprintf(
                /* translators: %s: product title */
                    __( 'Product "%s" processed successfully.', 'storeagent-ai-for-woocommerce' ),
                    get_the_title( $product_id )
                ),
                $agent_name,
                'success'
            );
        }

        if ( $is_cli ) {
            $progress->finish();
            \WP_CLI::success(
                sprintf(
                /* translators: %1$s: Agent name; %2$d: Number of products processed successfully; %3$d: Number of products with errors; %4$d: Number of products skipped. */
                    __( 'Bulk run for %1$s agent has been completed! %2$d Success; %3$d Failed; %4$d Skipped;', 'storeagent-ai-for-woocommerce' ),
                    wp_basename( get_class( $agent ) ),
                    count( $product_ids ) - $had_errors - $skipped,
                    $had_errors,
                    $skipped
                )
            );
        }
    }

    /**
     * Log the error.
     *
     * @param string $message The message.
     * @param string $agent_name The agent name.
     * @param string $type The type.
     */
    public function log( $message, $agent_name, $type = 'info' ) {
        if ( defined( 'WP_CLI' ) && WP_CLI ) {
            switch ( $type ) {
                case 'warning':
                    \WP_CLI::warning( $message );
                    break;
                case 'debug':
                    \WP_CLI::debug( $message );
                    break;
                case 'success':
                    \WP_CLI::success( $message );
                    break;
                case 'error':
                    \WP_CLI::error( $message );
                    break;
                case 'info':
                default:
                    \WP_CLI::log( $message );
                    break;
            }
        } else {
            switch ( $type ) {
                case 'warning':
                    wc_get_logger()->warning( $message, array( 'source' => 'storeagent-bulk-run-' . $agent_name ) );
                    break;
                case 'debug':
                    wc_get_logger()->debug( $message, array( 'source' => 'storeagent-bulk-run-' . $agent_name ) );
                    break;
                case 'error':
                    wc_get_logger()->error( $message, array( 'source' => 'storeagent-bulk-run-' . $agent_name ) );
                    break;
                case 'info':
                case 'success': // wc_get_logger() doesn't have a success method, so we use info instead.
                default:
                    wc_get_logger()->info( $message, array( 'source' => 'storeagent-bulk-run-' . $agent_name ) );
                    break;
            }
        }
    }

    /**
     * Maybe log the CLI output.
     *
     * @param string $message The message to log.
     */
    public function maybe_log_cli_output( $message ) {
        if ( defined( 'WP_CLI' ) && WP_CLI ) {
            \WP_CLI::log( $message );
        }
    }

    /**
     * Maybe warn the CLI output.
     *
     * @param string $message The message to warn.
     */
    public function maybe_warn_cli_output( $message ) {
        if ( defined( 'WP_CLI' ) && WP_CLI ) {
            \WP_CLI::warning( $message );
        }
    }

    /**
     * Maybe error the CLI output.
     *
     * @param string $message The message to error.
     * @param bool   $halt    Whether to exit the script.
     */
    public function maybe_error_cli_output( $message, $halt = true ) {
        if ( defined( 'WP_CLI' ) && WP_CLI ) {
            \WP_CLI::error( $message, $halt );
        }

        if ( $halt ) {
            exit;
        }
    }

    /*
    |--------------------------------------------------------------------------
    | Admin UI related methods
    |--------------------------------------------------------------------------
    */

    /**
     * Register bulk action.
     *
     * @param array $actions The actions.
     *
     * @return array The actions.
     */
    public function register_bulk_action( $actions ) {
        $actions['run_ai_agent'] = __( 'Run AI Agent', 'storeagent-ai-for-woocommerce' );

        return $actions;
    }

    /**
     * Handle bulk run agent AJAX request.
     */
    public function ajax_handle_bulk_run_agent() {
        check_ajax_referer( 'saai_bulk_run_agent', 'nonce' );

        if ( ! current_user_can( 'manage_woocommerce' ) ) {
            wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'storeagent-ai-for-woocommerce' ) ) );
        }

        $agent       = isset( $_POST['agent'] ) ? sanitize_text_field( wp_unslash( $_POST['agent'] ) ) : '';
        $product_ids = isset( $_POST['product_ids'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['product_ids'] ) ) : array();

        if ( empty( $agent ) || empty( $product_ids ) ) {
            wp_send_json_error( array( 'message' => __( 'Invalid request parameters.', 'storeagent-ai-for-woocommerce' ) ) );
        }

        // Schedule the bulk run.
        $this->schedule_bulk_run_agent( $agent, $product_ids );

        wp_send_json_success( array( 'message' => __( 'Bulk run scheduled successfully.', 'storeagent-ai-for-woocommerce' ) ) );
    }

    /*
    |--------------------------------------------------------------------------
    | Admin notices related methods
    |--------------------------------------------------------------------------
    */

    /**
     * Set an admin notice in transient.
     *
     * @param string $message The notice message.
     */
    private function set_admin_notice( $message ) {
        $notices = get_transient( self::ADMIN_NOTICES_TRANSIENT );
        $notices = ! is_array( $notices ) || empty( $notices ) ? array() : $notices;

        $notices[] = $message;

        set_transient(
            self::ADMIN_NOTICES_TRANSIENT,
            $notices,
            DAY_IN_SECONDS
        );
    }

    /**
     * Display admin notices.
     */
    public function display_admin_notices() {
        $notices = get_transient( self::ADMIN_NOTICES_TRANSIENT );
        if ( empty( $notices ) ) {
            return;
        }

        include SAAI_TEMPLATES_DIR . 'admin/bulk-run-agent-admin-notice.php';
    }

    /**
     * Maybe dismiss the admin notice.
     *
     * @return void
     */
    public function maybe_dismiss_admin_notices() {
        if ( (bool) get_transient( self::ADMIN_NOTICES_TRANSIENT ) && ! as_has_scheduled_action( self::BULK_RUN_AGENT_ACTION ) ) {
            delete_transient( self::ADMIN_NOTICES_TRANSIENT );
        }
    }

    /*
    |--------------------------------------------------------------------------
    | Utility methods
    |--------------------------------------------------------------------------
    */

    /**
     * Get the agent instance.
     *
     * @param string $agent_name The agent name.
     *
     * @return Abstract_AI_Agent|null The agent instance.
     */
    public static function get_agent( $agent_name ) {
        switch ( $agent_name ) {
            case 'product-description':
            case 'product-description-ai':
                return Product_Description_AI::instance();
            case 'product-tags-generator':
            case 'product-tags-generator-ai':
                return Product_Tags_Generator_AI::instance();
            case 'product-summary':
            case 'product-summaries-ai':
                return Product_Summaries_AI::instance();
            case 'review-summary':
            case 'review-summaries-ai':
                return Review_Summaries_AI::instance();
        }

        return null;
    }

    /**
     * Get agent label from its name.
     *
     * @param string $agent_name The agent name.
     * @return string The agent label.
     */
    private function get_agent_label( $agent_name ) {
        $options = self::get_bulk_ai_agent_options();
        foreach ( $options as $option ) {
            if ( $option['value'] === $agent_name ) {
                return $option['label'];
            }
        }
        return $agent_name;
    }

    /**
     * Get the bulk AI agent options.
     *
     * @return array The bulk AI agent options.
     */
    public static function get_bulk_ai_agent_options() {
        return array(
            array(
                'value'  => 'product-description-ai',
                'label'  => __( 'Product Description AI', 'storeagent-ai-for-woocommerce' ),
                'notice' => '',
            ),
            array(
                'value'  => 'product-tags-generator-ai',
                'label'  => __( 'Product Tags Generator AI', 'storeagent-ai-for-woocommerce' ),
                'notice' => __( 'Selected products that have no description will be skipped.', 'storeagent-ai-for-woocommerce' ),
            ),
            array(
                'value'  => 'product-summaries-ai',
                'label'  => __( 'Product Summaries AI', 'storeagent-ai-for-woocommerce' ),
                'notice' => __( 'Selected products that have no description will be skipped.', 'storeagent-ai-for-woocommerce' ),
            ),
            array(
                'value'  => 'review-summaries-ai',
                'label'  => __( 'Review Summaries AI', 'storeagent-ai-for-woocommerce' ),
                'notice' => __( 'Selected products that have no reviews will be skipped.', 'storeagent-ai-for-woocommerce' ),
            ),
        );
    }

    /**
     * Get the bulk run log file URL.
     *
     * @param string $agent_name The agent name.
     * @return string The bulk run log file URL.
     */
    public static function get_bulk_run_log_file_url( $agent_name ) {
        $date = wp_date( 'Y-m-d' );
        return admin_url( 'admin.php?page=wc-status&tab=logs&view=single_file&file_id=storeagent-bulk-run-' . $agent_name . '-' . $date );
    }

    /**
     * Run the handler.
     */
    public function run() {
        add_action( self::BULK_RUN_AGENT_ACTION, array( $this, 'bulk_run_agent' ), 10, 2 );
        add_action( 'bulk_actions-edit-product', array( $this, 'register_bulk_action' ) );
        add_action( 'wp_ajax_saai_bulk_run_agent', array( $this, 'ajax_handle_bulk_run_agent' ) );
        add_action( 'admin_notices', array( $this, 'display_admin_notices' ) );
        add_action( 'admin_init', array( $this, 'maybe_dismiss_admin_notices' ) );
    }
}
