<?php

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

class Niwa_AI_Name_Processor {

	/**
	 * @var Niwa_AI_AI_Client_Interface
	 */
	private $openai_client;

	/**
	 * @var Niwa_AI_Auth_Provider_Interface
	 */
	private $auth_manager;

	/**
	 * Blacklisted name values.
	 *
	 * @var array
	 */
	private static $name_blacklist = array(
		'test', 'asdf', 'admin', 'user', 'guest', 'anonymous', 'anon',
		'root', 'null', 'undefined', 'none', 'nobody', 'niko', 'aaaa',
		'qwerty', 'zxcv', 'aaa', 'bbb', 'ccc', 'xxx', 'yyy', 'zzz',
		'fuck', 'shit', 'dick', 'ass', 'bitch',
	);

	/**
	 * @param Niwa_AI_AI_Client_Interface    $openai_client OpenAI API client.
	 * @param Niwa_AI_Auth_Provider_Interface $auth_manager  Auth manager.
	 */
	public function __construct( Niwa_AI_AI_Client_Interface $openai_client, Niwa_AI_Auth_Provider_Interface $auth_manager ) {
		$this->openai_client = $openai_client;
		$this->auth_manager  = $auth_manager;
	}

	/**
	 * Full name processing pipeline: validate → sentence check → canonicalize → vocative → template fill.
	 *
	 * @param string $name_raw Raw user input.
	 * @param string $template Template string with {name} placeholder.
	 * @return array { success, final_text, should_store, name_canonical, is_sentence }
	 */
	public function process( $name_raw, $template ) {
		$name_raw = trim( (string) $name_raw );

		// Basic validation.
		$validation = $this->basic_validate_name( $name_raw );
		if ( ! $validation['valid'] ) {
			return array(
				'success'        => false,
				'final_text'     => '',
				'should_store'   => false,
				'name_canonical' => null,
				'is_sentence'    => false,
			);
		}

		// Sentence detection.
		if ( $this->looks_like_sentence( $name_raw ) ) {
			return array(
				'success'        => true,
				'final_text'     => '',
				'should_store'   => false,
				'name_canonical' => null,
				'is_sentence'    => true,
			);
		}

		// Canonicalize.
		$canonical = $this->canonicalize_name( $name_raw );

		// Get vocative form via LLM.
		$vocative = $this->get_vocative( $canonical );

		// Fill template with vocative form.
		$final_text = str_replace( '{name}', esc_html( $vocative ), $template );

		return array(
			'success'        => true,
			'final_text'     => $final_text,
			'should_store'   => true,
			'name_canonical' => $canonical,
			'is_sentence'    => false,
		);
	}

	/**
	 * Basic name validation.
	 *
	 * @param string $name Name to validate.
	 * @return array { valid: bool, reason: string }
	 */
	public function basic_validate_name( $name ) {
		$name = trim( (string) $name );

		// Length check: 2-40 characters.
		$len = mb_strlen( $name );
		if ( $len < 2 || $len > 40 ) {
			return array( 'valid' => false, 'reason' => 'length' );
		}

		// Reject emails (contains @).
		if ( false !== strpos( $name, '@' ) ) {
			return array( 'valid' => false, 'reason' => 'email' );
		}

		// Reject URLs (contains ://).
		if ( false !== strpos( $name, '://' ) ) {
			return array( 'valid' => false, 'reason' => 'url' );
		}

		// Reject emoji (common Unicode ranges).
		if ( preg_match( '/[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F1E0}-\x{1F1FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]/u', $name ) ) {
			return array( 'valid' => false, 'reason' => 'emoji' );
		}

		// Reject digits-only.
		if ( preg_match( '/^\d+$/', $name ) ) {
			return array( 'valid' => false, 'reason' => 'digits_only' );
		}

		// Blacklist check.
		$lower = mb_strtolower( $name );
		if ( in_array( $lower, self::$name_blacklist, true ) ) {
			return array( 'valid' => false, 'reason' => 'blacklist' );
		}

		// Reject 3+ consecutive identical characters.
		if ( preg_match( '/(.)\1{2,}/u', $name ) ) {
			return array( 'valid' => false, 'reason' => 'repeated_chars' );
		}

		// Reject more than 4 words.
		$words = preg_split( '/\s+/', trim( $name ) );
		if ( count( $words ) > 4 ) {
			return array( 'valid' => false, 'reason' => 'too_many_words' );
		}

		return array( 'valid' => true, 'reason' => '' );
	}

