<?php
/**
 * RSS Repository Class
 *
 * @package Wisewand
 * @subpackage Wisewand/includes/Repositories
 *
 * phpcs:disable WordPress.DB.PreparedSQL.NotPrepared -- False positive from Plugin Checker
 * All queries use $wpdb->prepare() with proper placeholders. The checker incorrectly flags
 * the prepare() method calls themselves. Known PHPCS limitation.
 *
 * phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- Repository pattern requirement
 * phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table, no object cache
 */

namespace Wisewand\Repositories;

/**
 * Class RSS_Repository
 *
 * Handles database interactions for RSS Feeds and Articles.
 */
class RSS_Repository extends Base_Repository {

	/**
	 * Articles table name.
	 *
	 * @var string
	 */
	protected $articles_table;

	/**
	 * Initialize the repository.
	 */
	public function __construct() {
		parent::__construct( 'wisewand_rss_feeds' );
		$this->articles_table = $this->wpdb->prefix . 'wisewand_rss_articles';
	}

	/**
	 * Get all active feeds.
	 *
	 * @return array
	 */
	public function get_active_feeds() {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
		return $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE sync_status = 'enabled' AND enabled_for_site = 1",
				$this->table_name
			),
			ARRAY_A
		);
	}

	/**
	 * Get feed by ID.
	 *
	 * @param int $id Feed ID.
	 * @return object|null
	 */
	public function get_feed( $id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
		return $this->wpdb->get_row(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE id = %d",
				$this->table_name,
				$id
			),
			ARRAY_A
		);
	}

	/**
	 * Get feed by Wisewand Feed ID (UUID).
	 *
	 * @param string $uuid Wisewand Feed ID.
	 * @return object|null
	 */
	public function get_feed_by_uuid( $uuid ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
		return $this->wpdb->get_row(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE wisewand_feed_id = %s",
				$this->table_name,
				$uuid
			),
			ARRAY_A
		);
	}

	/**
	 * Create or update a feed.
	 *
	 * @param array $data Feed data.
	 * @return int|false Insert ID or affected rows.
	 */
	public function save_feed( $data ) {
		// If ID exists, update, else insert.
		if ( isset( $data['id'] ) ) {
			$id = $data['id'];
			unset( $data['id'] );
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
			return $this->wpdb->update(
				$this->table_name,
				$data,
				array( 'id' => $id ),
				null,
				array( '%d' )
			);
		} else {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
			$result = $this->wpdb->insert(
				$this->table_name,
				$data
			);
			return $result ? $this->wpdb->insert_id : false;
		}
	}

	/**
	 * Get article by Wisewand Article ID.
	 *
	 * @param string $article_id Wisewand Article ID.
	 * @return object|null
	 */
	public function get_article_by_uuid( $article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
		return $this->wpdb->get_row(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE wisewand_article_id = %s",
				$this->articles_table,
				$article_id
			),
			ARRAY_A
		);
	}

	/**
	 * Save (Insert) an article.
	 *
	 * @param array $data Article data.
	 * @return int|false Insert ID.
	 */
	public function create_article( $data ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
		$result = $this->wpdb->insert(
			$this->articles_table,
			$data
		);
		return $result ? $this->wpdb->insert_id : false;
	}

	/**
	 * Update an article.
	 *
	 * @param int   $id   Article ID.
	 * @param array $data Data to update.
	 * @return int|false Affected rows.
	 */
	public function update_article( $id, $data ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table query required.
		return $this->wpdb->update(
			$this->articles_table,
			$data,
			array( 'id' => $id ),
			null,
			array( '%d' )
		);
	}
	/**
	 * Get all feeds with optional filters.
	 *
	 * @param array $filters Optional filters.
	 * @return array Array of feeds.
	 */
	public function get_all_feeds( $filters = array() ) {
		$where  = array( '1=1' );
		$values = array();

		if ( isset( $filters['auto_import_enabled'] ) ) {
			$where[]  = 'auto_import_enabled = %d';
			$values[] = $filters['auto_import_enabled'] ? 1 : 0;
		}

		if ( ! empty( $filters['wisewand_project_id'] ) ) {
			$where[]  = 'wisewand_project_id = %s';
			$values[] = $filters['wisewand_project_id'];
		}

		if ( ! empty( $filters['search'] ) ) {
			$where[]  = 'feed_name LIKE %s';
			$values[] = '%' . $this->wpdb->esc_like( $filters['search'] ) . '%';
		}

		$limit = isset( $filters['limit'] ) ? absint( $filters['limit'] ) : 1000;

		$where_clause = implode( ' AND ', $where );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE {$where_clause} ORDER BY created_at DESC LIMIT %d",
				array_merge( array( $this->table_name ), $values, array( $limit ) )
			),
			ARRAY_A
		);
	}

	/**
	 * Update feed settings.
	 *
	 * @param int   $feed_id  Feed ID.
	 * @param array $settings Settings to update.
	 * @return int|false Affected rows.
	 */
	public function update_feed_settings( $feed_id, $settings ) {
		$settings['updated_at'] = current_time( 'mysql' );
		
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->table_name,
			$settings,
			array( 'id' => $feed_id )
		);
	}

	/**
	 * Update feed statistics.
	 *
	 * @param int   $feed_id Feed ID.
	 * @param array $stats   Statistics to update.
	 * @return int|false Affected rows.
	 */
	public function update_feed_statistics( $feed_id, $stats ) {
		$stats['updated_at'] = current_time( 'mysql' );
		
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->table_name,
			$stats,
			array( 'id' => $feed_id )
		);
	}

	/**
	 * Get feeds due for synchronization.
	 *
	 * @param int $limit Maximum number of feeds.
	 * @return array Feeds due for sync.
	 */
	public function get_feeds_due_for_sync( $limit = 100 ) {
		$now = current_time( 'mysql' );
		
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT * FROM %i
				 WHERE auto_import_enabled = 1
				 AND (next_sync_at IS NULL OR next_sync_at <= %s)
				 ORDER BY next_sync_at ASC
				 LIMIT %d",
				$this->table_name,
				$now,
				$limit
			),
			ARRAY_A
		);
	}

	/**
	 * Update next sync time.
	 *
	 * @param int    $feed_id   Feed ID.
	 * @param string $next_sync Next sync timestamp (mysql format).
	 * @return int|false Affected rows.
	 */
	public function update_next_sync_time( $feed_id, $next_sync ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->table_name,
			array(
				'next_sync_at' => $next_sync,
				'updated_at'   => current_time( 'mysql' ),
			),
			array( 'id' => $feed_id ),
			array( '%s', '%s' ),
			array( '%d' )
		);
	}

	/**
	 * Delete a feed.
	 *
	 * @param int $feed_id Feed ID.
	 * @return int|false Number of rows deleted.
	 */
	public function delete_feed( $feed_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->delete(
			$this->table_name,
			array( 'id' => $feed_id ),
			array( '%d' )
		);
	}

	/**
	 * Get total feeds count.
	 *
	 * @param array $filters Optional filters.
	 * @return int Count.
	 */
	public function get_feeds_count( $filters = array() ) {
		$where  = array( '1=1' );
		$values = array();

		if ( isset( $filters['auto_import_enabled'] ) ) {
			$where[]  = 'auto_import_enabled = %d';
			$values[] = $filters['auto_import_enabled'] ? 1 : 0;
		}

		$where_clause = implode( ' AND ', $where );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return (int) $this->wpdb->get_var(
			$this->wpdb->prepare(
				"SELECT COUNT(*) FROM %i WHERE {$where_clause}",
				array_merge( array( $this->table_name ), $values )
			)
		);
	}

	/**
	 * Check if article exists and return status.
	 *
	 * @param string $wisewand_article_id Wisewand Article ID.
	 * @return array|false Article data or false.
	 */
	public function check_article_exists( $wisewand_article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_row(
			$this->wpdb->prepare(
				"SELECT import_status, wordpress_post_id, article_status
				 FROM %i
				 WHERE wisewand_article_id = %s
				 LIMIT 1",
				$this->articles_table,
				$wisewand_article_id
			),
			ARRAY_A
		);
	}

	/**
	 * Get cached output ID.
	 *
	 * @param string $wisewand_article_id Wisewand Article ID.
	 * @return string|null Output ID or null.
	 */
	public function get_cached_output_id( $wisewand_article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_var(
			$this->wpdb->prepare(
				"SELECT last_output_id FROM %i WHERE wisewand_article_id = %s",
				$this->articles_table,
				$wisewand_article_id
			)
		);
	}

	/**
	 * Get article by WordPress Post ID.
	 *
	 * @param int $post_id WordPress Post ID.
	 * @return array|null Article data or null.
	 */
	public function get_article_by_wordpress_post_id( $post_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_row(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE wordpress_post_id = %d",
				$this->articles_table,
				$post_id
			),
			ARRAY_A
		);
	}

	/**
	 * Get article by local ID.
	 *
	 * @param int $article_id Article ID.
	 * @return array|null Article data or null.
	 */
	public function get_article( $article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_row(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE id = %d",
				$this->articles_table,
				$article_id
			),
			ARRAY_A
		);
	}

	/**
	 * Get pending articles for a feed.
	 *
	 * @param int $feed_id Feed ID.
	 * @param int $limit   Limit.
	 * @return array Articles.
	 */
	public function get_pending_articles_for_feed( $feed_id, $limit = 10 ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT * FROM %i
				 WHERE feed_id = %d
				 AND import_status NOT IN ('imported', 'deleted', 'skipped')
				 AND article_status IN ('success', 'ready', 'creating', 'error', 'unknown')
				 ORDER BY article_created_at ASC
				 LIMIT %d",
				$this->articles_table,
				$feed_id,
				$limit
			),
			ARRAY_A
		);
	}

	/**
	 * Get articles for a feed with filters.
	 *
	 * @param int   $feed_id Feed ID.
	 * @param array $filters Filters.
	 * @return array Articles.
	 */
	public function get_feed_articles( $feed_id, $filters = array() ) {
		$where  = array( 'feed_id = %d' );
		$values = array( $feed_id );

		if ( ! empty( $filters['import_status'] ) ) {
			$where[]  = 'import_status = %s';
			$values[] = $filters['import_status'];
		}

		if ( ! empty( $filters['article_status'] ) ) {
			$where[]  = 'article_status = %s';
			$values[] = $filters['article_status'];
		}

		$limit = isset( $filters['limit'] ) ? absint( $filters['limit'] ) : 1000;

		$where_clause = implode( ' AND ', $where );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT * FROM %i WHERE {$where_clause} ORDER BY article_created_at DESC LIMIT %d",
				array_merge( array( $this->articles_table ), $values, array( $limit ) )
			),
			ARRAY_A
		);
	}

	/**
	 * Mark article as importing.
	 *
	 * @param int $article_id Article ID.
	 * @return int|false Affected rows.
	 */
	public function mark_as_importing( $article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->articles_table,
			array(
				'import_status'          => 'importing',
				'last_import_attempt_at' => current_time( 'mysql' ),
				'updated_at'             => current_time( 'mysql' ),
			),
			array( 'id' => $article_id ),
			array( '%s', '%s', '%s' ),
			array( '%d' )
		);
	}

	/**
	 * Mark article as imported.
	 *
	 * @param int    $article_id            Article ID.
	 * @param int    $wordpress_post_id     Post ID.
	 * @param string $wordpress_post_status Post status.
	 * @return int|false Affected rows.
	 */
	public function mark_as_imported( $article_id, $wordpress_post_id, $wordpress_post_status = 'draft' ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->articles_table,
			array(
				'import_status'         => 'imported',
				'wordpress_post_id'     => $wordpress_post_id,
				'wordpress_post_status' => $wordpress_post_status,
				'imported_at'           => current_time( 'mysql' ),
				'import_error'          => null,
				'updated_at'            => current_time( 'mysql' ),
			),
			array( 'id' => $article_id ),
			array( '%s', '%d', '%s', '%s', '%s', '%s' ),
			array( '%d' )
		);
	}

	/**
	 * Mark article as failed.
	 *
	 * @param int    $article_id    Article ID.
	 * @param string $error_message Error message.
	 * @return int|false Affected rows.
	 */
	public function mark_as_failed( $article_id, $error_message ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$result = $this->wpdb->update(
			$this->articles_table,
			array(
				'import_status'          => 'failed',
				'import_error'           => $error_message,
				'last_import_attempt_at' => current_time( 'mysql' ),
				'updated_at'             => current_time( 'mysql' ),
			),
			array( 'id' => $article_id ),
			array( '%s', '%s', '%s', '%s' ),
			array( '%d' )
		);

		if ( $result !== false ) {
			// Increment import_attempts manually
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$this->wpdb->query(
				$this->wpdb->prepare(
					"UPDATE %i SET import_attempts = import_attempts + 1 WHERE id = %d",
					$this->articles_table,
					$article_id
				)
			);
		}

		return $result;
	}

	/**
	 * Mark article as deleted.
	 *
	 * @param int    $article_id Article ID.
	 * @param string $reason     Reason.
	 * @return int|false Affected rows.
	 */
	public function mark_as_deleted( $article_id, $reason = '' ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->articles_table,
			array(
				'import_status'          => 'deleted',
				'import_error'           => $reason,
				'last_import_attempt_at' => current_time( 'mysql' ),
				'updated_at'             => current_time( 'mysql' ),
			),
			array( 'id' => $article_id ),
			array( '%s', '%s', '%s', '%s' ),
			array( '%d' )
		);
	}

	/**
	 * Mark article as skipped.
	 *
	 * @param int $article_id Article ID.
	 * @return int|false Affected rows.
	 */
	public function mark_as_skipped( $article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->articles_table,
			array(
				'import_status' => 'skipped',
				'updated_at'    => current_time( 'mysql' ),
			),
			array( 'id' => $article_id ),
			array( '%s', '%s' ),
			array( '%d' )
		);
	}

	/**
	 * Get feed statistics.
	 *
	 * @param int $feed_id Feed ID.
	 * @return array Statistics.
	 */
	public function get_feed_statistics( $feed_id ) {
		$stats = array(
			'total'             => 0,
			'pending'           => 0,
			'importing'         => 0,
			'imported'          => 0,
			'failed'            => 0,
			'skipped'           => 0,
			'by_article_status' => array(
				'success'  => 0,
				'error'    => 0,
				'creating' => 0,
				'ready'    => 0,
			),
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$import_counts = $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT import_status, COUNT(*) as count
				 FROM %i
				 WHERE feed_id = %d
				 GROUP BY import_status",
				$this->articles_table,
				$feed_id
			),
			ARRAY_A
		);

		foreach ( $import_counts as $row ) {
			$status           = $row['import_status'];
			$count            = (int) $row['count'];
			$stats[ $status ] = $count;
			$stats['total']  += $count;
		}

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$article_counts = $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT article_status, COUNT(*) as count
				 FROM %i
				 WHERE feed_id = %d
				 GROUP BY article_status",
				$this->articles_table,
				$feed_id
			),
			ARRAY_A
		);

		foreach ( $article_counts as $row ) {
			$status = $row['article_status'];
			$count  = (int) $row['count'];
			if ( isset( $stats['by_article_status'][ $status ] ) ) {
				$stats['by_article_status'][ $status ] = $count;
			}
		}

		return $stats;
	}

	/**
	 * Delete article.
	 *
	 * @param int $article_id Article ID.
	 * @return int|false Number of rows deleted.
	 */
	public function delete_article( $article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->delete(
			$this->articles_table,
			array( 'id' => $article_id ),
			array( '%d' )
		);
	}

	/**
	 * Cleanup old articles.
	 *
	 * @return array Cleanup stats.
	 */
	public function cleanup_old_articles() {
		$stats = array(
			'failed_deleted'      => 0,
			'deleted_removed'     => 0,
			'ungenerated_removed' => 0,
		);

		$failed_cutoff = gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$failed_deleted = $this->wpdb->query(
			$this->wpdb->prepare(
				"DELETE FROM %i
				 WHERE import_status = 'failed'
				 AND last_import_attempt_at < %s",
				$this->articles_table,
				$failed_cutoff
			)
		);
		$stats['failed_deleted'] = (int) $failed_deleted;

		$deleted_cutoff = gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$deleted_removed = $this->wpdb->query(
			$this->wpdb->prepare(
				"DELETE FROM %i
				 WHERE import_status = 'deleted'
				 AND last_import_attempt_at < %s",
				$this->articles_table,
				$deleted_cutoff
			)
		);
		$stats['deleted_removed'] = (int) $deleted_removed;

		$ungenerated_cutoff = gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$ungenerated_removed = $this->wpdb->query(
			$this->wpdb->prepare(
				"DELETE FROM %i
				 WHERE import_status = 'failed'
				 AND import_error LIKE %s
				 AND discovered_at < %s",
				$this->articles_table,
				'%no generated output yet%',
				$ungenerated_cutoff
			)
		);
		$stats['ungenerated_removed'] = (int) $ungenerated_removed;

		return $stats;
	}

	/**
	 * Purge deleted articles.
	 *
	 * @param int|null $feed_id Feed ID.
	 * @return int Number of rows deleted.
	 */
	public function purge_deleted_articles( $feed_id = null ) {
		if ( $feed_id ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			return $this->wpdb->query(
				$this->wpdb->prepare(
					"DELETE FROM %i WHERE import_status = 'deleted' AND feed_id = %d",
					$this->articles_table,
					$feed_id
				)
			);
		} else {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			return $this->wpdb->query(
				$this->wpdb->prepare(
					"DELETE FROM %i WHERE import_status = 'deleted'",
					$this->articles_table
				)
			);
		}
	}

	/**
	 * Reset article to pending.
	 *
	 * @param int $article_id Article ID.
	 * @return int|false Affected rows.
	 */
	public function reset_to_pending( $article_id ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->update(
			$this->articles_table,
			array(
				'import_status' => 'pending',
				'import_error'  => null,
				'updated_at'    => current_time( 'mysql' ),
			),
			array( 'id' => $article_id ),
			array( '%s', '%s', '%s' ),
			array( '%d' )
		);
	}

	/**
	 * Get recent articles for feed.
	 *
	 * @param int $feed_id Feed ID.
	 * @param int $limit   Limit.
	 * @return array Articles.
	 */
	public function get_recent_articles( $feed_id, $limit = 5 ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		return $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT * FROM %i
				 WHERE feed_id = %d
				 ORDER BY article_created_at DESC
				 LIMIT %d",
				$this->articles_table,
				$feed_id,
				$limit
			),
			ARRAY_A
		);
	}
}
