<?php
/**
 * Storeagent CLI
 *
 * @package SAAI
 */

namespace SAAI\Cli;

use SAAI\Abstracts\Abstract_CLI;
use SAAI\Classes\Bulk_Run_Agent;
use SAAI\Classes\Memory\Data_Upload;
use SAAI\Helpers\Helper;

/**
 * Use the Storeagent tools via CLI
 */
class Storeagent extends Abstract_CLI {

    /**
     * Invoke the Storeagent CLI and call the appropriate subcommand handler.
     *
     * ## EXAMPLES
     *
     *     wp storeagent <subcommand> [<args>...] [--<assoc_args>[=<assoc_args_value>]...]
     *
     * @when  after_wp_load
     * @param array $args       Positional arguments.
     * @param array $assoc_args Associative arguments.
     */
    public function __invoke( $args = array(), $assoc_args = array() ) {
        if ( empty( $args ) ) {
            \WP_CLI::error( __( 'Please provide a subcommand.', 'storeagent-ai-for-woocommerce' ) );
            return;
        }

        $subcommand = $args[0];

        // If the subcommand is 'help', display WP-CLI help directly.
        if ( 'help' === $subcommand ) {
            \WP_CLI::run_command( array( 'help', 'storeagent' ) );
            return;
        }

        // Handle the reset-data subcommand.
        if ( 'reset-data' === $subcommand ) {
            $this->reset_data( $assoc_args );
            return;
        }

        // Handle the upload-data subcommand.
        if ( 'upload-data' === $subcommand ) {
            $this->upload_data( $assoc_args );
            return;
        }

        // Handle the test-agents subcommand.
        if ( 'test-agents' === $subcommand ) {
            array_shift( $args );
            $test_agents = new Test_Agents();
            $test_agents->run( $args, $assoc_args );
            return;
        }

        // Remove the subcommand from the args array.
        array_shift( $args );

        // Convert subcommand to class name format (e.g., product-questions -> ProductQuestions).
        $agent_name = str_replace( ' ', '_', ucwords( str_replace( '-', ' ', $subcommand ) ) );
        $class      = 'SAAI\\AI_Agents\\' . $agent_name . '_AI';

        if ( ! class_exists( $class ) ) {
            /* translators: %s: The class name that doesn't exist */
            \WP_CLI::error( sprintf( __( 'The AI agent %s does not exist.', 'storeagent-ai-for-woocommerce' ), $agent_name ) );
            return;
        }

        // Check if this is a help request for a specific agent.
        if ( ! empty( $args ) && 'help' === $args[0] ) {
            // Display help for specific subcommand by using WP-CLI's built-in doc parser.
            $this->show_agent_help( $subcommand, $class );
            return;
        }

        // Instantiate the subcommand class and pass the remaining arguments.
        Bulk_Run_Agent::instance()->bulk_run_agent( $class::instance()->ai_id, $args );
    }

