<?php
/**
 * Translation cache using wp_options.
 *
 * @package Effortless_Taiwan_Mandarin_Helper
 */

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

/**
 * Manages wp_options-based translation cache.
 *
 * Keys are prefixed with `eltmh_cache_` followed by an MD5 hash of
 * provider + ':' + lower-cased, trimmed source text. Options are stored
 * without autoload to avoid bloating the options cache on every page load.
 */
class ELTMH_Cache {

	const KEY_PREFIX  = 'eltmh_cache_';
	const MAX_ENTRIES = 500;

	/**
	 * Returns a cached translation result or false on a miss.
	 *
	 * @param string $text     Source text.
	 * @param string $provider Provider slug (e.g. 'openai', 'anthropic').
	 * @return array<string,string>|false
	 */
	public function get( string $text, string $provider = '' ) {
		return get_option( $this->key( $text, $provider ), false );
	}

	/**
	 * Stores a translation result in the cache, then evicts oldest entries if over limit.
	 *
	 * @param string               $text     Source text.
	 * @param array<string,string> $result   Translation result.
	 * @param string               $provider Provider slug.
	 */
	public function set( string $text, array $result, string $provider = '' ): void {
		update_option( $this->key( $text, $provider ), $result, false );
		$this->maybe_evict();
	}

	/**
	 * Deletes all cached translations and returns the count removed.
	 *
	 * @return int
	 */
	public function flush_all(): int {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$keys = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
				$wpdb->esc_like( self::KEY_PREFIX ) . '%'
			)
		);

		foreach ( $keys as $key ) {
			delete_option( $key );
		}

		return count( $keys );
	}

	/**
	 * Evicts the oldest entries when the cache exceeds MAX_ENTRIES.
	 */
	private function maybe_evict(): void {
		global $wpdb;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$count = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE %s",
				$wpdb->esc_like( self::KEY_PREFIX ) . '%'
			)
		);

		if ( $count <= self::MAX_ENTRIES ) {
			return;
		}

		$excess = $count - self::MAX_ENTRIES;

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$old_keys = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_id ASC LIMIT %d",
				$wpdb->esc_like( self::KEY_PREFIX ) . '%',
				$excess
			)
		);

		foreach ( $old_keys as $key ) {
			delete_option( $key );
		}
	}

	/**
	 * Builds a cache key from provider and source text.
	 *
	 * @param string $text     Source text.
	 * @param string $provider Provider slug.
	 * @return string
	 */
	private function key( string $text, string $provider = '' ): string {
		return self::KEY_PREFIX . md5( $provider . ':' . strtolower( trim( $text ) ) );
	}
}
