<?php
/**
 * Truebe query: the path from a visitor's question to an answer.
 *
 * Thin on purpose. It hands the question to Truebe_Matcher (the scoring engine), and
 * shapes whatever comes back into one response the widget can render:
 *   - a verified answer (the matched seed's thesis) plus its citations, or
 *   - a followup answer when the question matched a seed's followup, or
 *   - a gap when nothing matched, which gets logged for the nightly gap report.
 *
 * No generation happens here. The answer is the seed text, locked to its source. This
 * class only retrieves and packages. Intent routing (who/what/aggregate) is a later layer
 * that sits on top of this without changing the shape below.
 */

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

if ( class_exists( 'Truebe_Query' ) ) {
	return;
}

class Truebe_Query {

	/**
	 * Answer a visitor question.
	 *
	 * @param string $question Raw visitor question.
	 * @return array {
	 *     @type string $status     'answer' | 'gap'
	 *     @type string $label      The canonical question this answer belongs to ('' on gap).
	 *     @type string $answer     The verified answer sentence ('' on gap).
	 *     @type array  $citations  List of { text, source } ([] on gap).
	 *     @type int    $score      Match score (0 on gap).
	 *     @type bool   $is_followup True if the answer came from a seed's followup.
	 *     @type array  $suggestions Nearest verified questions to offer on a gap ([] otherwise).
	 * }
	 */
	public static function answer( $question ) {
		$question = is_string( $question ) ? trim( $question ) : '';
		if ( '' === $question ) {
			return self::gap_response( '' );
		}

		if ( ! class_exists( 'Truebe_Matcher' ) ) {
			return self::gap_response( $question );
		}

		$match = Truebe_Matcher::match( $question );
		if ( null === $match || empty( $match['seed'] ) ) {
			return self::gap_response( $question );
		}

		$seed = $match['seed'];

		// Optional safety blocklist by risk category. Empty by default (refuses nothing).
		$blocked = (array) apply_filters( 'truebe_blocked_categories', array() );
		if ( ! empty( $seed['risk_category'] ) && in_array( $seed['risk_category'], $blocked, true ) ) {
			return self::gap_response( $question );
		}

		// Followup match: answer from the followup, cite the followup's own sources.
		if ( ! empty( $match['is_followup'] ) && ! empty( $match['followup'] ) ) {
			$fu = $match['followup'];
			return array(
				'status'      => 'answer',
				'label'       => isset( $fu['question'] ) ? (string) $fu['question'] : '',
				'answer'      => isset( $fu['thesis'] ) ? (string) $fu['thesis'] : '',
				'citations'   => self::clean_citations( isset( $fu['citations'] ) ? $fu['citations'] : array() ),
				'score'       => isset( $match['score'] ) ? (int) $match['score'] : 0,
				'is_followup' => true,
				'suggestions' => array(),
			);
		}

		// Seed match: the verified answer.
		return array(
			'status'      => 'answer',
			'label'       => isset( $seed['label'] ) ? (string) $seed['label'] : '',
			'answer'      => isset( $seed['thesis'] ) ? (string) $seed['thesis'] : '',
			'citations'   => self::clean_citations( isset( $seed['citations'] ) ? $seed['citations'] : array() ),
			'score'       => isset( $match['score'] ) ? (int) $match['score'] : 0,
			'is_followup' => false,
			'suggestions' => array(),
		);
	}

	/**
	 * Build a gap response and log the miss for the nightly gap report.
	 *
	 * @param string $question
	 * @return array
	 */
	private static function gap_response( $question ) {
		if ( '' !== $question && class_exists( 'Truebe_Gaps' ) && method_exists( 'Truebe_Gaps', 'log' ) ) {
			Truebe_Gaps::log( $question );
		}
		return array(
			'status'      => 'gap',
			'label'       => '',
			'answer'      => '',
			'citations'   => array(),
			'score'       => 0,
			'is_followup' => false,
			'suggestions' => array(),
		);
	}

	/**
	 * Normalize citations to a list of { text, source } with strings only.
	 *
	 * @param mixed $citations
	 * @return array
	 */
	private static function clean_citations( $citations ) {
		if ( ! is_array( $citations ) ) {
			return array();
		}
		$out = array();
		foreach ( $citations as $c ) {
			if ( ! is_array( $c ) ) {
				continue;
			}
			$text   = isset( $c['text'] ) ? (string) $c['text'] : '';
			$source = isset( $c['source'] ) ? (string) $c['source'] : '';
			if ( '' === $text && '' === $source ) {
				continue;
			}
			$out[] = array( 'text' => $text, 'source' => $source );
		}
		return $out;
	}
}