    /**
     * Reset all StoreAgent plugin data for testing the onboarding experience.
     *
     * This command will clear all plugin options, settings, post meta, and transients
     * to reset the plugin to its initial state. Use this to test the onboarding wizard
     * and other first-time experiences.
     *
     * ## OPTIONS
     *
     * [--yes]
     * : Skip confirmation prompt.
     *
     * [--include-notifications]
     * : Also clear all notifications data.
     *
     * ## EXAMPLES
     *
     *     wp storeagent reset-data
     *     wp storeagent reset-data --yes
     *     wp storeagent reset-data --yes --include-notifications
     *
     * @param array $assoc_args Associative arguments.
     */
    private function reset_data( $assoc_args = array() ) {
        global $wpdb;

        // Confirm before proceeding unless --yes flag is provided.
        if ( ! isset( $assoc_args['yes'] ) ) {
            \WP_CLI::confirm( __( 'Are you sure you want to reset all StoreAgent data? This action cannot be undone.', 'storeagent-ai-for-woocommerce' ) );
        }

        $include_notifications = isset( $assoc_args['include-notifications'] );

        \WP_CLI::line( __( 'Starting data reset...', 'storeagent-ai-for-woocommerce' ) );

        // 1. Delete onboarding wizard status.
        delete_option( 'saai_onboarding_wizard_status' );
        \WP_CLI::success( __( 'Deleted onboarding wizard status.', 'storeagent-ai-for-woocommerce' ) );

        // 2. Delete chat widget settings.
        delete_option( 'saai_chat_widget_settings' );
        \WP_CLI::success( __( 'Deleted chat widget settings.', 'storeagent-ai-for-woocommerce' ) );

        // 3. Delete chat post types settings.
        delete_option( 'saai_chat_post_types_settings' );
        delete_option( 'saai_chat_post_types' );
        \WP_CLI::success( __( 'Deleted chat post types settings.', 'storeagent-ai-for-woocommerce' ) );

        // 4. Delete AI agent settings.
        $ai_agents = array(
            'product-description-ai',
            'product-summaries-ai',
            'product-questions-ai',
            'product-tags-generator-ai',
            'category-description-ai',
            'image-alt-tags-ai',
            'review-summaries-ai',
        );

        foreach ( $ai_agents as $agent ) {
            $option_key = 'saai_' . str_replace( '-', '_', $agent );
            delete_option( $option_key );
        }
        \WP_CLI::success( __( 'Deleted AI agent settings.', 'storeagent-ai-for-woocommerce' ) );

        // 5. Delete notification retention settings.
        delete_option( 'saai_notification_retention_days' );
        \WP_CLI::success( __( 'Deleted notification settings.', 'storeagent-ai-for-woocommerce' ) );

        // 6. Delete tool execution times.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $tool_options = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
                'saai_tools_%_last_execution'
            )
        );

        foreach ( $tool_options as $option_name ) {
            delete_option( $option_name );
        }

        if ( ! empty( $tool_options ) ) {
            /* translators: %d: Number of tool execution time records deleted */
            \WP_CLI::success( sprintf( __( 'Deleted %d tool execution time records.', 'storeagent-ai-for-woocommerce' ), count( $tool_options ) ) );
        }

        // 7. Delete rate limiting transients.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $rate_limit_transients = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
                '_transient_saai_last_request_time_%',
                '_transient_saai_requests_made_%'
            )
        );

        foreach ( $rate_limit_transients as $transient_name ) {
            // Remove _transient_ prefix to get the actual transient key.
            $transient_key = str_replace( '_transient_', '', $transient_name );
            delete_transient( $transient_key );
        }

        if ( ! empty( $rate_limit_transients ) ) {
            /* translators: %d: Number of rate limiting transients deleted */
            \WP_CLI::success( sprintf( __( 'Deleted %d rate limiting transients.', 'storeagent-ai-for-woocommerce' ), count( $rate_limit_transients ) ) );
        }

        // 8. Delete post meta (product summaries, post type AI memory flags).
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $deleted_product_summaries = $wpdb->delete(
            $wpdb->postmeta,
            array( 'meta_key' => 'saai_product_summary' ),
            array( '%s' )
        );

        if ( $deleted_product_summaries ) {
            /* translators: %d: Number of product summary meta records deleted */
            \WP_CLI::success( sprintf( __( 'Deleted %d product summary meta records.', 'storeagent-ai-for-woocommerce' ), $deleted_product_summaries ) );
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $deleted_ai_memory_flags = $wpdb->delete(
            $wpdb->postmeta,
            array( 'meta_key' => 'saai_post_type_ai_memory_enabled' ),
            array( '%s' )
        );

        if ( $deleted_ai_memory_flags ) {
            /* translators: %d: Number of AI memory flag meta records deleted */
            \WP_CLI::success( sprintf( __( 'Deleted %d AI memory flag meta records.', 'storeagent-ai-for-woocommerce' ), $deleted_ai_memory_flags ) );
        }

        // 9. Optionally delete notifications from the custom table.
        if ( $include_notifications ) {
            $notifications_table = $wpdb->prefix . 'storeagent_notifications';

            // Check if table exists before trying to delete.
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            $table_exists = $wpdb->get_var( "SHOW TABLES LIKE '{$notifications_table}'" ) === $notifications_table;

            if ( $table_exists ) {
                // Count notifications before deleting.
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                $notification_count = $wpdb->get_var( "SELECT COUNT(*) FROM {$notifications_table}" );

                // Delete all notifications.
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                $wpdb->query( "TRUNCATE TABLE {$notifications_table}" );

                if ( $notification_count > 0 ) {
                    /* translators: %d: Number of notification records deleted */
                    \WP_CLI::success( sprintf( __( 'Deleted %d notification records from database table.', 'storeagent-ai-for-woocommerce' ), $notification_count ) );
                } else {
                    \WP_CLI::success( __( 'Notifications table was already empty.', 'storeagent-ai-for-woocommerce' ) );
                }
            } else {
                \WP_CLI::warning( __( 'Notifications table does not exist.', 'storeagent-ai-for-woocommerce' ) );
            }
        }

        // 10. Delete any other options starting with saai_ (catch-all for any missed options).
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $other_saai_options = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT option_name FROM {$wpdb->options} 
                WHERE option_name LIKE %s 
                AND option_name NOT LIKE %s 
                AND option_name NOT IN (%s, %s, %s, %s, %s)",
                'saai_%',
                '_transient_saai_%',
                'saai_onboarding_wizard_status',
                'saai_chat_widget_settings',
                'saai_chat_post_types_settings',
                'saai_chat_post_types',
                'saai_notification_retention_days'
            )
        );

        // Filter out AI agent options and tool options we already deleted.
        $other_saai_options = array_filter(
            $other_saai_options,
            function ( $option_name ) use ( $ai_agents ) {
                // Skip AI agent options.
                foreach ( $ai_agents as $agent ) {
                    if ( 'saai_' . str_replace( '-', '_', $agent ) === $option_name ) {
                        return false;
                    }
                }
                // Skip tool options.
                if ( strpos( $option_name, 'saai_tools_' ) === 0 ) {
                    return false;
                }
                return true;
            }
        );

        foreach ( $other_saai_options as $option_name ) {
            delete_option( $option_name );
        }

        if ( ! empty( $other_saai_options ) ) {
            /* translators: %d: Number of other SAAI options deleted */
            \WP_CLI::success( sprintf( __( 'Deleted %d other SAAI options.', 'storeagent-ai-for-woocommerce' ), count( $other_saai_options ) ) );
        }

        \WP_CLI::success( __( 'StoreAgent data reset complete! The plugin is now in its initial state.', 'storeagent-ai-for-woocommerce' ) );
    }

    /**
     * Upload post data to AI memory.
     *
     * This command uploads post data (products, posts, etc.) to AI memory for use
     * by the StoreAgent chat assistant. It processes posts in batches and shows
     * progress during the upload process.
     *
     * ## OPTIONS
     *
     * [--post-type=<post_type>]
     * : Upload data for a specific post type only. If not provided, uploads for all enabled post types.
     *
     * ## EXAMPLES
     *
     *     # Upload data for all enabled post types
     *     wp storeagent upload-data
     *
     *     # Upload data for products only
     *     wp storeagent upload-data --post-type=product
     *
     * @param array $assoc_args Associative arguments.
     */
    private function upload_data( $assoc_args = array() ) {
        $data_upload = Data_Upload::instance();
        $post_type   = isset( $assoc_args['post-type'] ) ? $assoc_args['post-type'] : null;

        \WP_CLI::line( __( 'Starting data upload to AI memory...', 'storeagent-ai-for-woocommerce' ) );

        // Get enabled post types.
        $enabled_post_types = Helper::get_post_types_with_ai_memory_enabled();

        if ( empty( $enabled_post_types ) ) {
            \WP_CLI::error( __( 'No post types have AI memory enabled. Please enable AI memory for post types in the StoreAgent settings.', 'storeagent-ai-for-woocommerce' ) );
            return;
        }

        // If specific post type is provided, validate it.
        if ( $post_type && ! in_array( $post_type, $enabled_post_types, true ) ) {
            \WP_CLI::error(
                sprintf(
                    /* translators: %1$s: post type, %2$s: list of enabled post types */
                    __( 'Post type "%1$s" is not enabled for AI memory. Enabled post types: %2$s', 'storeagent-ai-for-woocommerce' ),
                    $post_type,
                    implode( ', ', $enabled_post_types )
                )
            );
            return;
        }

        // Determine which post types to process.
        $post_types_to_process = $post_type ? array( $post_type ) : $enabled_post_types;

        // Show initial progress.
        $progress_data = $data_upload->get_detailed_upload_progress();
        \WP_CLI::line(
            sprintf(
                /* translators: %1$d: processed count, %2$d: total count, %3$s: percentage */
                __( 'Current progress: %1$d / %2$d posts (%3$s%%)', 'storeagent-ai-for-woocommerce' ),
                $progress_data['processed'],
                $progress_data['total'],
                round( $progress_data['percentage'], 1 )
            )
        );

        // Process each post type.
        $total_processed = 0;

        foreach ( $post_types_to_process as $pt ) {
            \WP_CLI::line( '' );
            \WP_CLI::line(
                sprintf(
                    /* translators: %s: post type */
                    __( 'Processing post type: %s', 'storeagent-ai-for-woocommerce' ),
                    $pt
                )
            );

            $batch_count               = 0;
            $consecutive_empty_batches = 0;
            $max_consecutive_empty     = 3; // Allow a few empty batches in case of temporary failures.

            while ( true ) {
                // Process a batch.
                $processed = $data_upload->process_post_data_upload( $pt );

                // Check remaining posts after processing.
                $remaining_after = $data_upload->get_posts_for_upload( $pt, 'count' );

                // If no posts were processed in this batch.
                if ( empty( $processed ) ) {
                    // If there are no remaining posts, we're truly done.
                    if ( 0 === $remaining_after ) {
                        break;
                    }

                    // If there are still remaining posts but batch was empty, it might be failures.
                    // Increment consecutive empty counter and continue.
                    ++$consecutive_empty_batches;

                    // If we've had too many consecutive empty batches with remaining posts,
                    // it likely means all remaining posts are failing. Exit to avoid infinite loop.
                    if ( $consecutive_empty_batches >= $max_consecutive_empty ) {
                        \WP_CLI::warning(
                            sprintf(
                                /* translators: %1$d: consecutive empty batches, %2$d: remaining count */
                                __( '  Warning: %1$d consecutive batches returned no successful uploads, but %2$d posts still remain. Some posts may have failed to upload. Exiting to prevent infinite loop.', 'storeagent-ai-for-woocommerce' ),
                                $consecutive_empty_batches,
                                $remaining_after
                            )
                        );
                        break;
                    }

                    // Show that we're retrying.
                    \WP_CLI::line(
                        sprintf(
                            /* translators: %1$d: remaining count */
                            __( '  Batch returned no successful uploads. %1$d posts still remain. Retrying...', 'storeagent-ai-for-woocommerce' ),
                            $remaining_after
                        )
                    );

                    // Small delay before retrying.
                    usleep( 1000000 ); // 1 second delay for retries.
                    continue;
                }

                // Reset consecutive empty counter on successful batch.
                $consecutive_empty_batches = 0;

                ++$batch_count;
                $total_processed += count( $processed );

                // Show progress.
                $current_progress = $data_upload->get_detailed_upload_progress();
                \WP_CLI::line(
                    sprintf(
                        /* translators: %1$d: batch number, %2$d: posts in batch, %3$d: processed total, %4$d: total posts, %5$s: percentage */
                        __( '  Batch %1$d: Uploaded %2$d posts. Total: %3$d / %4$d (%5$s%%)', 'storeagent-ai-for-woocommerce' ),
                        $batch_count,
                        count( $processed ),
                        $current_progress['processed'],
                        $current_progress['total'],
                        round( $current_progress['percentage'], 1 )
                    )
                );

                // Small delay to avoid overwhelming the server.
                usleep( 500000 ); // 0.5 seconds
            }

            \WP_CLI::success(
                sprintf(
                    /* translators: %1$s: post type, %2$d: number of batches */
                    __( 'Completed processing %1$s (%2$d batches)', 'storeagent-ai-for-woocommerce' ),
                    $pt,
                    $batch_count
                )
            );
        }

        // Show final summary.
        \WP_CLI::line( '' );
        $final_progress = $data_upload->get_detailed_upload_progress();
        \WP_CLI::success(
            sprintf(
                /* translators: %1$d: total processed, %2$d: total posts, %3$s: percentage */
                __( 'Upload complete! Processed %1$d / %2$d posts (%3$s%%)', 'storeagent-ai-for-woocommerce' ),
                $final_progress['processed'],
                $final_progress['total'],
                round( $final_progress['percentage'], 1 )
            )
        );
    }

    /**
     * Display help information for a specific AI agent.
     *
     * @param string $agent_name The agent name in command format.
     * @param string $class_name The fully qualified class name.
     */
    private function show_agent_help( $agent_name, $class_name ) {
        // Get the agent instance.
        $agent_instance = $class_name::instance();

        // Build a help message with available subcommands based on the agent's cli method.
        \WP_CLI::line( '' );
        /* translators: %s: The agent name */
        \WP_CLI::line( sprintf( __( '%s CLI Commands', 'storeagent-ai-for-woocommerce' ), $agent_name ) );
        \WP_CLI::line( str_repeat( '-', strlen( $agent_name ) + 13 ) );
        \WP_CLI::line( '' );

        // Try to get available subcommands via reflection.
        try {
            $reflection  = new \ReflectionClass( $class_name );
            $cli_method  = $reflection->getMethod( 'cli' );
            $doc_comment = $cli_method->getDocComment();

            if ( $doc_comment ) {
                // Extract and display the documentation.
                $doc_lines   = explode( "\n", $doc_comment );
                $started_doc = false;

                foreach ( $doc_lines as $line ) {
                    $line = preg_replace( '/^\s*\*\s*/', '', trim( $line ) );

                    // Skip the opening /** and closing */ lines.
                    if ( 0 === strpos( $line, '/**' ) || 0 === strpos( $line, '*/' ) ) {
                        continue;
                    }

                    // Skip empty lines at the beginning.
                    if ( ! $started_doc && '' === $line ) {
                        continue;
                    }

                    // Skip lines with @ annotations.
                    if ( 0 === strpos( $line, '@' ) ) {
                        continue;
                    }

                    // We've found content, set the flag.
                    $started_doc = true;

                    // Output the line if it's not empty and not just a slash.
                    if ( '' !== $line && '/' !== $line ) {
                        \WP_CLI::line( $line );
                    }
                }
                \WP_CLI::line( '' );
            }

            // Check for specific subcommands in the method body.
            $file = new \SplFileObject( $reflection->getFileName() );
            $file->seek( $cli_method->getStartLine() );

            $method_body  = '';
            $current_line = $file->key();

            while ( $current_line <= $cli_method->getEndLine() ) {
                $method_body .= $file->current();
                $file->next();
                $current_line = $file->key();
            }

            // Extract case statements from switch as available commands.
            preg_match_all( '/case\s+[\'"]([^\'"]+)[\'"]:/i', $method_body, $matches );

            if ( ! empty( $matches[1] ) ) {
                \WP_CLI::line( __( 'AVAILABLE SUBCOMMANDS:', 'storeagent-ai-for-woocommerce' ) );
                foreach ( $matches[1] as $subcmd ) {
                    \WP_CLI::line( '  - ' . $subcmd );
                }
                \WP_CLI::line( '' );
                \WP_CLI::line( sprintf( __( 'USAGE:', 'storeagent-ai-for-woocommerce' ) ) );
                \WP_CLI::line( sprintf( '  wp storeagent %s <subcommand> [--options]', $agent_name ) );
                \WP_CLI::line( '' );
            } else {
                \WP_CLI::line( __( 'No subcommands available for this agent.', 'storeagent-ai-for-woocommerce' ) );
            }
        } catch ( \Exception $e ) {
            \WP_CLI::line( __( 'No detailed help information available for this agent.', 'storeagent-ai-for-woocommerce' ) );
        }
    }
}
