<?php
/**
 * @package Hafenstudios\Core
 */

namespace Hafenstudios\Core\Ai;

defined( 'ABSPATH' ) || exit;

/**
 * Gemeinsame Basis fuer HTTP-basierte LLM-Treiber (BYO Anthropic, BYO
 * OpenAI-kompatibel). Kapselt den `wp_remote_post`-Aufruf und die einheitliche
 * Fehlerklassifikation analog zum Mail-AbstractApiEspDriver: 429 und 5xx sind
 * voruebergehend (Retry sinnvoll), 400/401/403 und uebrige 4xx sind dauerhaft
 * (ungueltiger Schluessel, verbotenes Modell, fehlerhafte Anfrage).
 *
 * Jeder Treiber liefert nur Endpunkt, Header, Body-Aufbau und die Extraktion
 * von Text und Verbrauchszaehlern aus der Anbieter-Antwort.
 *
 * Nicht final, weil die konkreten Treiber davon erben.
 */
abstract class AbstractHttpAiDriver implements AiDriver {

	/**
	 * Ziel-URL des API-Endpunkts.
	 *
	 * @return string
	 */
	abstract protected function endpoint(): string;

	/**
	 * Sind die Zugangsdaten (und der Endpunkt) vorhanden?
	 *
	 * @return bool
	 */
	abstract protected function is_configured(): bool;

	/**
	 * Meldung, wenn die Konfiguration fehlt.
	 *
	 * @return string
	 */
	abstract protected function config_error(): string;

	/**
	 * HTTP-Header des Aufrufs.
	 *
	 * @return array<string, string>
	 */
	abstract protected function headers(): array;

	/**
	 * Baut den Request-Body als assoziatives Array (wird JSON-kodiert).
	 *
	 * @param AiRequest $request Der Aufruf.
	 * @return array<string, mixed>
	 */
	abstract protected function build_body( AiRequest $request ): array;

	/**
	 * Extrahiert den erzeugten Text aus der dekodierten Erfolgsantwort.
	 *
	 * @param array<string, mixed> $json Dekodierte Antwort.
	 * @return string
	 */
	abstract protected function parse_text( array $json ): string;

	/**
	 * Extrahiert die Verbrauchszaehler (input/output) aus der Antwort.
	 *
	 * @param array<string, mixed> $json Dekodierte Antwort.
	 * @return array{input:int,output:int} -1 je Wert, wenn nicht lieferbar.
	 */
	abstract protected function parse_usage( array $json ): array;

	/**
	 * @param AiRequest $request Der Aufruf.
	 * @return AiResult
	 */
	public function complete( AiRequest $request ): AiResult {
		if ( ! $this->is_configured() ) {
			return AiResult::permanent( $this->config_error() );
		}

		$response = wp_remote_post(
			$this->endpoint(),
			array(
				'timeout' => $request->timeout,
				'headers' => $this->headers(),
				'body'    => (string) wp_json_encode( $this->build_body( $request ) ),
			)
		);

		if ( is_wp_error( $response ) ) {
			return AiResult::transient( $response->get_error_message() );
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		$body = (string) wp_remote_retrieve_body( $response );

		if ( $code >= 200 && $code < 300 ) {
			$json = json_decode( $body, true );
			if ( ! is_array( $json ) ) {
				return AiResult::permanent( 'Ungueltige Antwort vom Anbieter.' );
			}
			$usage = $this->parse_usage( $json );
			return AiResult::success( $this->parse_text( $json ), (int) $usage['input'], (int) $usage['output'] );
		}

		$error = 'HTTP ' . $code . ': ' . substr( $body, 0, 200 );
		if ( 429 === $code || $code >= 500 ) {
			return AiResult::transient( $error );
		}
		return AiResult::permanent( $error );
	}
}
