<?php
/**
 * Custom Fields location resolver.
 *
 * @package CustomFields
 * @since 1.0.0
 */

namespace CustomFields;

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

/**
 * Resolves which enabled field groups apply to a given object.
 */
final class Locations {

	private Storage $storage;

	public function __construct( Storage $storage ) {
		$this->storage = $storage;
	}

	/**
	 * Enabled groups for a post edit screen.
	 *
	 * @param int         $post_id   Post ID (0 = relaxed matching).
	 * @param string|null $post_type Post type when ID is 0.
	 * @return array<string, array<string, mixed>>
	 */
	public function groupsForPost( int $post_id, ?string $post_type = null ): array {
		$context = LocationRules::postContext( $post_id, $post_type );

		return $this->matchingGroups( 'post', $context, $post_id <= 0 );
	}

	/**
	 * @param string $post_type Post type key.
	 * @return array<string, array<string, mixed>>
	 */
	public function groupsForPostType( string $post_type ): array {
		return $this->groupsForPost( 0, $post_type );
	}

	/**
	 * Enabled groups for a taxonomy term screen.
	 *
	 * @param string $taxonomy Taxonomy key.
	 * @param int    $term_id  Term ID (0 = relaxed).
	 * @return array<string, array<string, mixed>>
	 */
	public function groupsForTaxonomy( string $taxonomy, int $term_id = 0 ): array {
		$context = LocationRules::termContext( $taxonomy, $term_id );

		return $this->matchingGroups( 'taxonomy', $context, $term_id <= 0 );
	}

	/**
	 * @param string               $screen  post|taxonomy.
	 * @param array<string, mixed> $context Evaluation context.
	 * @param bool                 $relaxed Skip strict params.
	 * @return array<string, array<string, mixed>>
	 */
	private function matchingGroups( string $screen, array $context, bool $relaxed ): array {
		$out = [];

		foreach ( $this->storage->all() as $key => $group ) {
			if ( ! is_array( $group ) || empty( $group['enabled'] ) ) {
				continue;
			}

			$rules = LocationRules::get( $group );
			if ( ( $rules['screen'] ?? 'post' ) !== $screen ) {
				continue;
			}

			if ( RuleEngine::matches( $rules, $context, $relaxed ) ) {
				$out[ (string) $key ] = $group;
			}
		}

		return $out;
	}

	/**
	 * Distinct post types from location rules.
	 *
	 * @return array<int, string>
	 */
	public function activePostTypes(): array {
		$types = [];
		foreach ( $this->storage->all() as $group ) {
			if ( ! is_array( $group ) || empty( $group['enabled'] ) ) {
				continue;
			}
			$rules = LocationRules::get( $group );
			if ( ( $rules['screen'] ?? 'post' ) !== 'post' ) {
				continue;
			}
			foreach ( LocationTargets::postTypesFromRules( $rules ) as $type ) {
				$types[] = $type;
			}
		}

		return array_values( array_unique( array_filter( $types ) ) );
	}

	/**
	 * Distinct taxonomies from location rules.
	 *
	 * @return array<int, string>
	 */
	public function activeTaxonomies(): array {
		$taxes = [];
		foreach ( $this->storage->all() as $group ) {
			if ( ! is_array( $group ) || empty( $group['enabled'] ) ) {
				continue;
			}
			$rules = LocationRules::get( $group );
			if ( ( $rules['screen'] ?? '' ) !== 'taxonomy' ) {
				continue;
			}
			foreach ( LocationTargets::taxonomiesFromRules( $rules ) as $taxonomy ) {
				$taxes[] = $taxonomy;
			}
		}

		return array_values( array_unique( array_filter( $taxes ) ) );
	}

	/**
	 * Legacy helper — groups with post_type location (enabled).
	 *
	 * @param string $location_type Ignored legacy arg.
	 * @return array<string, array<string, mixed>>
	 */
	public function enabledGroupsForType( string $location_type ): array {
		if ( $location_type === 'taxonomy' ) {
			$out = [];
			foreach ( $this->activeTaxonomies() as $taxonomy ) {
				foreach ( $this->groupsForTaxonomy( $taxonomy, 0 ) as $key => $group ) {
					$out[ $key ] = $group;
				}
			}
			return $out;
		}

		$out = [];
		foreach ( $this->activePostTypes() as $post_type ) {
			foreach ( $this->groupsForPost( 0, $post_type ) as $key => $group ) {
				$out[ $key ] = $group;
			}
		}

		return $out;
	}
}
