<?php
/**
 * AI API abstraction layer.
 *
 * @package Effortless_Taiwan_Mandarin_Helper
 */

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

/**
 * Calls OpenAI or Anthropic to translate text to Taiwanese Mandarin.
 */
class ELTMH_API {

	/**
	 * Sets up the instance.
	 *
	 * @param array<string,string> $settings Plugin settings from get_option('eltmh_settings').
	 */
	public function __construct( private array $settings ) {}

	/**
	 * Returns the configured provider slug.
	 *
	 * @return string
	 */
	public function get_provider(): string {
		return $this->settings['provider'] ?? 'openai';
	}

	/**
	 * Translates source text to Taiwanese Mandarin.
	 *
	 * @param string $text Source text in any language.
	 * @return array<string,string>|\WP_Error Result keys: source_language, chinese, pinyin.
	 */
	public function translate( string $text ) {
		$provider = $this->settings['provider'] ?? 'openai';

		if ( 'anthropic' === $provider ) {
			return $this->call_anthropic( $text );
		}

		if ( 'wp_ai_client' === $provider ) {
			return $this->call_wp_ai_client( $text );
		}

		return $this->call_openai( $text );
	}

	/**
	 * Sends the translation request to OpenAI Chat Completions.
	 *
	 * @param string $text Source text.
	 * @return array<string,string>|\WP_Error
	 */
	private function call_openai( string $text ) {
		$api_key = $this->settings['openai_api_key'] ?? '';

		if ( empty( $api_key ) ) {
			return new WP_Error( 'eltmh_no_key', __( 'OpenAI API key is not configured.', 'effortless-taiwan-mandarin-helper' ) );
		}

		$response = wp_remote_post(
			'https://api.openai.com/v1/chat/completions',
			array(
				'headers' => array(
					'Authorization' => 'Bearer ' . $api_key,
					'Content-Type'  => 'application/json',
				),
				'body'    => wp_json_encode(
					array(
						'model'    => $this->settings['openai_model'] ?? 'gpt-4o',
						'messages' => array(
							array(
								'role'    => 'system',
								'content' => $this->system_prompt(),
							),
							array(
								'role'    => 'user',
								'content' => $text,
							),
						),
					)
				),
				'timeout' => 30,
			)
		);

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$status = wp_remote_retrieve_response_code( $response );
		if ( 200 !== (int) $status ) {
			$err_body     = json_decode( wp_remote_retrieve_body( $response ), true );
			$provider_msg = $err_body['error']['message'] ?? null;
			$message      = $provider_msg
				? 'OpenAI: ' . sanitize_text_field( $provider_msg )
				/* translators: %d: HTTP status code */
				: sprintf( __( 'OpenAI API returned HTTP %d.', 'effortless-taiwan-mandarin-helper' ), $status );
			return new WP_Error( 'eltmh_api_error', $message );
		}

		$body    = json_decode( wp_remote_retrieve_body( $response ), true );
		$content = $body['choices'][0]['message']['content'] ?? '';

		return $this->parse_result( $content );
	}

