<?php
/**
 * Circuit Breaker Storage Class
 *
 * Persists circuit breaker state using WordPress options.
 *
 * @package Sequensy
 * @since 2.0.0
 */

declare(strict_types=1);

namespace Sequensy\Http;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Class CircuitBreakerStorage
 *
 * Manages persistence of circuit breaker states in WordPress options.
 */
final class CircuitBreakerStorage {

	/**
	 * Option name prefix for circuit breaker states.
	 *
	 * @var string
	 */
	private const OPTION_PREFIX = 'sequensy_circuit_';

	/**
	 * Default state for a new circuit.
	 *
	 * @var array
	 */
	private const DEFAULT_STATE = array(
		'state'             => 'closed',
		'failure_count'     => 0,
		'success_count'     => 0,
		'last_failure_time' => null,
		'opened_at'         => null,
		'last_error'        => null,
	);

	/**
	 * Get circuit state from WordPress options.
	 *
	 * @param string $key Circuit identifier.
	 * @return array Circuit state array.
	 */
	public function getState( string $key ): array {
		$option_name = $this->get_option_name( $key );
		$state       = get_option( $option_name, null );

		if ( null === $state || ! is_array( $state ) ) {
			return self::DEFAULT_STATE;
		}

		// Merge with defaults to ensure all keys exist.
		return array_merge( self::DEFAULT_STATE, $state );
	}

	/**
	 * Save circuit state to WordPress options.
	 *
	 * @param string $key   Circuit identifier.
	 * @param array  $state Circuit state array.
	 * @return bool True on success, false on failure.
	 */
	public function setState( string $key, array $state ): bool {
		$option_name = $this->get_option_name( $key );

		// Ensure state has required fields.
		$state = array_merge( self::DEFAULT_STATE, $state );

		return update_option( $option_name, $state, false );
	}

	/**
	 * Clear circuit state (delete from options).
	 *
	 * @param string $key Circuit identifier.
	 * @return bool True on success, false on failure.
	 */
	public function clearState( string $key ): bool {
		$option_name = $this->get_option_name( $key );
		return delete_option( $option_name );
	}

	/**
	 * Get all circuit breaker states.
	 *
	 * Useful for admin visibility and debugging.
	 *
	 * @return array<string, array> Array of circuit states keyed by circuit identifier.
	 */
	public function getAllCircuits(): array {
		global $wpdb;

		$prefix  = $wpdb->prefix . 'options';
		$pattern = self::OPTION_PREFIX . '%';

		// Query all circuit breaker options.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Bulk read of all circuit states; caching individual options not practical.
		$results = $wpdb->get_results(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from wpdb.
				"SELECT option_name, option_value FROM {$prefix} WHERE option_name LIKE %s",
				$pattern
			),
			ARRAY_A
		);

		$circuits = array();

		foreach ( $results as $row ) {
			// Extract circuit key from option name.
			$key = str_replace( self::OPTION_PREFIX, '', $row['option_name'] );

			// Unserialize the state.
			$state = maybe_unserialize( $row['option_value'] );

			if ( is_array( $state ) ) {
				$circuits[ $key ] = array_merge( self::DEFAULT_STATE, $state );
			}
		}

		return $circuits;
	}

	/**
	 * Clear all circuit breaker states.
	 *
	 * Resets all circuits to closed state.
	 *
	 * @return int Number of circuits cleared.
	 */
	public function clearAllCircuits(): int {
		global $wpdb;

		$prefix  = $wpdb->prefix . 'options';
		$pattern = self::OPTION_PREFIX . '%';

		// Delete all circuit breaker options.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Bulk delete of all circuit states.
		$result = $wpdb->query(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from wpdb.
				"DELETE FROM {$prefix} WHERE option_name LIKE %s",
				$pattern
			)
		);

		return is_int( $result ) ? $result : 0;
	}

	/**
	 * Get the WordPress option name for a circuit.
	 *
	 * @param string $key Circuit identifier.
	 * @return string Option name.
	 */
	private function get_option_name( string $key ): string {
		// Sanitize key to be safe for option names.
		$safe_key = preg_replace( '/[^a-z0-9_]/', '_', strtolower( $key ) );
		return self::OPTION_PREFIX . $safe_key;
	}

	/**
	 * Get the default state array.
	 *
	 * @return array Default circuit state.
	 */
	public static function getDefaultState(): array {
		return self::DEFAULT_STATE;
	}
}
