<?php
/**
 * Chat Feedback model.
 *
 * @package KimaAI\Models
 */

declare(strict_types=1);

namespace KimaAI\Models;

defined('ABSPATH') || exit;

class ChatFeedbackModel {
  use HasSchema;

  public const TABLE = 'kimaai_chat_feedback';
  public const PRIMARY_KEY = 'id';

  // Respect current code style: public const COLUMNS
  public const COLUMNS = [
    'id' => 'BIGINT(20) NOT NULL AUTO_INCREMENT',
    'user_id' => 'BIGINT(20) NOT NULL',
    'ip' => 'VARCHAR(64) NOT NULL',
    'session_id' => 'VARCHAR(64) NOT NULL',
    'message_id' => 'VARCHAR(128) NOT NULL',
    'rating' => 'TINYINT NOT NULL', // 1 or -1
    'comment' => 'TEXT NULL',
    'created_at' => 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP',
  ];

  public const INDEXES = [
    'KEY session_idx (session_id)',
    'KEY message_idx (message_id)',
    'UNIQUE KEY uniq_session_message (session_id, message_id)',
  ];

  /**
   * The fully-qualified table name.
   *
   * @var string
   */
  private string $table_name;

  /**
   * ChatFeedbackModel constructor.
   */
  public function __construct() {
    global $wpdb;
    $this->table_name = esc_sql($wpdb->prefix . self::TABLE);
  }

  /**
   * Inserts a new feedback record and returns the new row ID.
   *
   * @param array $data Feedback data.
   * @return int The inserted row ID, or 0 on failure.
   */
  public function insert(array $data): int {
    global $wpdb;

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
    $result = $wpdb->insert(
      $this->table_name,
      [
        'user_id' => (int) ($data['user_id'] ?? 0),
        'ip' => (string) ($data['ip'] ?? ''),
        'session_id' => (string) ($data['session_id'] ?? ''),
        'message_id' => (string) ($data['message_id'] ?? ''),
        'rating' => (int) ($data['rating'] ?? 0),
        'comment' => $data['comment'] ?? null,
        'created_at' => current_time('mysql'),
      ],
      ['%d', '%s', '%s', '%s', '%d', '%s', '%s']
    );

    return $result ? (int) $wpdb->insert_id : 0;
  }

  /**
   * Returns message IDs from the given list that already have a feedback record for the session.
   *
   * @param string $session_id The session ID.
   * @param array $message_ids Message IDs to check.
   * @return string[]
   */
  public function getExistingMessageIds(string $session_id, array $message_ids): array {
    global $wpdb;

    $session_id = sanitize_text_field($session_id);
    $message_ids = array_values(array_filter(array_map('sanitize_text_field', $message_ids)));

    if ($session_id === '' || empty($message_ids)) {
      return [];
    }

    $placeholders = implode(',', array_fill(0, count($message_ids), '%s'));

    // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
    $sql = $wpdb->prepare(
      // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
      "SELECT message_id FROM {$this->table_name} WHERE session_id = %s AND message_id IN ($placeholders)",
      array_merge([$session_id], $message_ids)
    );

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared
    $rows = $wpdb->get_col($sql);

    return array_values(array_unique(array_map('strval', $rows)));
  }

  /**
   * Returns true if a feedback record already exists for the session and message.
   *
   * @param string $session_id The session ID.
   * @param string $message_id The message ID.
   * @return bool
   */
  public function exists(string $session_id, string $message_id): bool {
    global $wpdb;

    $session_id = sanitize_text_field($session_id);
    $message_id = sanitize_text_field($message_id);

    if ($session_id === '' || $message_id === '') {
      return false;
    }

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    $val = $wpdb->get_var(
      $wpdb->prepare(
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        "SELECT 1 FROM {$this->table_name} WHERE session_id = %s AND message_id = %s LIMIT 1",
        $session_id,
        $message_id
      )
    );

    return (bool) $val;
  }

  /**
   * Deletes feedback rows by primary key IDs and returns the number of deleted rows.
   *
   * @param array $ids Row IDs to delete.
   * @return int
   */
  public function deleteByIds(array $ids): int {
    global $wpdb;

    $ids = array_values(array_filter(array_map('absint', $ids)));
    if ($ids === []) {
      return 0;
    }

    $placeholders = implode(', ', array_fill(0, count($ids), '%d'));

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    $result = $wpdb->query(
      $wpdb->prepare(
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
        "DELETE FROM {$this->table_name} WHERE id IN ($placeholders)",
        ...$ids
      )
    );

    return is_numeric($result) ? (int) $result : 0;
  }

  /**
   * Returns feedback row IDs matching the given session IDs.
   *
   * @param array $session_ids Session IDs to look up.
   * @return int[]
   */
  public function getIdsBySessionIds(array $session_ids): array {
    global $wpdb;

    $session_ids = array_values(array_filter(array_map('sanitize_text_field', $session_ids)));
    if ($session_ids === []) {
      return [];
    }

    $placeholders = implode(', ', array_fill(0, count($session_ids), '%s'));

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    $rows = $wpdb->get_col(
      $wpdb->prepare(
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
        "SELECT id FROM {$this->table_name} WHERE session_id IN ($placeholders)",
        ...$session_ids
      )
    );

    return array_values(array_unique(array_map('intval', $rows)));
  }

  /**
   * Deletes feedback rows for the given session IDs and returns the number of deleted rows.
   *
   * @param array $session_ids Session IDs whose rows should be deleted.
   * @return int
   */
  public function deleteBySessionIds(array $session_ids): int {
    global $wpdb;

    $session_ids = array_values(array_filter(array_map('sanitize_text_field', $session_ids)));
    if ($session_ids === []) {
      return 0;
    }

    $placeholders = implode(', ', array_fill(0, count($session_ids), '%s'));

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
    $result = $wpdb->query(
      $wpdb->prepare(
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
        "DELETE FROM {$this->table_name} WHERE session_id IN ($placeholders)",
        ...$session_ids
      )
    );

    return is_numeric($result) ? (int) $result : 0;
  }
}