	/**
	 * Sends the translation request to Anthropic Messages API.
	 *
	 * @param string $text Source text.
	 * @return array<string,string>|\WP_Error
	 */
	private function call_anthropic( string $text ) {
		$api_key = $this->settings['anthropic_api_key'] ?? '';

		if ( empty( $api_key ) ) {
			return new WP_Error( 'eltmh_no_key', __( 'Anthropic API key is not configured.', 'effortless-taiwan-mandarin-helper' ) );
		}

		$response = wp_remote_post(
			'https://api.anthropic.com/v1/messages',
			array(
				'headers' => array(
					'x-api-key'         => $api_key,
					'anthropic-version' => '2023-06-01',
					'Content-Type'      => 'application/json',
				),
				'body'    => wp_json_encode(
					array(
						'model'      => $this->settings['anthropic_model'] ?? 'claude-sonnet-4-6',
						'max_tokens' => 1024,
						'system'     => $this->system_prompt(),
						'messages'   => array(
							array(
								'role'    => 'user',
								'content' => $text,
							),
						),
					)
				),
				'timeout' => 30,
			)
		);

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$status = wp_remote_retrieve_response_code( $response );
		if ( 200 !== (int) $status ) {
			$err_body     = json_decode( wp_remote_retrieve_body( $response ), true );
			$provider_msg = $err_body['error']['message'] ?? null;
			$message      = $provider_msg
				? 'Anthropic: ' . sanitize_text_field( $provider_msg )
				/* translators: %d: HTTP status code */
				: sprintf( __( 'Anthropic API returned HTTP %d.', 'effortless-taiwan-mandarin-helper' ), $status );
			return new WP_Error( 'eltmh_api_error', $message );
		}

		$body    = json_decode( wp_remote_retrieve_body( $response ), true );
		$content = $body['content'][0]['text'] ?? '';

		return $this->parse_result( $content );
	}

	/**
	 * Whether WordPress core's AI Client is available in this runtime.
	 *
	 * Extracted as a seam so tests can force either branch without needing
	 * wp_ai_client_prompt() to truly exist or not exist for the whole process.
	 *
	 * @return bool
	 */
	protected function is_wp_ai_client_available(): bool {
		return function_exists( 'wp_ai_client_prompt' );
	}

	/**
	 * Sends the translation request to WordPress core's AI Client (WP 7.0+).
	 *
	 * @param string $text Source text.
	 * @return array<string,string>|\WP_Error
	 */
	private function call_wp_ai_client( string $text ) {
		if ( ! function_exists( 'wp_ai_client_prompt' ) || ! $this->is_wp_ai_client_available() ) {
			return new WP_Error(
				'eltmh_ai_client_unavailable',
				__( 'The WordPress AI Client requires WordPress 7.0 or later. Update WordPress or choose a different provider in Mandarin Helper settings.', 'effortless-taiwan-mandarin-helper' )
			);
		}

		$builder = wp_ai_client_prompt( $text )->using_system_instruction( $this->system_prompt() );

		if ( ! $builder->is_supported_for_text_generation() ) {
			return new WP_Error(
				'eltmh_ai_client_not_configured',
				__( 'No AI provider is configured for this site. Configure one under Settings, or choose a different provider in Mandarin Helper settings.', 'effortless-taiwan-mandarin-helper' )
			);
		}

		$schema = array(
			'type'                 => 'object',
			'properties'           => array(
				'source_language' => array( 'type' => 'string' ),
				'chinese'         => array( 'type' => 'string' ),
				'pinyin'          => array( 'type' => 'string' ),
				'english'         => array( 'type' => array( 'string', 'null' ) ),
			),
			'required'             => array( 'source_language', 'chinese', 'pinyin', 'english' ),
			'additionalProperties' => false,
		);

		$result = $builder->as_json_response( $schema )->generate_text();

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return $this->parse_result( (string) $result );
	}

