<?php
/**
 * Personalities REST API Endpoint.
 *
 * @package BoostBot
 * @since   1.0.0
 */

// Prevent direct access
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Class BoostBot_Personalities_Endpoint
 *
 * Handles REST API endpoints for bot personalities.
 *
 * @since 1.0.0
 */
class BoostBot_Personalities_Endpoint extends BoostBot_Base_Endpoint {

    /**
     * REST base for this endpoint.
     *
     * @var string
     */
    protected $rest_base = 'personalities';

    /**
     * Register routes for this endpoint.
     *
     * @return void
     */
    public function register() {
        // GET /personalities - List all personalities
        $this->register_route(
            '',
            'GET',
            array( $this, 'get_personalities' ),
            array( $this, 'admin_access' )
        );

        // POST /personalities - Create personality
        $this->register_route(
            '',
            'POST',
            array( $this, 'create_personality' ),
            array( $this, 'admin_access' )
        );

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

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

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

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

        // GET /personalities/traits - List all traits
        $this->register_route(
            '/traits',
            'GET',
            array( $this, 'get_traits' ),
            array( $this, 'admin_access' )
        );

        // POST /personalities/traits - Create trait
        $this->register_route(
            '/traits',
            'POST',
            array( $this, 'create_trait' ),
            array( $this, 'admin_access' )
        );

        // PUT /personalities/traits/{id} - Update trait
        $this->register_route(
            '/traits/(?P<id>\d+)',
            'PUT',
            array( $this, 'update_trait' ),
            array( $this, 'admin_access' )
        );

        // DELETE /personalities/traits/{id} - Delete trait
        $this->register_route(
            '/traits/(?P<id>\d+)',
            'DELETE',
            array( $this, 'delete_trait' ),
            array( $this, 'admin_access' )
        );

        // GET /personalities/dropdown - Get for dropdown
        $this->register_route(
            '/dropdown',
            'GET',
            array( $this, 'get_dropdown' ),
            array( $this, 'admin_access' )
        );

        // GET /personalities/{id}/linked-bots - Get bots using this personality
        $this->register_route(
            '/(?P<id>\d+)/linked-bots',
            'GET',
            array( $this, 'get_linked_bots' ),
            array( $this, 'admin_access' )
        );

        // POST /personalities/{id}/unlink-bot/{bot_id} - Unlink a bot from this personality
        $this->register_route(
            '/(?P<id>\d+)/unlink-bot/(?P<bot_id>\d+)',
            'POST',
            array( $this, 'unlink_bot' ),
            array( $this, 'admin_access' )
        );

        // POST /personalities/{id}/duplicate - Duplicate a personality
        $this->register_route(
            '/(?P<id>\d+)/duplicate',
            'POST',
            array( $this, 'duplicate_personality' ),
            array( $this, 'admin_access' )
        );

        // GET /personalities/by-slug/{slug} - Get personality by slug
        $this->register_route(
            '/by-slug/(?P<slug>[a-zA-Z0-9_-]+)',
            'GET',
            array( $this, 'get_by_slug' ),
            array( $this, 'admin_access' )
        );
    }

