<?php
/**
 * Bots Endpoint for BoostBot REST API.
 *
 * @package BoostBot
 * @since   1.1.0
 */

// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * BoostBot_Bots_Endpoint class.
 *
 * Handles CRUD operations for chatbots.
 *
 * @since 1.1.0
 */
class BoostBot_Bots_Endpoint extends BoostBot_Base_Endpoint {

    /**
     * The base path for this endpoint.
     *
     * @var string
     */
    protected $rest_base = 'bots';

    /**
     * Register the routes for this endpoint.
     */
    public function register() {
        // GET /bots - List all bots
        $this->register_route(
            '',
            'GET',
            array( $this, 'get_bots' ),
            array( $this, 'admin_access' )
        );

        // POST /bots - Create new bot
        $this->register_route(
            '',
            'POST',
            array( $this, 'create_bot' ),
            array( $this, 'admin_access' )
        );

        // GET /bots/stats - Get statistics (must be before /bots/{id})
        $this->register_route(
            '/stats',
            'GET',
            array( $this, 'get_stats' ),
            array( $this, 'admin_access' )
        );

        // GET /bots/{id} - Get single bot
        $this->register_route(
            '/(?P<id>\d+)',
            'GET',
            array( $this, 'get_bot' ),
            array( $this, 'admin_access' )
        );

        // PUT /bots/{id} - Update bot
        $this->register_route(
            '/(?P<id>\d+)',
            'PUT',
            array( $this, 'update_bot' ),
            array( $this, 'admin_access' )
        );

        // DELETE /bots/{id} - Delete bot
        $this->register_route(
            '/(?P<id>\d+)',
            'DELETE',
            array( $this, 'delete_bot' ),
            array( $this, 'admin_access' )
        );

        // POST /bots/{id}/duplicate - Duplicate bot
        $this->register_route(
            '/(?P<id>\d+)/duplicate',
            'POST',
            array( $this, 'duplicate_bot' ),
            array( $this, 'admin_access' )
        );

        // PUT /bots/{id}/default - Set as default
        $this->register_route(
            '/(?P<id>\d+)/default',
            'PUT',
            array( $this, 'set_default' ),
            array( $this, 'admin_access' )
        );

        // GET /content-items - Get content items for display conditions
        register_rest_route(
            $this->namespace,
            '/content-items',
            array(
                'methods'             => 'GET',
                'callback'            => array( $this, 'get_content_items' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // GET /bots/{id}/overriding-bots - Get bots that override this one due to more specific conditions
        $this->register_route(
            '/(?P<id>\d+)/overriding-bots',
            'GET',
            array( $this, 'get_overriding_bots' ),
            array( $this, 'admin_access' )
        );

        // GET /bots/{id}/overridden-bots - Get bots that this one overrides due to its more specific conditions
        $this->register_route(
            '/(?P<id>\d+)/overridden-bots',
            'GET',
            array( $this, 'get_overridden_bots' ),
            array( $this, 'admin_access' )
        );

        // GET /bots/by-slug/{slug} - Get bot by slug (useful for idempotent provisioning)
        $this->register_route(
            '/by-slug/(?P<slug>[a-zA-Z0-9_-]+)',
            'GET',
            array( $this, 'get_bot_by_slug' ),
            array( $this, 'admin_access' )
        );

        // POST /bots/provision - Batch-create bot with personality, widgets, and assignments
        $this->register_route(
            '/provision',
            'POST',
            array( $this, 'provision_bot' ),
            array( $this, 'admin_access' )
        );
    }

    /**
     * Get content items by post type for display conditions.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_content_items( $request ) {
        $post_type      = sanitize_text_field( $request->get_param( 'post_type' ) );
        $search         = sanitize_text_field( $request->get_param( 'search' ) );
        $exclude_bot_id = absint( $request->get_param( 'exclude_bot_id' ) );

        if ( empty( $post_type ) ) {
            return $this->error( 'post_type is required' );
        }

        // Validate post type exists
        if ( ! post_type_exists( $post_type ) ) {
            return $this->error( 'Invalid post type' );
        }

        // Get total count first
        $count_args = array(
            'post_type'      => $post_type,
            'post_status'    => 'publish',
            'posts_per_page' => -1,
            'fields'         => 'ids',
        );
        $total_count = count( get_posts( $count_args ) );

        // Get posts with optional search
        $args = array(
            'post_type'      => $post_type,
            'post_status'    => 'publish',
            'posts_per_page' => 50, // Limit results
            'orderby'        => 'title',
            'order'          => 'ASC',
        );

        // If search term provided, search in title
        if ( ! empty( $search ) ) {
            $args['s']              = $search;
            $args['posts_per_page'] = 50; // More results when searching
        }

        $posts = get_posts( $args );

        // Get bot assignments for these posts
        $post_ids = array_map( function( $post ) {
            return $post->ID;
        }, $posts );
        $bot_assignments = $this->get_bot_assignments_for_posts( $post_ids, $post_type, $exclude_bot_id );

        $items = array();
        foreach ( $posts as $post ) {
            $item = array(
                'id'    => $post->ID,
                'title' => $post->post_title,
            );
            
            // Add assigned bot info if exists
            if ( isset( $bot_assignments[ $post->ID ] ) ) {
                $item['assigned_bot'] = $bot_assignments[ $post->ID ];
            }
            
            $items[] = $item;
        }

        return $this->success( array(
            'items'      => $items,
            'post_type'  => $post_type,
            'total'      => $total_count,
            'showing'    => count( $items ),
            'has_search' => ! empty( $search ),
        ) );
    }

    /**
     * Get bot assignments for posts based on display_conditions.
     *
     * @param array  $post_ids       Array of post IDs to check.
     * @param string $post_type      The post type being checked.
     * @param int    $exclude_bot_id Bot ID to exclude from results.
     * @return array Map of post_id => { bot_id, bot_name }
     */
    private function get_bot_assignments_for_posts( $post_ids, $post_type, $exclude_bot_id = 0 ) {
        if ( empty( $post_ids ) ) {
            return array();
        }

        $bot_model = new BoostBot_Bot_Model();
        $bots = $bot_model->get_all( array( 'where' => array( 'is_active' => 1 ) ) );
        
        $assignments = array();
        
        foreach ( $bots as $bot ) {
            // Skip the current bot being edited
            if ( $exclude_bot_id && $bot->id == $exclude_bot_id ) {
                continue;
            }
            
            $conditions = json_decode( $bot->display_conditions, true );
            if ( empty( $conditions ) || ! is_array( $conditions ) ) {
                continue;
            }
            
            foreach ( $conditions as $condition ) {
                if ( ! isset( $condition['type'] ) || $condition['type'] !== 'post_type' ) {
                    continue;
                }
                if ( ! isset( $condition['operator'] ) || $condition['operator'] !== 'include' ) {
                    continue;
                }
                
                $value = isset( $condition['value'] ) ? $condition['value'] : '';
                if ( empty( $value ) ) {
                    continue;
                }
                
                // Parse value - can be "post_type" or "post_type:id" or "post_type:id1,id2"
                if ( strpos( $value, ':' ) !== false ) {
                    list( $type, $ids_str ) = explode( ':', $value, 2 );
                    
                    // Only process if matching post type
                    if ( $type !== $post_type ) {
                        continue;
                    }
                    
                    $ids = array_map( 'intval', explode( ',', $ids_str ) );
                    
                    foreach ( $ids as $id ) {
                        if ( in_array( $id, $post_ids ) && ! isset( $assignments[ $id ] ) ) {
                            $assignments[ $id ] = array(
                                'bot_id'   => $bot->id,
                                'bot_name' => $bot->bot_name,
                            );
                        }
                    }
                }
            }
        }
        
        return $assignments;
    }

    /**
     * Get all bots.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_bots( $request ) {
        $model = new BoostBot_Bot_Model();

        // Pagination
        $pagination = $this->get_pagination_params( $request );

        // Filters
        $filters = array();
        if ( $request->has_param( 'active' ) ) {
            $filters['is_active'] = (bool) $request->get_param( 'active' ) ? 1 : 0;
        }
        if ( $request->has_param( 'language' ) ) {
            $filters['language'] = $request->get_param( 'language' );
        }

        $bots = $model->get_all( array(
            'where'   => $filters,
            'orderby' => 'priority',
            'order'   => 'DESC',
            'limit'   => $pagination['per_page'],
            'offset'  => $pagination['offset'],
        ) );

        $total = $model->count( $filters );

        // Parse JSON fields
        foreach ( $bots as &$bot ) {
            $bot = $this->parse_json_fields( $bot );
        }

        return $this->success( array(
            'bots'       => $bots,
            'pagination' => $this->build_pagination_response(
                $pagination['page'],
                $pagination['per_page'],
                $total
            ),
        ) );
    }

    /**
     * Get single bot.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function get_bot( $request ) {
        $id = absint( $request->get_param( 'id' ) );

        $model = new BoostBot_Bot_Model();
        $bot   = $model->get( $id );

        if ( ! $bot ) {
            return $this->not_found( 'Bot' );
        }

        $bot = $this->parse_json_fields( $bot );

        return $this->success( array( 'bot' => $bot ) );
    }

    /**
     * Create new bot.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function create_bot( $request ) {
        $model = new BoostBot_Bot_Model();

        $data               = $request->get_json_params();
        $data['created_by'] = get_current_user_id();

        // Generate slug if not provided
        if ( empty( $data['bot_slug'] ) && ! empty( $data['bot_name'] ) ) {
            $data['bot_slug'] = sanitize_title( $data['bot_name'] );
        }

        $id = $model->create( $data );

        if ( is_wp_error( $id ) ) {
            return $this->error( $id->get_error_message(), 400 );
        }

        if ( ! $id ) {
            return $this->error( __( 'Failed to create bot', 'boostbot' ), 400 );
        }

        $bot = $model->get( $id );
        $bot = $this->parse_json_fields( $bot );

        // Sync WooCommerce widgets for new bot if preset is linked.
        $preset_id = $bot->woocommerce_preset_id ?? null;
        if ( $preset_id && class_exists( 'BoostBot_WooCommerce_Widget_Manager' ) ) {
            try {
                $manager = new BoostBot_WooCommerce_Widget_Manager();
                $manager->sync_bot_widgets( $id, (int) $preset_id );
            } catch ( \Throwable $e ) {
                if ( class_exists( 'BoostBot_Logger' ) ) {
                    BoostBot_Logger::error( 'WooCommerce widget sync failed on bot create', array(
                        'bot_id' => $id,
                        'error'  => $e->getMessage(),
                    ) );
                }
            }
        }

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot created', array( 'id' => $id, 'name' => $bot->bot_name ) );
        }

        return $this->success( array(
            'bot'     => $bot,
            'message' => __( 'Bot created successfully', 'boostbot' ),
        ), 201 );
    }

    /**
     * Update bot.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function update_bot( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Bot_Model();

        $old_bot = $model->get( $id );
        if ( ! $old_bot ) {
            return $this->not_found( 'Bot' );
        }

        $old_preset_id = $old_bot->woocommerce_preset_id ?? null;
        $old_language  = $old_bot->language ?? null;

        $data   = $request->get_json_params();
        $result = $model->update( $id, $data );

        if ( is_wp_error( $result ) ) {
            return $this->error( $result->get_error_message(), 400 );
        }

        if ( ! $result ) {
            return $this->error( __( 'Failed to update bot', 'boostbot' ), 400 );
        }

        $bot = $model->get( $id );
        $bot = $this->parse_json_fields( $bot );

        // Sync WooCommerce widgets when preset link changes.
        $new_preset_id = $bot->woocommerce_preset_id ?? null;
        $new_language  = $bot->language ?? null;
        if ( $new_preset_id != $old_preset_id || $new_language !== $old_language ) {
            if ( class_exists( 'BoostBot_WooCommerce_Widget_Manager' ) ) {
                try {
                    $manager = new BoostBot_WooCommerce_Widget_Manager();
                    $manager->sync_bot_widgets( $id, $new_preset_id ? (int) $new_preset_id : null );
                } catch ( \Throwable $e ) {
                    if ( class_exists( 'BoostBot_Logger' ) ) {
                        BoostBot_Logger::error( 'WooCommerce widget sync failed on bot update', array(
                            'bot_id' => $id,
                            'error'  => $e->getMessage(),
                        ) );
                    }
                }
            }
        }

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot updated', array( 'id' => $id ) );
        }

        return $this->success( array(
            'bot'     => $bot,
            'message' => __( 'Bot updated successfully', 'boostbot' ),
        ) );
    }

    /**
     * Delete bot.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function delete_bot( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Bot_Model();

        $bot = $model->get( $id );
        if ( ! $bot ) {
            return $this->not_found( 'Bot' );
        }

        // Prevent deleting default bot
        if ( $bot->is_default ) {
            return $this->error( __( 'Cannot delete the default bot', 'boostbot' ), 400 );
        }

        $result = $model->delete( $id );

        if ( ! $result ) {
            return $this->error( __( 'Failed to delete bot', 'boostbot' ), 500 );
        }

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot deleted', array( 'id' => $id ) );
        }

        return $this->success( array(
            'message' => __( 'Bot deleted successfully', 'boostbot' ),
        ) );
    }

    /**
     * Duplicate bot.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function duplicate_bot( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Bot_Model();

        $new_id = $model->duplicate( $id );

        if ( ! $new_id ) {
            return $this->error( __( 'Failed to duplicate bot', 'boostbot' ), 500 );
        }

        $bot = $model->get( $new_id );
        $bot = $this->parse_json_fields( $bot );

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot duplicated', array( 'original_id' => $id, 'new_id' => $new_id ) );
        }

        return $this->success( array(
            'bot'     => $bot,
            'message' => __( 'Bot duplicated successfully', 'boostbot' ),
        ), 201 );
    }

    /**
     * Set bot as default.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function set_default( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Bot_Model();

        if ( ! $model->get( $id ) ) {
            return $this->not_found( 'Bot' );
        }

        $result = $model->set_default( $id );

        if ( ! $result ) {
            return $this->error( __( 'Failed to set default bot', 'boostbot' ), 500 );
        }

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Default bot changed', array( 'id' => $id ) );
        }

        return $this->success( array(
            'message' => __( 'Default bot updated', 'boostbot' ),
        ) );
    }

    /**
     * Get bots that override a given bot due to more specific display conditions.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_overriding_bots( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Bot_Model();

        $current_bot = $model->get( $id );
        if ( ! $current_bot ) {
            return $this->not_found( 'Bot' );
        }

        // Get all other active bots
        $all_bots = $model->get_active();
        if ( empty( $all_bots ) ) {
            return $this->success( array( 'overriding_bots' => array() ) );
        }

        $resolver       = new BoostBot_Bot_Resolver();
        $overriding     = array();

        // Parse current bot's conditions
        $current_conditions = $current_bot->display_conditions;
        if ( is_string( $current_conditions ) && ! empty( $current_conditions ) ) {
            $current_conditions = json_decode( $current_conditions, true );
        }
        if ( ! is_array( $current_conditions ) ) {
            $current_conditions = array();
        }

        // Get specificity info for current bot
        $current_scope = $this->describe_bot_scope( $current_bot, $current_conditions );

        foreach ( $all_bots as $other_bot ) {
            if ( (int) $other_bot->id === $id ) {
                continue; // Skip self
            }

            // Parse other bot's conditions
            $other_conditions = $other_bot->display_conditions;
            if ( is_string( $other_conditions ) && ! empty( $other_conditions ) ) {
                $other_conditions = json_decode( $other_conditions, true );
            }
            if ( ! is_array( $other_conditions ) ) {
                $other_conditions = array();
            }

            // Check if other bot's conditions overlap and are more specific
            $overlap_info = $this->find_condition_overlaps( $current_conditions, $other_conditions, $current_bot, $other_bot );

            if ( ! empty( $overlap_info['overlaps'] ) ) {
                $overriding[] = array(
                    'id'                 => (int) $other_bot->id,
                    'bot_name'           => $other_bot->bot_name,
                    'is_default'         => ! empty( $other_bot->is_default ),
                    'overlapping_scopes' => $overlap_info['overlaps'],
                    'conditions_summary' => $this->summarize_conditions( $other_conditions ),
                );
            }
        }

        return $this->success( array(
            'current_bot'    => array(
                'id'       => (int) $current_bot->id,
                'bot_name' => $current_bot->bot_name,
                'scope'    => $current_scope,
            ),
            'overriding_bots' => $overriding,
        ) );
    }

    /**
     * Get bots that this bot overrides due to its more specific display conditions.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_overridden_bots( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Bot_Model();

        $current_bot = $model->get( $id );
        if ( ! $current_bot ) {
            return $this->not_found( 'Bot' );
        }

        // Get all other active bots
        $all_bots = $model->get_active();
        if ( empty( $all_bots ) ) {
            return $this->success( array( 'overridden_bots' => array() ) );
        }

        // Parse current bot's conditions
        $current_conditions = $current_bot->display_conditions;
        if ( is_string( $current_conditions ) && ! empty( $current_conditions ) ) {
            $current_conditions = json_decode( $current_conditions, true );
        }
        if ( ! is_array( $current_conditions ) ) {
            $current_conditions = array();
        }

        // Get specificity info for current bot
        $current_scope       = $this->describe_bot_scope( $current_bot, $current_conditions );
        $current_specificity = $this->get_max_specificity( $current_conditions );

        $overridden = array();

        foreach ( $all_bots as $other_bot ) {
            if ( (int) $other_bot->id === $id ) {
                continue; // Skip self
            }

            // Parse other bot's conditions
            $other_conditions = $other_bot->display_conditions;
            if ( is_string( $other_conditions ) && ! empty( $other_conditions ) ) {
                $other_conditions = json_decode( $other_conditions, true );
            }
            if ( ! is_array( $other_conditions ) ) {
                $other_conditions = array();
            }

            $other_specificity = $this->get_max_specificity( $other_conditions );

            // For default bots with no include conditions, specificity is very low
            if ( ! empty( $other_bot->is_default ) ) {
                $has_includes = false;
                foreach ( $other_conditions as $cond ) {
                    if ( isset( $cond['operator'] ) && 'include' === $cond['operator'] && ( $cond['type'] ?? '' ) !== 'all' ) {
                        $has_includes = true;
                        break;
                    }
                }
                if ( ! $has_includes ) {
                    $other_specificity = 5;
                }
            }

            // Current bot is more specific - it overrides the other bot
            if ( $current_specificity > $other_specificity ) {
                // Check for actual overlap
                $overlap_scopes = array();
                foreach ( $current_conditions as $current_cond ) {
                    if ( ( $current_cond['operator'] ?? '' ) !== 'include' ) {
                        continue;
                    }
                    // Check if this overlaps with other bot's scope
                    if ( $this->conditions_overlap( $other_conditions, $current_cond ) || ! empty( $other_bot->is_default ) ) {
                        $overlap_scopes[] = $this->describe_single_condition( $current_cond );
                    }
                }

                if ( ! empty( $overlap_scopes ) || ! empty( $other_bot->is_default ) ) {
                    $overridden[] = array(
                        'id'                 => (int) $other_bot->id,
                        'bot_name'           => $other_bot->bot_name,
                        'is_default'         => ! empty( $other_bot->is_default ),
                        'their_scope'        => $this->describe_bot_scope( $other_bot, $other_conditions ),
                        'overlapping_scopes' => $overlap_scopes,
                    );
                }
            }
        }

        return $this->success( array(
            'current_bot' => array(
                'id'       => (int) $current_bot->id,
                'bot_name' => $current_bot->bot_name,
                'scope'    => $current_scope,
            ),
            'overridden_bots' => $overridden,
        ) );
    }

    /**
     * Describe the scope of a bot's display conditions.
     *
     * @param object $bot        The bot object.
     * @param array  $conditions The parsed conditions.
     * @return string Human-readable scope description.
     */
    private function describe_bot_scope( $bot, $conditions ) {
        if ( ! empty( $bot->is_default ) ) {
            return __( 'כל האתר (ברירת מחדל)', 'boostbot' );
        }

        if ( empty( $conditions ) ) {
            return __( 'לא מוגדר', 'boostbot' );
        }

        $includes = array();
        foreach ( $conditions as $cond ) {
            if ( ( $cond['operator'] ?? '' ) !== 'include' ) {
                continue;
            }
            $type = $cond['type'] ?? '';
            switch ( $type ) {
                case 'all':
                    $includes[] = __( 'כל האתר', 'boostbot' );
                    break;
                case 'homepage':
                    $includes[] = __( 'עמוד הבית', 'boostbot' );
                    break;
                case 'post_type':
                    $value = $cond['value'] ?? '';
                    if ( strpos( $value, ':' ) !== false ) {
                        list( $pt, $ids ) = explode( ':', $value, 2 );
                        $count = count( array_filter( explode( ',', $ids ) ) );
                        /* translators: %1$d: number of items, %2$s: post type */
                        $includes[] = sprintf( __( '%1$d פריטים ספציפיים (%2$s)', 'boostbot' ), $count, $pt );
                    } else {
                        /* translators: %s: post type */
                        $includes[] = sprintf( __( 'כל ה%s', 'boostbot' ), $value ?: 'תוכן' );
                    }
                    break;
                case 'taxonomy':
                    $includes[] = __( 'קטגוריה/תגית', 'boostbot' );
                    break;
                case 'url_contains':
                    $includes[] = __( 'URL מכיל', 'boostbot' );
                    break;
                default:
                    $includes[] = $type;
            }
        }

        return ! empty( $includes ) ? implode( ', ', $includes ) : __( 'ללא תנאים', 'boostbot' );
    }

    /**
     * Find overlapping conditions where other_bot is more specific.
     *
     * @param array  $current_conditions Current bot's conditions.
     * @param array  $other_conditions   Other bot's conditions.
     * @param object $current_bot        Current bot object.
     * @param object $other_bot          Other bot object.
     * @return array Overlap info with descriptions.
     */
    private function find_condition_overlaps( $current_conditions, $other_conditions, $current_bot, $other_bot ) {
        $overlaps = array();

        // If current bot is default (shows everywhere), any bot with specific includes overrides it
        if ( ! empty( $current_bot->is_default ) ) {
            foreach ( $other_conditions as $other_cond ) {
                if ( ( $other_cond['operator'] ?? '' ) !== 'include' ) {
                    continue;
                }
                $type = $other_cond['type'] ?? '';
                if ( 'all' === $type ) {
                    continue; // Same scope, not more specific
                }
                // This is a more specific condition
                $overlaps[] = $this->describe_single_condition( $other_cond );
            }
            return array( 'overlaps' => $overlaps );
        }

        // For non-default bots, check if other bot's conditions are within current bot's scope
        // and more specific
        $current_specificity = $this->get_max_specificity( $current_conditions );
        $other_specificity   = $this->get_max_specificity( $other_conditions );

        // Other bot is more specific
        if ( $other_specificity > $current_specificity ) {
            // Check for actual overlap
            foreach ( $other_conditions as $other_cond ) {
                if ( ( $other_cond['operator'] ?? '' ) !== 'include' ) {
                    continue;
                }
                // Check if this overlaps with current bot's scope
                if ( $this->conditions_overlap( $current_conditions, $other_cond ) ) {
                    $overlaps[] = $this->describe_single_condition( $other_cond );
                }
            }
        }

        return array( 'overlaps' => $overlaps );
    }

    /**
     * Get maximum specificity score from conditions.
     *
     * @param array $conditions The conditions array.
     * @return int Max specificity score.
     */
    private function get_max_specificity( $conditions ) {
        $max = 0;
        $specificity_map = array(
            'all'          => 10,
            'homepage'     => 80,
            'page'         => 100,
            'post_type'    => 50, // Will be adjusted for specific items
            'taxonomy'     => 60,
            'url_contains' => 55,
            'url_exact'    => 95,
            'user_role'    => 40,
        );

        foreach ( $conditions as $cond ) {
            if ( ( $cond['operator'] ?? '' ) !== 'include' ) {
                continue;
            }
            $type  = $cond['type'] ?? '';
            $value = $cond['value'] ?? '';
            $score = $specificity_map[ $type ] ?? 20;

            // Adjust for specific post_type items
            if ( 'post_type' === $type && is_string( $value ) && strpos( $value, ':' ) !== false ) {
                $score = 100;
            }

            if ( $score > $max ) {
                $max = $score;
            }
        }

        return $max;
    }

    /**
     * Check if a condition overlaps with a set of conditions.
     *
     * @param array $current_conditions Set of conditions to check against.
     * @param array $other_condition    Single condition to check.
     * @return bool True if overlaps.
     */
    private function conditions_overlap( $current_conditions, $other_condition ) {
        $other_type  = $other_condition['type'] ?? '';
        $other_value = $other_condition['value'] ?? '';

        foreach ( $current_conditions as $current ) {
            if ( ( $current['operator'] ?? '' ) !== 'include' ) {
                continue;
            }
            $current_type  = $current['type'] ?? '';
            $current_value = $current['value'] ?? '';

            // "All" overlaps with everything
            if ( 'all' === $current_type ) {
                return true;
            }

            // Same type checks
            if ( $current_type === $other_type ) {
                // Same type, likely overlaps (simplified check)
                // For post_type, check if other is subset of current
                if ( 'post_type' === $current_type ) {
                    // Current is all of type, other is specific items of same type
                    if ( is_string( $current_value ) && ! strpos( $current_value, ':' ) ) {
                        // Current: "page" (all pages)
                        if ( is_string( $other_value ) && strpos( $other_value, ':' ) !== false ) {
                            list( $other_pt, ) = explode( ':', $other_value, 2 );
                            if ( $other_pt === $current_value ) {
                                return true; // Other is specific pages within current's "all pages"
                            }
                        }
                    }
                }
                return true; // Same type = overlap
            }

            // post_type overlaps with page type
            if ( 'post_type' === $current_type && 'page' === $other_type ) {
                if ( is_string( $current_value ) && ( $current_value === 'page' || strpos( $current_value, 'page' ) === 0 ) ) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Describe a single condition in human-readable form.
     *
     * @param array $condition The condition.
     * @return string Description.
     */
    private function describe_single_condition( $condition ) {
        $type  = $condition['type'] ?? '';
        $value = $condition['value'] ?? '';

        switch ( $type ) {
            case 'all':
                return __( 'כל האתר', 'boostbot' );
            case 'homepage':
                return __( 'עמוד הבית', 'boostbot' );
            case 'post_type':
                if ( is_string( $value ) && strpos( $value, ':' ) !== false ) {
                    list( $pt, $ids ) = explode( ':', $value, 2 );
                    $count   = count( array_filter( explode( ',', $ids ) ) );
                    $pt_name = $this->get_post_type_label( $pt );
                    /* translators: %1$d: count, %2$s: post type name */
                    return sprintf( __( '%1$d %2$s ספציפיים', 'boostbot' ), $count, $pt_name );
                }
                $pt_name = $this->get_post_type_label( $value );
                /* translators: %s: post type name */
                return sprintf( __( 'כל ה%s', 'boostbot' ), $pt_name );
            case 'taxonomy':
                return __( 'קטגוריה/תגית ספציפית', 'boostbot' );
            case 'url_contains':
                /* translators: %s: URL pattern */
                return sprintf( __( 'URL מכיל: %s', 'boostbot' ), $value );
            default:
                return $type;
        }
    }

    /**
     * Get human-readable label for post type.
     *
     * @param string $post_type The post type slug.
     * @return string Label.
     */
    private function get_post_type_label( $post_type ) {
        $labels = array(
            'post'    => __( 'פוסטים', 'boostbot' ),
            'page'    => __( 'עמודים', 'boostbot' ),
            'product' => __( 'מוצרים', 'boostbot' ),
        );
        return $labels[ $post_type ] ?? $post_type;
    }

    /**
     * Summarize conditions for display.
     *
     * @param array $conditions The conditions array.
     * @return string Summary.
     */
    private function summarize_conditions( $conditions ) {
        $parts = array();
        foreach ( $conditions as $cond ) {
            if ( ( $cond['operator'] ?? '' ) === 'include' ) {
                $parts[] = $this->describe_single_condition( $cond );
            }
        }
        return ! empty( $parts ) ? implode( ', ', $parts ) : __( 'ללא הגדרות', 'boostbot' );
    }

    /**
     * Get bot statistics.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_stats( $request ) {
        $model  = new BoostBot_Bot_Model();
        $counts = $model->get_counts();

        return $this->success( $counts );
    }

    /**
     * Get bot by slug.
     *
     * Useful for idempotent scripts that need to check if a bot exists
     * before creating one (e.g., companion plugin provisioning).
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function get_bot_by_slug( $request ) {
        $slug  = sanitize_title( $request->get_param( 'slug' ) );
        $model = new BoostBot_Bot_Model();

        $bot = $model->get_by_slug( $slug );

        if ( ! $bot ) {
            return $this->not_found( 'Bot' );
        }

        $bot = $this->parse_json_fields( $bot );

        return $this->success( array( 'bot' => $bot ) );
    }

    /**
     * Batch-provision a complete bot setup in one request.
     *
     * Creates (optionally) a personality, widgets, opening screen preset,
     * and the bot itself — all linked together. If any step fails, previously
     * created resources in this call are cleaned up (best-effort rollback).
     *
     * Expected JSON body:
     * {
     *   "bot": { ...bot fields... },
     *   "personality": { ...personality fields... },           // optional
     *   "widgets": [ { ...widget fields... }, ... ],           // optional
     *   "opening_screen": { ...opening screen preset fields... } // optional
     * }
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function provision_bot( $request ) {
        $data = $request->get_json_params();

        if ( empty( $data['bot'] ) || ! is_array( $data['bot'] ) ) {
            return $this->error( __( 'bot object is required', 'boostbot' ), 400 );
        }

        // Track created IDs for rollback on failure.
        $created = array(
            'personality_id'          => null,
            'widget_ids'              => array(),
            'opening_screen_preset_id' => null,
            'bot_id'                  => null,
        );

        try {
            // ── 1. Personality ───────────────────────────────────────
            if ( ! empty( $data['personality'] ) && is_array( $data['personality'] ) ) {
                $pers_model = new BoostBot_Personality_Model();
                $pers_data  = $data['personality'];

                if ( empty( $pers_data['slug'] ) && ! empty( $pers_data['name'] ) ) {
                    $pers_data['slug'] = sanitize_title( $pers_data['name'] );
                }

                $pid = $pers_model->create( $pers_data );
                if ( ! $pid || is_wp_error( $pid ) ) {
                    throw new \RuntimeException( 'Failed to create personality' );
                }
                $created['personality_id'] = $pid;
            }

            // ── 2. Widgets ──────────────────────────────────────────
            if ( ! empty( $data['widgets'] ) && is_array( $data['widgets'] ) ) {
                $widget_model = new BoostBot_Widget_Model();

                foreach ( $data['widgets'] as $w ) {
                    if ( empty( $w['name'] ) || empty( $w['type'] ) ) {
                        continue; // Skip malformed entries.
                    }

                    $wid = $widget_model->create_with_fields( $w );
                    if ( $wid ) {
                        $created['widget_ids'][] = $wid;
                    }
                }
            }

            // ── 3. Opening Screen Preset ────────────────────────────
            if ( ! empty( $data['opening_screen'] ) && is_array( $data['opening_screen'] ) ) {
                $os_model = new BoostBot_Opening_Screen_Preset_Model();
                $os_data  = $data['opening_screen'];

                if ( empty( $os_data['slug'] ) && ! empty( $os_data['name'] ) ) {
                    $os_data['slug'] = sanitize_title( $os_data['name'] );
                }

                $os_id = $os_model->create( $os_data );
                if ( $os_id && ! is_wp_error( $os_id ) ) {
                    $created['opening_screen_preset_id'] = $os_id;
                }
            }

            // ── 4. Bot ──────────────────────────────────────────────
            $bot_model = new BoostBot_Bot_Model();
            $bot_data  = $data['bot'];
            $bot_data['created_by'] = get_current_user_id();

            // Auto-generate slug if missing.
            if ( empty( $bot_data['bot_slug'] ) && ! empty( $bot_data['bot_name'] ) ) {
                $bot_data['bot_slug'] = sanitize_title( $bot_data['bot_name'] );
            }

            // Link resources created above.
            if ( $created['personality_id'] ) {
                $bot_data['personality_id'] = $created['personality_id'];
            }
            if ( $created['opening_screen_preset_id'] ) {
                $bot_data['opening_screen_preset_id'] = $created['opening_screen_preset_id'];
            }

            $bot_id = $bot_model->create( $bot_data );
            if ( ! $bot_id || is_wp_error( $bot_id ) ) {
                throw new \RuntimeException( is_wp_error( $bot_id ) ? $bot_id->get_error_message() : 'Failed to create bot' );
            }
            $created['bot_id'] = $bot_id;

            // ── 5. Assign widgets to bot ────────────────────────────
            if ( ! empty( $created['widget_ids'] ) ) {
                $widget_model = $widget_model ?? new BoostBot_Widget_Model();
                foreach ( $created['widget_ids'] as $wid ) {
                    $widget_model->assign_to_chatbot( $wid, $bot_id );
                }
            }

        } catch ( \RuntimeException $e ) {
            // Best-effort rollback.
            $this->rollback_provision( $created );
            return $this->error( $e->getMessage(), 500 );
        }

        // ── Build response ──────────────────────────────────────────
        $bot = $bot_model->get( $bot_id );
        $bot = $this->parse_json_fields( $bot );

        $response = array(
            'bot'     => $bot,
            'created' => array(
                'personality_id'          => $created['personality_id'],
                'widget_ids'              => $created['widget_ids'],
                'opening_screen_preset_id' => $created['opening_screen_preset_id'],
            ),
            'message' => __( 'Bot provisioned successfully', 'boostbot' ),
        );

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot provisioned via API', array(
                'bot_id'     => $bot_id,
                'widgets'    => count( $created['widget_ids'] ),
                'personality' => $created['personality_id'],
            ) );
        }

        return $this->success( $response, 201 );
    }

    /**
     * Best-effort rollback of resources created during a failed provision.
     *
     * @param array $created Map of created resource IDs.
     */
    private function rollback_provision( $created ) {
        // Delete bot (if created).
        if ( $created['bot_id'] ) {
            ( new BoostBot_Bot_Model() )->delete( $created['bot_id'] );
        }

        // Delete widgets.
        if ( ! empty( $created['widget_ids'] ) ) {
            $wm = new BoostBot_Widget_Model();
            foreach ( $created['widget_ids'] as $wid ) {
                $wm->delete_with_fields( $wid );
            }
        }

        // Delete opening screen preset.
        if ( $created['opening_screen_preset_id'] ) {
            ( new BoostBot_Opening_Screen_Preset_Model() )->delete( $created['opening_screen_preset_id'] );
        }

        // Delete personality.
        if ( $created['personality_id'] ) {
            ( new BoostBot_Personality_Model() )->delete( $created['personality_id'] );
        }
    }

    /**
     * Parse JSON fields in bot object.
     *
     * @param object $bot The bot object.
     * @return object The bot with parsed JSON.
     */
    private function parse_json_fields( $bot ) {
        $json_fields = array(
            'personality_traits',
            'display_conditions',
            'knowledge_files',
            'attachment_files',
            'enabled_coupons',
            'enabled_widgets',
            'appearance_overrides',
            'personality_overrides',
            'opening_screen_overrides',
            'brand_card_override',
        );

        foreach ( $json_fields as $field ) {
            if ( isset( $bot->$field ) && is_string( $bot->$field ) && ! empty( $bot->$field ) ) {
                $decoded = json_decode( $bot->$field, true );
                if ( json_last_error() === JSON_ERROR_NONE ) {
                    $bot->$field = $decoded;
                }
            }
        }

        return $bot;
    }
}

