<?php
namespace TeknoTok\AiTranslator\Database;

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

/**
 * Repository to interface with the Translation Memory database table.
 */
class MemoryRepository {
	/**
	 * Static in-memory cache for loaded translations.
	 * Format: array( target_lang => array( source_hash => translated_text ) )
	 *
	 * @var array
	 */
	private static $static_cache = array();

	/**
	 * Flag to track if the cache size for a language exceeds the in-memory threshold.
	 *
	 * @var array
	 */
	private static $large_cache_mode = array();

	/**
	 * Get table name.
	 *
	 * @return string
	 */
	private static function get_table_name() {
		global $wpdb;
		return $wpdb->prefix . 'teknotok_translations';
	}

	/**
	 * Set the large cache mode flag for a target language (used for testing).
	 *
	 * @param string $target_lang Target language code.
	 * @param bool   $enabled     Whether large cache mode is enabled.
	 * @return void
	 */
	public static function set_large_cache_mode( $target_lang, $enabled ) {
		self::$large_cache_mode[ $target_lang ] = (bool) $enabled;
	}

	/**
	 * Initialize static cache for the given target language if not already loaded.
	 *
	 * @param string $target_lang Target language code.
	 * @return void
	 */
	private static function init_static_cache( $target_lang ) {
		global $wpdb;

		if ( isset( self::$static_cache[ $target_lang ] ) ) {
			return;
		}

		self::$static_cache[ $target_lang ] = array();
		$table = self::get_table_name();

		// Count the translations for this target language to determine caching strategy
		$count = 0;
		if ( ! isset( self::$large_cache_mode[ $target_lang ] ) && method_exists( $wpdb, 'get_var' ) ) {
			$count = (int) $wpdb->get_var( $wpdb->prepare(
				"SELECT COUNT(*) FROM {$table} WHERE target_lang = %s",
				$target_lang
			) );
		}

		if ( ! empty( self::$large_cache_mode[ $target_lang ] ) || $count >= 5000 ) {
			self::$large_cache_mode[ $target_lang ] = true;
			return;
		}

		$query = $wpdb->prepare(
			"SELECT source_hash, translated_text FROM {$table} WHERE target_lang = %s",
			$target_lang
		);

		$results = $wpdb->get_results( $query );

		if ( ! empty( $results ) ) {
			foreach ( $results as $row ) {
				self::$static_cache[ $target_lang ][ $row->source_hash ] = $row->translated_text;
			}
		}
	}

	/**
	 * Retrieve a translation from cache.
	 *
	 * @param string $source_text Raw original text.
	 * @param string $target_lang Target language code.
	 * @return string|false Translated string on success, false if not cached.
	 */
	public static function get_translation( $source_text, $target_lang ) {
		if ( empty( $source_text ) ) {
			return $source_text;
		}

		global $wpdb;

		$hash = md5( trim( $source_text ) );
		self::init_static_cache( $target_lang );

		// Check static memory cache
		if ( isset( self::$static_cache[ $target_lang ][ $hash ] ) ) {
			$translated_text = html_entity_decode( self::$static_cache[ $target_lang ][ $hash ], ENT_QUOTES, 'UTF-8' );
			return '' !== trim( (string) $translated_text ) ? $translated_text : false;
		}

		// If in large cache mode, query database/object cache for the specific translation
		if ( ! empty( self::$large_cache_mode[ $target_lang ] ) ) {
			$cache_group = 'teknotok_translations_' . $target_lang;
			$cached = wp_cache_get( $hash, $cache_group );
			
			if ( false !== $cached ) {
				return '' !== trim( (string) $cached ) ? html_entity_decode( $cached, ENT_QUOTES, 'UTF-8' ) : false;
			}

			$table = self::get_table_name();
			$db_translation = $wpdb->get_var( $wpdb->prepare(
				"SELECT translated_text FROM {$table} WHERE source_hash = %s AND target_lang = %s LIMIT 1",
				$hash,
				$target_lang
			) );

			$store_val = $db_translation !== null ? $db_translation : '';
			self::$static_cache[ $target_lang ][ $hash ] = $store_val;
			wp_cache_set( $hash, $store_val, $cache_group );

			if ( '' !== $store_val ) {
				return html_entity_decode( $store_val, ENT_QUOTES, 'UTF-8' );
			}
		}

		return false;
	}

