<?php
/**
 * Compiled HTML cache service for Mail Doctor emails.
 *
 * @package MailDoctor
 */

namespace MailDoctor\Email;

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

/**
 * Provides helpers for storing and invalidating compiled template output.
 */
class CacheService {
	public const CACHE_VERSION = 'v1';

	protected const INDEX_OPTION          = 'mail_doctor_compiled_template_index';
	protected const MAX_KEYS_PER_TEMPLATE = 25;

	protected static int $hits   = 0;
	protected static int $misses = 0;

	/**
	 * Retrieve cached HTML for a given template/email/locale combination.
	 *
	 * @param string $template_id Template identifier.
	 * @param string $email_id    Woo email identifier.
	 * @param string $locale      Locale string.
	 * @param string $hash        Cache hash component.
	 * @return string|null
	 */
	public static function get( string $template_id, string $email_id, string $locale, string $hash ): ?string {
		$key   = self::build_key( $template_id, $email_id, $locale, $hash );
		$value = get_transient( $key );

		if ( false === $value || null === $value ) {
			++self::$misses;
			return null;
		}

		++self::$hits;

		return (string) $value;
	}

	/**
	 * Store compiled HTML in cache.
	 *
	 * @param string $template_id Template identifier.
	 * @param string $email_id    Woo email identifier.
	 * @param string $locale      Locale string.
	 * @param string $hash        Cache hash component.
	 * @param string $html        Compiled HTML payload.
	 * @param int    $ttl         Cache lifetime in seconds.
	 * @return void
	 */
	public static function set( string $template_id, string $email_id, string $locale, string $hash, string $html, int $ttl = DAY_IN_SECONDS ): void {
		$key = self::build_key( $template_id, $email_id, $locale, $hash );

		set_transient( $key, $html, max( MINUTE_IN_SECONDS, $ttl ) );

		$index = self::load_index();
		$list  = $index[ $template_id ] ?? array();

		$list[ $key ] = time();

		if ( count( $list ) > self::MAX_KEYS_PER_TEMPLATE ) {
			asort( $list );
			$list = array_slice( $list, -1 * self::MAX_KEYS_PER_TEMPLATE, null, true );
		}

		$index[ $template_id ] = $list;
		self::save_index( $index );
	}

	/**
	 * Drop cached entries for a specific template.
	 *
	 * @param string $template_id Template identifier.
	 * @return void
	 */
	public static function invalidate_template( string $template_id ): void {
		$template_id = (string) $template_id;
		if ( '' === $template_id ) {
			return;
		}

		$index = self::load_index();

		if ( empty( $index[ $template_id ] ) || ! is_array( $index[ $template_id ] ) ) {
			return;
		}

		foreach ( array_keys( $index[ $template_id ] ) as $cache_key ) {
			delete_transient( $cache_key );
		}

		unset( $index[ $template_id ] );
		self::save_index( $index );
	}

	/**
	 * Clear the entire compiled cache.
	 *
	 * @return void
	 */
	public static function invalidate_all(): void {
		$index = self::load_index();

		foreach ( $index as $template_id => $keys ) {
			if ( ! is_array( $keys ) ) {
				continue;
			}

			foreach ( array_keys( $keys ) as $cache_key ) {
				delete_transient( $cache_key );
			}
		}

		self::save_index( array() );
	}

	/**
	 * Provide cache diagnostics for admin displays.
	 *
	 * @return array<string, mixed>
	 */
	public static function diagnostics(): array {
		return array(
			'version' => self::CACHE_VERSION,
			'hits'    => self::$hits,
			'misses'  => self::$misses,
		);
	}

	/**
	 * Report the active cache version string.
	 *
	 * @return string
	 */
	public static function version(): string {
		return self::CACHE_VERSION;
	}

	/**
	 * Build a cache key string.
	 *
	 * @param string $template_id Template identifier.
	 * @param string $email_id    Email identifier.
	 * @param string $locale      Locale code.
	 * @param string $hash        Hash component.
	 * @return string
	 */
	protected static function build_key( string $template_id, string $email_id, string $locale, string $hash ): string {
		$segments = array(
			'wpmd',
			'tpl',
			self::slugify( $template_id ),
			self::slugify( $email_id ),
			self::slugify( $locale ),
			self::CACHE_VERSION,
			substr( preg_replace( '/[^a-f0-9]/', '', strtolower( $hash ) ), 0, 12 ),
		);

		return implode( '_', array_filter( $segments ) );
	}

	/**
	 * Load cache index from persistent storage.
	 *
	 * @return array<string, array<string, int>>
	 */
	protected static function load_index(): array {
		$index = get_option( self::INDEX_OPTION, array() );

		return is_array( $index ) ? $index : array();
	}

	/**
	 * Persist cache index.
	 *
	 * @param array<string, array<string, int>> $index Index data.
	 * @return void
	 */
	protected static function save_index( array $index ): void {
		update_option( self::INDEX_OPTION, $index, false );
	}

	/**
	 * Convert a value to a slug-safe segment.
	 *
	 * @param string $value Raw string.
	 * @return string
	 */
	protected static function slugify( string $value ): string {
		$value = strtolower( (string) $value );
		$value = preg_replace( '/[^a-z0-9]+/', '-', $value );

		return trim( (string) $value, '-' );
	}
}
