<?php
/**
 * Location rule normalization and helpers.
 *
 * @package CustomFields
 * @since 1.3.0
 */

namespace CustomFields;

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

/**
 * Location rule utilities.
 */
final class LocationRules {

	/**
	 * Get normalized location rules from a group (migrates legacy location).
	 *
	 * @param array<string, mixed> $group Group definition.
	 * @return array{screen: string, match: string, rules: array<int, array<string, string>>}
	 */
	public static function get( array $group ): array {
		if ( isset( $group['location_rules'] ) && is_array( $group['location_rules'] ) ) {
			$rules  = $group['location_rules'];
			$screen = (string) ( $rules['screen'] ?? 'post' );

			return self::normalizeScreen(
				[
					'screen' => in_array( $screen, [ 'post', 'taxonomy' ], true ) ? $screen : 'post',
					'match'  => (string) ( $rules['match'] ?? 'all' ) === 'any' ? 'any' : 'all',
					'rules'  => self::sanitizeRulesList( $rules['rules'] ?? [] ),
				]
			);
		}

		return self::normalizeScreen( self::fromLegacyLocation( $group['location'] ?? [] ) );
	}

	/**
	 * Align screen with rule parameters when saved data is inconsistent.
	 *
	 * Fixes groups saved before the location screen ternary precedence bug.
	 *
	 * @param array{screen: string, match: string, rules: array<int, array<string, string>>} $rules Normalized rules.
	 * @return array{screen: string, match: string, rules: array<int, array<string, string>>}
	 */
	public static function normalizeScreen( array $rules ): array {
		$list = isset( $rules['rules'] ) && is_array( $rules['rules'] ) ? $rules['rules'] : [];
		if ( $list === [] ) {
			return $rules;
		}

		$post_params = [ 'post_type', 'post_id', 'post_status', 'page_template', 'post_format', 'post_term', 'post_taxonomy', 'post_parent' ];
		$tax_params  = [ 'taxonomy', 'term_id' ];
		$params      = array_map(
			static fn( array $rule ): string => (string) ( $rule['param'] ?? '' ),
			$list
		);

		$has_post = array_intersect( $params, $post_params ) !== [];
		$has_tax  = array_intersect( $params, $tax_params ) !== [];

		if ( $has_post && ! $has_tax ) {
			$rules['screen'] = 'post';
		} elseif ( $has_tax && ! $has_post ) {
			$rules['screen'] = 'taxonomy';
		}

		return $rules;
	}

	/**
	 * @param array<string, mixed> $location Legacy location block.
	 * @return array{screen: string, match: string, rules: array<int, array<string, string>>}
	 */
	public static function fromLegacyLocation( array $location ): array {
		$type   = (string) ( $location['type'] ?? 'post_type' );
		$screen = $type === 'taxonomy' ? 'taxonomy' : 'post';
		$rules  = [];

		foreach ( (array) ( $location['values'] ?? [] ) as $value ) {
			$value = (string) $value;
			if ( $value === '' ) {
				continue;
			}
			$rules[] = [
				'param'    => $screen === 'taxonomy' ? 'taxonomy' : 'post_type',
				'operator' => '==',
				'value'    => $value,
			];
		}

		foreach ( (array) ( $location['page_templates'] ?? [] ) as $template ) {
			$template = (string) $template;
			if ( $template === '' ) {
				continue;
			}
			$rules[] = [
				'param'    => 'page_template',
				'operator' => '==',
				'value'    => $template,
			];
		}

		return [
			'screen' => $screen,
			'match'  => 'any',
			'rules'  => $rules,
		];
	}

	/**
	 * @param mixed $raw Raw rules list.
	 * @return array<int, array{param: string, operator: string, value: string}>
	 */
	public static function sanitizeRulesList( mixed $raw ): array {
		if ( ! is_array( $raw ) ) {
			return [];
		}

		$clean = [];
		foreach ( $raw as $rule ) {
			if ( ! is_array( $rule ) ) {
				continue;
			}
			$param = sanitize_key( (string) ( $rule['param'] ?? '' ) );
			if ( ! array_key_exists( $param, Config::locationRuleParams() ) ) {
				continue;
			}
			$operator = (string) ( $rule['operator'] ?? '==' );
			if ( ! array_key_exists( $operator, Config::ruleOperators() ) ) {
				$operator = '==';
			}
			$value = sanitize_text_field( (string) ( $rule['value'] ?? '' ) );
			if ( $value === '' && ! in_array( $operator, [ 'empty', 'not_empty' ], true ) ) {
				continue;
			}
			$clean[] = [
				'param'    => $param,
				'operator' => $operator,
				'value'    => $value,
			];
		}

		return $clean;
	}

	/**
	 * Build post-edit context for rule evaluation.
	 *
	 * @param int         $post_id   Post ID.
	 * @param string|null $post_type Post type override.
	 * @return array<string, mixed>
	 */
	public static function postContext( int $post_id, ?string $post_type = null ): array {
		$post = $post_id > 0 ? get_post( $post_id ) : null;
		if ( $post_type === null || $post_type === '' ) {
			$post_type = $post instanceof \WP_Post ? $post->post_type : '';
		}

		$user      = wp_get_current_user();
		$roles     = $user instanceof \WP_User ? array_values( (array) $user->roles ) : [];
		$template  = $post_id > 0 ? get_page_template_slug( $post_id ) : '';
		$template  = $template !== '' ? $template : 'default';

		$post_format = 'standard';
		if ( $post instanceof \WP_Post && $post_id > 0 ) {
			$format = get_post_format( $post );
			$post_format = $format !== false && $format !== '' ? (string) $format : 'standard';
		}

		$post_terms = [];
		if ( $post instanceof \WP_Post && $post_id > 0 && $post_type !== '' ) {
			foreach ( get_object_taxonomies( $post_type ) as $taxonomy ) {
				$term_ids = wp_get_post_terms( $post_id, $taxonomy, [ 'fields' => 'ids' ] );
				if ( is_array( $term_ids ) ) {
					foreach ( $term_ids as $term_id ) {
						$post_terms[] = (string) $term_id;
					}
				}
			}
		}

		return [
			'post_type'      => (string) $post_type,
			'post_id'        => (string) $post_id,
			'post_status'    => $post instanceof \WP_Post ? (string) $post->post_status : '',
			'page_template'  => (string) $template,
			'post_format'    => $post_format,
			'post_term'      => implode( ',', array_unique( $post_terms ) ),
			'user_id'        => (string) ( $user->ID ?? 0 ),
			'user_role'      => implode( ',', $roles ),
			'post_parent'    => $post instanceof \WP_Post ? (string) $post->post_parent : '0',
		];
	}

	/**
	 * Build term-edit context.
	 *
	 * @param string $taxonomy Taxonomy key.
	 * @param int    $term_id  Term ID.
	 * @return array<string, mixed>
	 */
	public static function termContext( string $taxonomy, int $term_id = 0 ): array {
		$user  = wp_get_current_user();
		$roles = $user instanceof \WP_User ? array_values( (array) $user->roles ) : [];

		return [
			'taxonomy'  => $taxonomy,
			'term_id'   => (string) $term_id,
			'user_id'   => (string) ( $user->ID ?? 0 ),
			'user_role' => implode( ',', $roles ),
		];
	}
}
