<?php

namespace Limb_Chatbot\Includes\Vector_Dbs\Pinecone\Endpoints\Query;

use Limb_Chatbot\Includes\Data_Objects\Vector;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Vector_Dbs\Pinecone\Response_Handler as Base_Response_Handler;

/**
 * Class Response_Handler
 *
 * Handles the response from Pinecone query endpoint,
 * parses vector matches and enriches them with scores.
 *
 * @package Limb_Chatbot\Includes\Vector_Dbs\Pinecone\Endpoints\Query
 *
 * @since 1.0.0
 */
class Response_Handler extends Base_Response_Handler {
	/**
	 * Special role allowing universal access.
	 */
	private const ROLE_EVERYONE = 'everyone';

	/**
	 * @var bool|null Flag to cast response body as array
	 *
	 * @since 1.0.0
	 */
	protected ?bool $body_cast_array = true;

	/**
	 * @var Collection|null Collection of matched vectors with scores
	 *
	 * @since 1.0.0
	 */
	protected ?Collection $vectors = null;

	/**
	 * Parse the response body and populate vectors.
	 *
	 * @return void
	 *
	 * @since 1.0.0
	 */
	public function parse(): void {
		parent::parse();
		$this->define_vectors();
	}

	/**
	 * Define vectors by matching response IDs with stored vectors,
	 * and attach score values from the matches.
	 *
	 * @return void
	 *
	 * @since 1.0.0
	 */
	public function define_vectors(): void {
		if ( ! empty( $this->get_body() ) && is_array( $this->get_body() ) && ! empty( $this->get_body()['matches'] ) ) {
			$matches = $this->get_body()['matches'];
			$vectors = Vector::where( [ 'uuid' => array_column( $matches, 'id' ) ] );
			if ( ! $vectors->is_empty() ) {
				$vectors->filter( function ( $vector ) use ( $matches ) {
					foreach ( $matches as $match ) {
						if ( $vector->get_uuid() === $match['id'] ) {
							$vector->score = $match['score'];
						}
					}

					return $this->vector_is_accessible_for_current_roles( $vector );
				} );
			}
			$this->vectors = $vectors;
		}
	}

	/**
	 * Enforce role-based access on returned vectors.
	 *
	 * @param  Vector  $vector
	 *
	 * @return bool
	 */
	private function vector_is_accessible_for_current_roles( Vector $vector ): bool {
		$allowed_roles = $vector->get_access_roles();
		if ( ! is_array( $allowed_roles ) || empty( $allowed_roles ) ) {
			return true;
		}

		$allowed_roles = array_values( array_filter( array_map( static function ( $role ) {
			return is_string( $role ) ? sanitize_key( $role ) : '';
		}, $allowed_roles ) ) );
		if ( empty( $allowed_roles ) ) {
			return true;
		}
		if ( in_array( self::ROLE_EVERYONE, $allowed_roles, true ) ) {
			return true;
		}

		$current_roles = $this->endpoint->get_utility()->get_global_utility()->get_query_access_roles();
		if ( empty( $current_roles ) ) {
			return false;
		}

		return ! empty( array_intersect( $allowed_roles, $current_roles ) );
	}

	/**
	 * Get the collection of matched vectors with scores.
	 *
	 * @return Collection|null Collection of vectors or null if none
	 *
	 * @since 1.0.0
	 */
	public function get_vectors(): ?Collection {
		return $this->vectors ?? new Collection();
	}
}
