<?php
/**
 * Custom Fields storage.
 *
 * Persists all field-group definitions in a single, non-autoloaded option,
 * keyed by group key.
 *
 * @package CustomFields
 * @since 1.0.0
 */

namespace CustomFields;

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

/**
 * Data access layer for stored field groups.
 */
final class Storage {

	/**
	 * Option holding the keyed map of field-group definitions.
	 */
	public const OPTION = 'wp_powersuite_cf_groups';

	/**
	 * Request-level cache of the decoded option.
	 *
	 * @var array<string, array<string, mixed>>|null
	 */
	private ?array $cache = null;

	/**
	 * Retrieve all stored field groups keyed by group key.
	 *
	 * @return array<string, array<string, mixed>>
	 */
	public function all(): array {
		if ( $this->cache !== null ) {
			return $this->cache;
		}

		$stored = get_option( self::OPTION, [] );
		if ( ! is_array( $stored ) ) {
			$stored = [];
		}

		$clean = [];
		foreach ( $stored as $key => $definition ) {
			if ( ! is_string( $key ) || ! is_array( $definition ) ) {
				continue;
			}
			$clean[ $key ] = $definition;
		}

		$this->cache = $clean;

		return $this->cache;
	}

	/**
	 * Retrieve a single field group by key.
	 *
	 * @param string $key Group key.
	 * @return array<string, mixed>|null
	 */
	public function get( string $key ): ?array {
		$all = $this->all();

		return $all[ $key ] ?? null;
	}

	/**
	 * Whether a field group exists for the given key.
	 *
	 * @param string $key Group key.
	 */
	public function exists( string $key ): bool {
		return $this->get( $key ) !== null;
	}

	/**
	 * Create or update a single field group.
	 *
	 * @param string               $key        Group key.
	 * @param array<string, mixed> $definition Sanitized definition.
	 */
	public function save( string $key, array $definition ): bool {
		if ( $key === '' ) {
			return false;
		}

		$all         = $this->all();
		$all[ $key ] = $definition;

		return $this->persist( $all );
	}

	/**
	 * Delete a single field group.
	 *
	 * @param string $key Group key.
	 */
	public function delete( string $key ): bool {
		$all = $this->all();
		if ( ! isset( $all[ $key ] ) ) {
			return false;
		}

		unset( $all[ $key ] );

		return $this->persist( $all );
	}

	/**
	 * Persist the full map back to storage (non-autoloaded).
	 *
	 * @param array<string, array<string, mixed>> $all Full definition map.
	 */
	private function persist( array $all ): bool {
		$this->cache = $all;
		update_option( self::OPTION, $all, false );

		do_action( 'wpps_cf_groups_updated', $all );

		return true;
	}
}