	/**
	 * Parses the raw JSON string returned by the AI model.
	 *
	 * Attempts three recovery stages before giving up:
	 * 1. Strip markdown code fences, then decode.
	 * 2. Extract first {...} substring from original content, then decode.
	 * 3. Return WP_Error.
	 *
	 * @param string $content Raw text from the model response.
	 * @return array<string,string>|\WP_Error
	 */
	private function parse_result( string $content ) {
		// Stage 1: strip markdown code fences (language-tagged or plain).
		$stripped = trim( $content );
		$stripped = (string) preg_replace( '/^```[a-z]*\s*/i', '', $stripped );
		$stripped = (string) preg_replace( '/\s*```$/i', '', $stripped );
		$stripped = trim( $stripped );

		$data = json_decode( $stripped, true );

		// Stage 2: extract the first {...} block from the original content.
		// Note: greedy match — if the model appends curly-brace-containing text after
		// the JSON object, the extracted span may be unparseable; Stage 3 handles that.
		if ( ! is_array( $data ) && preg_match( '/\{.*\}/s', $content, $m ) ) {
			$data = json_decode( $m[0], true );
		}

		// Stage 3: all recovery failed.
		if ( ! is_array( $data ) ) {
			return new WP_Error( 'eltmh_parse_error', __( 'Invalid JSON returned by AI provider.', 'effortless-taiwan-mandarin-helper' ) );
		}

		if ( isset( $data['error'] ) ) {
			return new WP_Error( 'eltmh_api_error', sanitize_text_field( $data['error'] ) );
		}

		if ( empty( $data['chinese'] ) || empty( $data['pinyin'] ) ) {
			return new WP_Error( 'eltmh_parse_error', __( 'Missing required fields in AI response.', 'effortless-taiwan-mandarin-helper' ) );
		}

		$result = array(
			'source_language' => sanitize_text_field( $data['source_language'] ?? 'Unknown' ),
			'chinese'         => sanitize_text_field( $data['chinese'] ),
			'pinyin'          => sanitize_text_field( $data['pinyin'] ),
		);

		if ( ! empty( $data['english'] ) ) {
			$result['english'] = sanitize_text_field( $data['english'] );
		}

		return $result;
	}

	/**
	 * Returns the system prompt for the translation model.
	 *
	 * @return string
	 */
	private function system_prompt(): string {
		return 'You are an expert translator and Taiwanese Mandarin language learning assistant.

Your task is to convert any input text into natural Taiwanese Mandarin (Traditional Chinese as used in Taiwan).

The user may provide text in any language, including English, French, Spanish, German, Japanese, Korean, Simplified Chinese, Traditional Chinese, Taiwanese Mandarin, or any other language.

## Translation Rules

### 1. Translate to Taiwanese Mandarin

If the input text is not already in Taiwanese Mandarin:
- Translate it into natural Taiwanese Mandarin.
- Use Traditional Chinese characters.
- Use vocabulary and expressions commonly used in Taiwan. Prefer Taiwanese Mandarin over Mainland Chinese Mandarin.
- Avoid Mainland China terminology whenever a Taiwanese equivalent exists.
- Preserve the original meaning, tone, and intent.

### 2. If the Input Is Already Chinese

If the input text is already written in Chinese (Simplified or Traditional, including Taiwanese Mandarin):
- Convert Simplified Chinese to Traditional Chinese if necessary.
- Adapt wording to Taiwanese Mandarin when appropriate.
- Use vocabulary and expressions commonly used in Taiwan. Prefer Taiwanese Mandarin over Mainland Chinese Mandarin.
- Preserve the original meaning.
- Also provide an English translation in the "english" field.

### 3. Pinyin Generation

Generate accurate Hanyu Pinyin for the final Taiwanese Mandarin text.
- Use tone marks.
- Preserve punctuation.
- Preserve sentence structure.
- Generate pinyin only for the Chinese output.

### 4. Bopomofo / Zhuyin

Do not generate Zhuyin. Return only Traditional Chinese and Pinyin.

### 5. Output Format

Return ONLY valid JSON. Do not include explanations, markdown, code blocks, or comments.

For non-Chinese input, use exactly this structure:

{"source_language":"detected language","chinese":"Traditional Chinese text used in Taiwan","pinyin":"Pinyin with tone marks"}

For Chinese input (Simplified or Traditional), also include an English translation:

{"source_language":"Chinese","chinese":"Traditional Chinese text used in Taiwan","pinyin":"Pinyin with tone marks","english":"English translation"}

### Error Handling

If the input is empty, return: {"error":"No text provided."}

### Important

Always return valid JSON. Never return markdown. Never return explanations. Never return notes. Never generate Zhuyin.';
	}
}