	/**
	 * Retrieve a batch of translations in a single database query.
	 *
	 * @param array  $source_texts List of raw original strings.
	 * @param string $target_lang  Target language code.
	 * @return array Array mapping MD5 hash to translated text.
	 */
	public static function get_translations_batch( $source_texts, $target_lang ) {
		if ( empty( $source_texts ) ) {
			return array();
		}

		global $wpdb;
		self::init_static_cache( $target_lang );

		$translations = array();
		$missing_hashes = array();
		$hash_to_text = array();

		foreach ( $source_texts as $text ) {
			$trimmed = trim( $text );
			if ( empty( $trimmed ) ) {
				continue;
			}
			$hash = md5( $trimmed );
			$hash_to_text[ $hash ] = $trimmed;

			if ( isset( self::$static_cache[ $target_lang ][ $hash ] ) ) {
				$translated_text = html_entity_decode( self::$static_cache[ $target_lang ][ $hash ], ENT_QUOTES, 'UTF-8' );
				if ( '' !== trim( (string) $translated_text ) ) {
					$translations[ $hash ] = $translated_text;
				}
			} elseif ( ! empty( self::$large_cache_mode[ $target_lang ] ) ) {
				$missing_hashes[] = $hash;
			}
		}

		if ( ! empty( $missing_hashes ) ) {
			$cache_group = 'teknotok_translations_' . $target_lang;
			$db_hashes = array();

			// 1. Try WP Object Cache
			foreach ( $missing_hashes as $hash ) {
				$cached = wp_cache_get( $hash, $cache_group );
				if ( false !== $cached ) {
					self::$static_cache[ $target_lang ][ $hash ] = $cached;
					if ( '' !== trim( (string) $cached ) ) {
						$translations[ $hash ] = html_entity_decode( $cached, ENT_QUOTES, 'UTF-8' );
					}
				} else {
					$db_hashes[] = $hash;
				}
			}

			// 2. Query missing hashes from DB in a single batch
			if ( ! empty( $db_hashes ) ) {
				$table = self::get_table_name();
				$placeholders = implode( ',', array_fill( 0, count( $db_hashes ), '%s' ) );
				$query = $wpdb->prepare(
					"SELECT source_hash, translated_text FROM {$table} WHERE target_lang = %s AND source_hash IN ($placeholders)",
					array_merge( array( $target_lang ), $db_hashes )
				);

				$results = $wpdb->get_results( $query );
				$found_hashes = array();

				if ( ! empty( $results ) ) {
					foreach ( $results as $row ) {
						$found_hashes[ $row->source_hash ] = $row->translated_text;
						self::$static_cache[ $target_lang ][ $row->source_hash ] = $row->translated_text;
						wp_cache_set( $row->source_hash, $row->translated_text, $cache_group );
						
						$translated_text = html_entity_decode( $row->translated_text, ENT_QUOTES, 'UTF-8' );
						if ( '' !== trim( (string) $translated_text ) ) {
							$translations[ $row->source_hash ] = $translated_text;
						}
					}
				}

				// Cache empty values for missing translations to prevent repeated DB queries
				foreach ( $db_hashes as $hash ) {
					if ( ! isset( $found_hashes[ $hash ] ) ) {
						self::$static_cache[ $target_lang ][ $hash ] = '';
						wp_cache_set( $hash, '', $cache_group );
					}
				}
			}
		}

		return $translations;
	}

	/**
	 * Store a translation in the translation memory table.
	 *
	 * @param string $source_text Original text string.
	 * @param string $translated_text Translated text string.
	 * @param string $source_lang Source language code.
	 * @param string $target_lang Target language code.
	 * @return bool True on success, false on failure.
	 */
	public static function save_translation( $source_text, $translated_text, $source_lang, $target_lang ) {
		global $wpdb;

		if ( empty( $source_text ) || empty( $translated_text ) ) {
			return false;
		}

		$translated_text = html_entity_decode( $translated_text, ENT_QUOTES, 'UTF-8' );
		$hash = md5( trim( $source_text ) );
		$table = self::get_table_name();

		$data = array(
			'source_hash'     => $hash,
			'source_text'     => $source_text,
			'translated_text' => $translated_text,
			'source_lang'     => $source_lang,
			'target_lang'     => $target_lang,
			'last_used'       => current_time( 'mysql' ),
		);

		$format = array( '%s', '%s', '%s', '%s', '%s', '%s' );

		$inserted = $wpdb->replace( $table, $data, $format );

		if ( false !== $inserted ) {
			self::init_static_cache( $target_lang );
			self::$static_cache[ $target_lang ][ $hash ] = $translated_text;
			return true;
		}

		return false;
	}

	/**
	 * Delete translation memory rows for removed target languages.
	 *
	 * @param array $languages Target language codes.
	 * @return int Number of deleted translation rows.
	 */
	public static function delete_translations_for_languages( $languages ) {
		global $wpdb;

		$languages = array_values( array_unique( array_filter( array_map( 'sanitize_key', (array) $languages ) ) ) );
		if ( empty( $languages ) ) {
			return 0;
		}

		foreach ( $languages as $language ) {
			unset( self::$static_cache[ $language ], self::$large_cache_mode[ $language ] );

			if ( function_exists( 'wp_cache_flush_group' ) ) {
				wp_cache_flush_group( 'teknotok_translations_' . $language );
			}
		}

		$table        = self::get_table_name();
		$placeholders = implode( ',', array_fill( 0, count( $languages ), '%s' ) );

		return (int) $wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$table} WHERE target_lang IN ({$placeholders})",
				$languages
			)
		);
	}

	/**
	 * Clear translation cache data.
	 *
	 * @return void
	 */
	public static function clear_cache() {
		global $wpdb;
		$table = self::get_table_name();
		$wpdb->query( "TRUNCATE TABLE {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		self::$static_cache = array();
	}
}
