<?php

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

abstract class Sabriel_AI_Provider {

	const CHIAVE_JSON = 'sabriel_ai_json';

	public static function legge_pdf() {
		return true;
	}

	abstract public function generate( $system_text, $contents, $generation_config );

	public static function extract_text( $gemini_shaped_data ) {
		$parti = $gemini_shaped_data['candidates'][0]['content']['parts'] ?? null;
		if ( ! is_array( $parti ) ) {
			return '';
		}

		$testo = '';
		foreach ( $parti as $parte ) {
			if ( isset( $parte['text'] ) && is_scalar( $parte['text'] ) ) {
				$testo .= (string) $parte['text'];
			}
		}

		return $testo;
	}

	protected static function modello_sicuro( $valore, $riserva ) {
		$valore = trim( (string) $valore );
		if ( '' === $valore || ! preg_match( '/^[A-Za-z0-9._-]+$/', $valore ) ) {
			return $riserva;
		}
		return $valore;
	}

	protected static function togli_blocchi_vuoti( $blocchi ) {
		$puliti = [];
		foreach ( (array) $blocchi as $b ) {
			if ( ! is_array( $b ) ) {
				continue;
			}
			if ( isset( $b['type'] ) && 'text' === $b['type'] ) {
				if ( ! is_scalar( $b['text'] ?? null ) || '' === trim( (string) $b['text'] ) ) {
					continue;
				}
				$b['text'] = (string) $b['text'];
			}
			$puliti[] = $b;
		}
		return $puliti;
	}

	/**
	 * Turns a provider's own error into a message the site owner can read
	 * in their own language.
	 *
	 * Each engine reports the reason in a structured field, not in prose:
	 * OpenAI in error.code and error.type, Anthropic in error.type, Google in
	 * error.status. Matching those is stable; matching the English sentence is
	 * not, since providers reword it whenever they like. Anything unrecognised
	 * falls back to the provider's own wording, so nothing is ever swallowed.
	 *
	 * @param string $nome_motore engine name shown to the user, e.g. OpenAI.
	 * @param int    $code        HTTP response code.
	 * @param mixed  $data        decoded response body.
	 * @param string $modello     model identifier that was requested.
	 * @return string
	 */
	public static function errore_leggibile( $nome_motore, $code, $data, $modello = '' ) {
		$code    = (int) $code;
		$errore  = ( is_array( $data ) && isset( $data['error'] ) && is_array( $data['error'] ) ) ? $data['error'] : [];
		$grezzo  = ( isset( $errore['message'] ) && is_scalar( $errore['message'] ) ) ? trim( (string) $errore['message'] ) : '';

		$segnali = '';
		foreach ( [ 'code', 'type', 'status', 'reason' ] as $sab_campo ) {
			if ( isset( $errore[ $sab_campo ] ) && is_scalar( $errore[ $sab_campo ] ) ) {
				$segnali .= ' ' . strtolower( (string) $errore[ $sab_campo ] );
			}
		}

		$ha = function ( $ago ) use ( $segnali ) {
			return false !== strpos( $segnali, $ago );
		};

		// Chiave sbagliata, revocata o scaduta.
		if ( 401 === $code || $ha( 'invalid_api_key' ) || $ha( 'authentication_error' ) || $ha( 'unauthenticated' ) ) {
			return sprintf(
				/* translators: %s: name of the AI engine, e.g. OpenAI */
				__( 'The %s key was refused: it is wrong, it has been revoked, or it has expired. Create a new one on the provider\'s site and paste it in again.', 'sabriel-ai' ),
				$nome_motore
			);
		}

		// Modello che non esiste o a cui l'account non ha accesso.
		if ( 404 === $code || $ha( 'model_not_found' ) || $ha( 'not_found' ) ) {
			if ( '' !== $modello ) {
				return sprintf(
					/* translators: 1: model identifier that was requested; 2: name of the AI engine */
					__( 'The model "%1$s" does not exist on %2$s, or your account has no access to it. Pick another one from the model list.', 'sabriel-ai' ),
					$modello,
					$nome_motore
				);
			}
			return sprintf(
				/* translators: %s: name of the AI engine, e.g. OpenAI */
				__( 'The chosen model does not exist on %s, or your account has no access to it. Pick another one from the model list.', 'sabriel-ai' ),
				$nome_motore
			);
		}

		// Credito finito.
		if ( $ha( 'insufficient_quota' ) || $ha( 'billing' ) || $ha( 'credit' ) ) {
			return sprintf(
				/* translators: %s: name of the AI engine, e.g. OpenAI */
				__( 'The %s account has run out of credit. Top up the balance or add a payment method in your provider account, then try again.', 'sabriel-ai' ),
				$nome_motore
			);
		}

		// Chiave valida ma non abilitata a questa API.
		if ( 403 === $code || $ha( 'permission_denied' ) || $ha( 'permission' ) ) {
			return sprintf(
				/* translators: %s: name of the AI engine, e.g. Google Gemini */
				__( '%s refused the request: the key is valid but not allowed to use this service. Check the restrictions on the key in your provider account.', 'sabriel-ai' ),
				$nome_motore
			);
		}

		// Niente di riconosciuto: si riporta la frase del fornitore, che e'
		// piu' utile di un codice, dicendo almeno da chi arriva.
		if ( '' !== $grezzo ) {
			return sprintf(
				/* translators: 1: name of the AI engine; 2: the error message returned by that engine, in English */
				__( '%1$s reports: %2$s', 'sabriel-ai' ),
				$nome_motore,
				$grezzo
			);
		}

		return sprintf(
			/* translators: 1: name of the AI engine; 2: HTTP response code */
			__( 'Error %1$s, code %2$d', 'sabriel-ai' ),
			$nome_motore,
			$code
		);
	}

	protected static function chiave_o_errore( $opzione, $nome_motore ) {
		$grezza = (string) get_option( $opzione, '' );
		$chiara = class_exists( 'Sabriel_Crypto' ) ? (string) Sabriel_Crypto::decrypt( $grezza ) : $grezza;

		if ( '' !== $chiara ) {
			return [ 'chiave' => $chiara ];
		}

		if ( '' !== $grezza ) {
			return [ 'errore' => [
				'ok'    => false,
				'error' => sprintf(
					/* translators: %s: name of the AI engine, e.g. Gemini */
					__( 'The API key %s is saved but no longer readable: this happens when WordPress security keys change or the site is moved. Go to Sabriel AI → AI Engine and enter it again.', 'sabriel-ai' ),
					$nome_motore
				),

				'http_status' => 503,
			] ];
		}

		return [ 'errore' => [
			'ok'    => false,
			'error' => sprintf(
				/* translators: %s: name of the AI engine, e.g. Gemini */
				__( 'API key %s not configured. Go to Sabriel AI → AI Engine.', 'sabriel-ai' ),
				$nome_motore
			),

			'http_status' => 503,
		] ];
	}
}
