<?php
/**
 * ANCP Core - Judgements table data access.
 *
 * A judgement is the human decision on a candidate: verified or dismissed,
 * with a canned reason and optional (or consumer-required) free text. The
 * judgement log is append-only by design - it is the research trail that
 * explains WHY the dataset looks the way it does, it exports with the
 * archive, and a dismissal must persist so the same suggestion never
 * resurfaces. Nothing here deletes or rewrites history except full dataset
 * teardown. Shipped schema-only at DB 0.12.0; class arrived with its first
 * consumer (OPS Phase 3, core 0.22.0).
 *
 * @package AncestorPress\Core
 * @since   0.22.0
 */

namespace AncestorPress\Core;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Judgements table data access (append + read; no update by design).
 *
 * @since 0.22.0
 */
class Judgements_Table {

	/**
	 * Fully-qualified table name.
	 *
	 * @return string
	 */
	public static function table() {
		return Tables::judgements();
	}

	/**
	 * Append a judgement.
	 *
	 * @param  array $data Field => value pairs (incl. gedcom, candidateID,
	 *                     decision; canned_reason/reason_text optional).
	 * @return int|false Inserted row id, or false.
	 */
	public static function insert( $data ) {
		global $wpdb;
		if ( empty( $data['gedcom'] ) || empty( $data['candidateID'] ) || empty( $data['decision'] ) ) {
			return false;
		}

		$now  = Tables::now();
		$data = array_merge(
			array(
				'created_by_user_id' => get_current_user_id(),
				'created_at'         => $now,
				'updated_at'         => $now,
			),
			$data
		);

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table.
		return $wpdb->insert( self::table(), $data ) ? (int) $wpdb->insert_id : false;
	}

	/**
	 * All judgements on one candidate, oldest first (the decision history).
	 *
	 * @param  string $gedcom      Tree identifier.
	 * @param  string $candidateID Candidate key.
	 * @return array
	 */
	public static function get_by_candidate( $gedcom, $candidateID ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table; identifier bound via %i.
		return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE gedcom = %s AND candidateID = %s ORDER BY id ASC', self::table(), $gedcom, $candidateID ) );
	}

	/**
	 * The full judgement log for a tree, oldest first (archive export order).
	 *
	 * @param  string $gedcom Tree identifier.
	 * @return array
	 */
	public static function get_by_tree( $gedcom ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table; identifier bound via %i.
		return $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE gedcom = %s ORDER BY id ASC', self::table(), $gedcom ) );
	}

	/**
	 * candidateIDs that carry at least one judgement with the given decision -
	 * the engine's never-resurface screen ('dismiss') and re-emission guard.
	 *
	 * @param  string $gedcom   Tree identifier.
	 * @param  string $decision Decision value, e.g. 'dismiss' or 'verify'.
	 * @return array Flat list of candidateID strings.
	 */
	public static function candidate_ids_with_decision( $gedcom, $decision ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table; identifier bound via %i.
		return (array) $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT candidateID FROM %i WHERE gedcom = %s AND decision = %s', self::table(), $gedcom, $decision ) );
	}

	/**
	 * Delete every judgement for a tree (dataset teardown ONLY - the log is
	 * otherwise append-only).
	 *
	 * @param  string $gedcom Tree identifier.
	 * @return int|false
	 */
	public static function delete_by_gedcom( $gedcom ) {
		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- own table.
		return $wpdb->delete( self::table(), array( 'gedcom' => $gedcom ) );
	}
}
