<?php
/**
 * Personality Model for database operations.
 *
 * @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_Personality_Model
 *
 * Handles CRUD operations for bot personalities.
 *
 * @since 1.0.0
 */
class BoostBot_Personality_Model extends BoostBot_Base_Model {

    use BoostBot_Localizable;

    /**
     * Constructor.
     */
    public function __construct() {
        parent::__construct();
        $this->table_name = self::sanitize_identifier( $this->wpdb->prefix . 'boostbot_personalities' );
        $this->fillable   = array(
            'name',
            'slug',
            'description',
            'system_prompt',
            'traits',
            'tone',
            'proactive_engagement',
            'proactive_delay_seconds',
            'custom_instructions',
            'is_default',
            'is_system',
            'language',
        );
        $this->column_types = array(
            'id'                      => 'bigint',
            'proactive_engagement'    => 'tinyint',
            'proactive_delay_seconds' => 'int',
            'is_default'              => 'tinyint',
            'is_system'               => 'tinyint',
        );
    }

    /**
     * Get the default personality.
     *
     * @return object|null The default personality or null.
     */
    public function get_default() {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        return $this->wpdb->get_row(
            "SELECT * FROM {$this->table_name} WHERE is_default = 1 LIMIT 1" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        );
    }

    /**
     * Set a personality as default.
     *
     * @param int $id The personality ID.
     * @return bool True on success.
     */
    public function set_default( $id ) {
        $personality = $this->get( $id );
        if ( ! $personality ) {
            return false;
        }

        $language = $personality->language ?? '';

        // Remove current default FOR THIS LANGUAGE ONLY
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $this->wpdb->query(
            $this->wpdb->prepare(
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                "UPDATE {$this->table_name} SET is_default = 0 WHERE is_default = 1 AND language = %s",
                $language
            )
        );

        // Set new default
        return $this->update( $id, array( 'is_default' => 1 ) );
    }

    /**
     * Get the full prompt for a personality including traits.
     *
     * @param int $personality_id The personality ID.
     * @return string The complete prompt.
     */
    public function get_prompt( $personality_id ) {
        $personality = $this->get( $personality_id );

        if ( ! $personality ) {
            $personality = $this->get_default();
        }

        if ( ! $personality ) {
            return '';
        }

        $prompt = $personality->system_prompt;

        // Add traits to prompt
        if ( ! empty( $personality->traits ) ) {
            $traits = json_decode( $personality->traits, true );
            if ( is_array( $traits ) && ! empty( $traits ) ) {
                $trait_model = new BoostBot_Personality_Trait_Model();
                foreach ( $traits as $trait_slug ) {
                    $trait = $trait_model->get_by_slug( $trait_slug );
                    if ( $trait && ! empty( $trait->prompt_addition ) ) {
                        $prompt .= "\n" . $trait->prompt_addition;
                    }
                }
            }
        }

        return $prompt;
    }

    /**
     * Get all personalities for dropdown selection.
     *
     * @return array Array of personalities with id, name, is_default.
     */
    public function get_for_dropdown() {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        return $this->wpdb->get_results(
            "SELECT id, name, is_default FROM {$this->table_name} ORDER BY is_default DESC, name ASC" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        );
    }

    /**
     * Get personality by slug.
     *
     * @param string $slug The personality slug.
     * @return object|null The personality or null.
     */
    public function get_by_slug( $slug ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        return $this->wpdb->get_row(
            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
            $this->wpdb->prepare(
                "SELECT * FROM {$this->table_name} WHERE slug = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                $slug
            )
        );
    }