    /**
     * Get all personalities.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response
     */
    public function get_personalities( $request ) {
        $model = new BoostBot_Personality_Model();

        $filters = array();
        if ( $request->has_param( 'language' ) ) {
            $filters['language'] = $request->get_param( 'language' );
        }

        $args = array(
            'where'   => $filters,
            'orderby' => 'is_default',
            'order'   => 'DESC',
            'limit'   => 100,
        );

        $personalities = $model->get_all( $args );

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

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

    /**
     * Get single personality.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error
     */
    public function get_personality( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Personality_Model();

        $personality = $model->get( $id );

        if ( ! $personality ) {
            return $this->not_found( 'Personality' );
        }

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

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

    /**
     * Create new personality.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error
     */
    public function create_personality( $request ) {
        $model = new BoostBot_Personality_Model();
        $data  = $request->get_json_params();

        // Generate slug if not provided
        if ( empty( $data['slug'] ) && ! empty( $data['name'] ) ) {
            $data['slug'] = sanitize_title( $data['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 personality', 'boostbot' ), 400 );
        }

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

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

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

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

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

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

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

        // If setting as default, use the dedicated method
        if ( ! empty( $data['is_default'] ) && ! $existing->is_default ) {
            $model->set_default( $id );
        }

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

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

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

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

        // Cannot delete system personalities
        if ( $existing->is_system ) {
            return $this->error( __( 'Cannot delete system personality', 'boostbot' ), 403 );
        }

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

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

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

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

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

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

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

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

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

    /**
     * Get all traits.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response
     */
    public function get_traits( $request ) {
        $model  = new BoostBot_Personality_Trait_Model();
        $traits = $model->get_active();

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

    /**
     * Create new trait.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error
     */
    public function create_trait( $request ) {
        $model = new BoostBot_Personality_Trait_Model();
        $data  = $request->get_json_params();

        // Generate slug if not provided
        if ( empty( $data['slug'] ) && ! empty( $data['name'] ) ) {
            $data['slug'] = sanitize_title( $data['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 trait', 'boostbot' ), 400 );
        }

        $trait = $model->get( $id );

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

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

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

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

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

        $trait = $model->get( $id );

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

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

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

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

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

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

    /**
     * Get personalities for dropdown.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response
     */
    public function get_dropdown( $request ) {
        $model         = new BoostBot_Personality_Model();
        $personalities = $model->get_for_dropdown();

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

    /**
     * Parse JSON fields in personality object.
     *
     * @param object $personality The personality object.
     * @return object The personality with parsed JSON fields.
     */
    private function parse_json_fields( $personality ) {
        if ( isset( $personality->traits ) && is_string( $personality->traits ) ) {
            $personality->traits = json_decode( $personality->traits, true );
            if ( ! is_array( $personality->traits ) ) {
                $personality->traits = array();
            }
        }

        return $personality;
    }

    /**
     * Get bots linked to this personality.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error
     */
    public function get_linked_bots( $request ) {
        $personality_id = absint( $request->get_param( 'id' ) );

        global $wpdb;
        $bots_table = self::sanitize_identifier( $wpdb->prefix . 'boostbot_bots' );

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table identifier sanitized via sanitize_identifier(); values bind via prepare().
        $bots = $wpdb->get_results(
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $wpdb->prepare(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                "SELECT id, bot_name FROM {$bots_table} WHERE personality_id = %d ORDER BY bot_name ASC",
                $personality_id
            )
        );

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

    /**
     * Unlink a bot from this personality.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error
     */
    public function unlink_bot( $request ) {
        $personality_id = absint( $request->get_param( 'id' ) );
        $bot_id         = absint( $request->get_param( 'bot_id' ) );

        // Get the personality to copy values before unlinking
        $personality_model = new BoostBot_Personality_Model();
        $personality       = $personality_model->get( $personality_id );

        if ( ! $personality ) {
            return $this->not_found( 'Personality' );
        }

        // Get the bot
        $bot_model = new BoostBot_Bot_Model();
        $bot       = $bot_model->get( $bot_id );

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

        // Copy personality values to bot and unlink
        $update_data = array(
            'personality_id'          => null,
            'system_prompt'           => $personality->system_prompt,
            'tone'                    => $personality->tone,
            'proactive_engagement'    => $personality->proactive_engagement,
            'proactive_delay_seconds' => $personality->proactive_delay_seconds ?? 10,
            'custom_instructions'     => $personality->custom_instructions ?? '',
        );

        $result = $bot_model->update( $bot_id, $update_data );

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

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot unlinked from personality', array(
                'bot_id'         => $bot_id,
                'personality_id' => $personality_id,
            ) );
        }

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

    /**
     * Duplicate a personality.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error
     */
    public function duplicate_personality( $request ) {
        $id    = absint( $request->get_param( 'id' ) );
        $model = new BoostBot_Personality_Model();

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

        // Build copy data.
        $data = array(
            'name'                    => $original->name . __( ' (העתק)', 'boostbot' ),
            'slug'                    => '', // Will be auto-generated.
            'description'             => $original->description,
            'system_prompt'           => $original->system_prompt,
            'traits'                  => $original->traits, // Already JSON string in DB.
            'tone'                    => $original->tone,
            'proactive_engagement'    => $original->proactive_engagement,
            'proactive_delay_seconds' => $original->proactive_delay_seconds ?? 10,
            'custom_instructions'     => $original->custom_instructions,
            'is_default'              => 0,
            'is_system'               => 0,
        );

        // Generate unique slug.
        $data['slug'] = sanitize_title( $data['name'] );

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

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

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

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

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

    /**
     * Get personality by slug.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error
     */
    public function get_by_slug( $request ) {
        $slug  = sanitize_title( $request->get_param( 'slug' ) );
        $model = new BoostBot_Personality_Model();

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

        if ( ! $personality ) {
            return $this->not_found( 'Personality' );
        }

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

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

    /**
     * Restrict a SQL identifier to [A-Za-z0-9_] characters.
     *
     * DDL cannot use $wpdb->prepare() for identifiers, so any identifier
     * that ends up interpolated into a query first passes through this
     * allow-list.
     *
     * @param string $identifier Raw identifier.
     * @return string Identifier with disallowed characters removed.
     */
    private static function sanitize_identifier( $identifier ) {
        return preg_replace( '/[^A-Za-z0-9_]/', '', (string) $identifier );
    }
}