	/**
	 * Check if input looks like a sentence/question rather than a name.
	 *
	 * @param string $input User input.
	 * @return bool
	 */
	public function looks_like_sentence( $input ) {
		$lower = mb_strtolower( trim( (string) $input ) );

		// Punctuation indicators.
		$sentence_indicators = array(
			'?', '.', '!',
			' je ', ' sam ', ' mi ', ' me ', ' my ',
			' is ', ' am ', ' are ', ' do ', ' can ',
			' the ', ' what ', ' how ', ' why ', ' when ',
			' sta ', ' šta ', ' kako ', ' zasto ', ' zašto ',
			' da li ', ' koji ', ' koja ', ' koje ',
		);

		foreach ( $sentence_indicators as $indicator ) {
			if ( false !== mb_strpos( $lower, $indicator ) ) {
				return true;
			}
		}

		// Long input with spaces is likely a sentence.
		if ( mb_strlen( $input ) > 60 && false !== mb_strpos( $input, ' ' ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Canonicalize a name: title case, trim, normalize whitespace.
	 *
	 * @param string $name Raw name.
	 * @return string
	 */
	public function canonicalize_name( $name ) {
		$name = trim( (string) $name );
		$name = preg_replace( '/\s+/', ' ', $name );
		$name = mb_convert_case( $name, MB_CASE_TITLE, 'UTF-8' );
		return $name;
	}

	/**
	 * Get vocative case of a Serbian name via LLM, with transient caching.
	 *
	 * @param string $canonical_name Canonical (title-cased) name.
	 * @return string Vocative form, or original name on failure.
	 */
	public function get_vocative( $canonical_name ) {
		$cache_key = 'niwa_ai_vocative_' . md5( mb_strtolower( $canonical_name ) );
		$cached    = get_transient( $cache_key );

		if ( false !== $cached && '' !== $cached ) {
			return $cached;
		}

		$system_prompt = 'Ti si ekspert za srpski jezik. Pretvori dato ime u vokativ. '
			. 'Primeni pravila srpskog vokativa za uneto lično ime i nemoj dodavati objašnjenja, pozdrav, interpunkciju ni alternativne oblike. '
			. 'Vrati SAMO vokativ, ništa drugo.';

		$auth_mode = $this->auth_manager->get_mode();
		$creds     = $this->auth_manager->get_credentials();

		$result = $this->openai_client->generate(
			'gpt-4.1-mini',
			$canonical_name,
			$system_prompt,
			$auth_mode,
			$creds,
			10
		);

		if ( ! empty( $result['success'] ) && ! empty( $result['answer'] ) ) {
			$vocative = trim( $result['answer'] );
			// Sanity check: vocative should be short and similar length to input.
			if ( mb_strlen( $vocative ) <= mb_strlen( $canonical_name ) + 5 && mb_strlen( $vocative ) >= 1 ) {
				// Remove any surrounding quotes or punctuation from LLM output.
				$vocative = trim( $vocative, " \t\n\r\0\x0B\"'.,!?" );
				set_transient( $cache_key, $vocative, HOUR_IN_SECONDS );
				return $vocative;
			}
		}

		// Fallback: return canonical name.
		set_transient( $cache_key, $canonical_name, HOUR_IN_SECONDS );
		return $canonical_name;
	}
}
