<?php
/**
 * Answyr — AI client façade.
 *
 * The single entry point modules use to talk to the active AI provider. Reads
 * the configured provider / model / key from settings and dispatches, so
 * callers never touch provider internals.
 *
 * @package Answyr
 */

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

final class Answyr_AI_Client {

	/** Resolve the provider selected in settings (falling back to the first registered). */
	public function active_provider(): ?Answyr_AI_Provider {
		$id       = Answyr_Settings::get( 'provider', 'anthropic' );
		$provider = Answyr_Provider_Registry::get( $id );
		if ( $provider ) {
			return $provider;
		}
		$all = Answyr_Provider_Registry::all();
		return $all ? reset( $all ) : null;
	}

	/**
	 * Blocking completion through the active provider.
	 *
	 * @param array<int,array{role:string,content:string}> $messages
	 * @param array<string,mixed>                          $overrides Optional model/max_tokens/temperature/api_key.
	 */
	public function complete( string $system, array $messages, array $overrides = array() ): string {
		/**
		 * Short-circuit the provider call. Return a string to skip the network
		 * request entirely (used by tests to stub AI output, and available for
		 * custom caching/mocking). Return null to proceed normally.
		 */
		$pre = apply_filters( 'answyr_ai_pre_complete', null, $system, $messages, $overrides );
		if ( is_string( $pre ) ) {
			return $pre;
		}

		$provider = $this->active_provider();
		if ( ! $provider ) {
			throw new \RuntimeException( esc_html__( 'No AI provider is configured.', 'answyr' ) );
		}

		$opts = $this->resolve_opts( $provider, $overrides );

		if ( '' === trim( (string) $opts['api_key'] ) ) {
			throw new \RuntimeException(
				/* translators: %s: provider label */
				esc_html( sprintf( __( 'No API key set for %s.', 'answyr' ), $provider->label() ) )
			);
		}

		return $provider->complete( $system, $messages, $opts );
	}

	/**
	 * Merge defaults + settings + per-call overrides into the opts bag.
	 *
	 * @param array<string,mixed> $overrides
	 * @return array<string,mixed>
	 */
	private function resolve_opts( Answyr_AI_Provider $provider, array $overrides ): array {
		$defaults = array(
			'api_key'     => Answyr_Settings::api_key( $provider->id() ),
			'model'       => Answyr_Settings::get( 'model', $provider->default_model() ),
			'max_tokens'  => (int) Answyr_Settings::get( 'max_tokens', 1500 ),
			'temperature' => (float) Answyr_Settings::get( 'temperature', 0.3 ),
		);

		$opts = array_merge( $defaults, $overrides );

		// If a model was selected for a different provider, fall back to this one's default.
		if ( '' === trim( (string) $opts['model'] ) || ! array_key_exists( $opts['model'], $provider->models() ) ) {
			$opts['model'] = $provider->default_model();
		}

		return $opts;
	}
}