    /**
     * Validate personality data.
     *
     * @param array    $data The data to validate.
     * @param int|null $id   Optional. The record ID for updates.
     * @return true|WP_Error True if valid, WP_Error otherwise.
     */
    public function validate( $data, $id = null ) {
        $errors = new WP_Error();

        // Name is required
        if ( empty( $data['name'] ) ) {
            $errors->add( 'name_required', __( 'Personality name is required.', 'boostbot' ) );
        }

        // System prompt is required
        if ( empty( $data['system_prompt'] ) ) {
            $errors->add( 'system_prompt_required', __( 'System prompt is required.', 'boostbot' ) );
        }

        // Check slug uniqueness (within same language)
        if ( ! empty( $data['slug'] ) ) {
            $lang     = $data['language'] ?? '';
            $existing = $this->get_by_slug( $data['slug'] );
            if ( $existing && ( ! $id || (int) $existing->id !== (int) $id ) ) {
                if ( '' === $lang || ( isset( $existing->language ) && $existing->language === $lang ) ) {
                    $errors->add( 'slug_exists', __( 'A personality with this slug already exists.', 'boostbot' ) );
                }
            }
        }

        if ( $errors->has_errors() ) {
            return $errors;
        }

        return true;
    }

    /**
     * Sanitize personality data.
     *
     * @param array $data The data to sanitize.
     * @return array The sanitized data.
     */
    public function sanitize( $data ) {
        $sanitized = array();

        if ( isset( $data['name'] ) ) {
            $sanitized['name'] = sanitize_text_field( $data['name'] );
        }

        if ( isset( $data['slug'] ) ) {
            $sanitized['slug'] = sanitize_title( $data['slug'] );
        } elseif ( isset( $data['name'] ) && empty( $data['slug'] ) ) {
            $sanitized['slug'] = sanitize_title( $data['name'] );
        }

        if ( isset( $data['description'] ) ) {
            $sanitized['description'] = sanitize_textarea_field( $data['description'] );
        }

        if ( isset( $data['system_prompt'] ) ) {
            $sanitized['system_prompt'] = sanitize_textarea_field( $data['system_prompt'] );
        }

        if ( isset( $data['traits'] ) ) {
            $sanitized['traits'] = is_string( $data['traits'] )
                ? $data['traits']
                : wp_json_encode( $data['traits'] );
        }

        if ( isset( $data['tone'] ) ) {
            // Tone can be any string (custom tones are now supported)
            $sanitized['tone'] = sanitize_text_field( $data['tone'] );
        }

        if ( isset( $data['proactive_engagement'] ) ) {
            $sanitized['proactive_engagement'] = absint( $data['proactive_engagement'] ) ? 1 : 0;
        }

        if ( isset( $data['proactive_delay_seconds'] ) ) {
            // Clamp between 1 and 60 seconds, default 10
            $delay = absint( $data['proactive_delay_seconds'] );
            $sanitized['proactive_delay_seconds'] = max( 1, min( 60, $delay ) );
        }

        if ( isset( $data['custom_instructions'] ) ) {
            $sanitized['custom_instructions'] = sanitize_textarea_field( $data['custom_instructions'] );
        }

        if ( isset( $data['is_default'] ) ) {
            $sanitized['is_default'] = absint( $data['is_default'] ) ? 1 : 0;
        }

        if ( isset( $data['is_system'] ) ) {
            $sanitized['is_system'] = absint( $data['is_system'] ) ? 1 : 0;
        }

        if ( isset( $data['language'] ) ) {
            $sanitized['language'] = sanitize_text_field( $data['language'] );
        }

        return $sanitized;
    }

    /**
     * Create a new personality with default handling.
     *
     * @param array $data The data to insert.
     * @return int|false The new record ID or false on failure.
     */
    public function create( $data ) {
        // If setting as default, unset other defaults for the same language only
        if ( ! empty( $data['is_default'] ) ) {
            $language = $data['language'] ?? '';
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $this->wpdb->query(
                $this->wpdb->prepare(
                    // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
                    "UPDATE {$this->table_name} SET is_default = 0 WHERE is_default = 1 AND language = %s",
                    $language
                )
            );
        }

        return parent::create( $data );
    }
}




