<?php
/**
 * CPT Builder taxonomy storage.
 *
 * Persists all taxonomy definitions in a single, non-autoloaded option, keyed
 * by taxonomy key. Mirrors {@see Storage} for post types.
 *
 * @package CptBuilder
 * @since 1.1.0
 */

namespace CptBuilder;

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

/**
 * Data access layer for stored taxonomy definitions.
 */
final class TaxonomyStorage {

	/**
	 * Option holding the keyed map of taxonomy definitions.
	 */
	public const OPTION = 'wp_powersuite_cptb_taxonomies';

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

	/**
	 * Retrieve all stored taxonomy definitions keyed by taxonomy 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 taxonomy definition by key.
	 *
	 * @param string $key Taxonomy key.
	 * @return array<string, mixed>|null
	 */
	public function get( string $key ): ?array {
		$all = $this->all();

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

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

	/**
	 * Create or update a single taxonomy definition.
	 *
	 * @param string               $key        Taxonomy 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 taxonomy definition.
	 *
	 * @param string $key Taxonomy 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 );

		return true;
	}
}
