<?php

namespace ElementPack\Includes\Builder;

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

/**
 * Display conditions for Theme Builder header/footer (Elementor-style rows).
 */
class Template_Conditions {

	/**
	 * @param int $post_id
	 * @return array<int, array<string, mixed>>
	 */
	public static function get_rows( $post_id ) {
		$raw = get_post_meta( $post_id, Meta::TEMPLATE_CONDITIONS, true );
		if ( is_string( $raw ) && $raw !== '' ) {
			$decoded = json_decode( $raw, true );
			if ( is_array( $decoded ) && ! empty( $decoded ) ) {
				return self::normalize_rows( $decoded );
			}
		}

		return [ self::legacy_row( $post_id ) ];
	}

	/**
	 * @param array<int, array<string, mixed>> $rows
	 * @return array<int, array<string, mixed>>
	 */
	public static function normalize_rows( array $rows ) {
		$out = [];
		foreach ( $rows as $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}
			$polarity = isset( $row['polarity'] ) && 'exclude' === $row['polarity'] ? 'exclude' : 'include';
			$out[]      = [
				'polarity'              => $polarity,
				'condition_a'           => isset( $row['condition_a'] ) ? (string) $row['condition_a'] : 'entire_site',
				'condition_singular'    => isset( $row['condition_singular'] ) ? (string) $row['condition_singular'] : 'all',
				'condition_archive'     => isset( $row['condition_archive'] ) ? (string) $row['condition_archive'] : 'all',
				'condition_woo'           => isset( $row['condition_woo'] ) ? (string) $row['condition_woo'] : 'all',
				'condition_singular_id' => isset( $row['condition_singular_id'] ) ? (string) $row['condition_singular_id'] : '',
			];
		}

		return $out ?: [ self::legacy_row( 0 ) ];
	}

	/**
	 * @param int $post_id Post ID or 0 for empty defaults.
	 * @return array<string, mixed>
	 */
	public static function legacy_row( $post_id ) {
		if ( ! $post_id ) {
			return [
				'polarity'              => 'include',
				'condition_a'           => 'entire_site',
				'condition_singular'    => 'all',
				'condition_archive'     => 'all',
				'condition_woo'         => 'all',
				'condition_singular_id' => '',
			];
		}

		$template_type = (string) get_post_meta( $post_id, Meta::TEMPLATE_TYPE, true );
		$defaults      = self::default_row_for_template_type( $template_type );
		if ( ! is_array( $defaults ) ) {
			$defaults = [
				'polarity'              => 'include',
				'condition_a'           => 'entire_site',
				'condition_singular'    => 'all',
				'condition_archive'     => 'all',
				'condition_woo'         => 'all',
				'condition_singular_id' => '',
			];
		}

		return [
			'polarity'              => 'include',
			'condition_a'           => (string) get_post_meta( $post_id, '_bdthemes_builder_template_condition_a', true ) ?: $defaults['condition_a'],
			'condition_singular'    => (string) get_post_meta( $post_id, '_bdthemes_builder_template_condition_singular', true ) ?: $defaults['condition_singular'],
			'condition_archive'     => (string) get_post_meta( $post_id, '_bdthemes_builder_template_condition_archive', true ) ?: $defaults['condition_archive'],
			'condition_woo'         => (string) get_post_meta( $post_id, '_bdthemes_builder_template_condition_woo', true ) ?: $defaults['condition_woo'],
			'condition_singular_id' => (string) get_post_meta( $post_id, '_bdthemes_builder_template_condition_singular_id', true ) ?: $defaults['condition_singular_id'],
		];
	}

	/**
	 * Default row used when a template doesn't have saved display conditions yet.
	 *
	 * @param string $type Template index (e.g. post|single).
	 * @return array<string, mixed>|null
	 */
	public static function default_row_for_template_type( $type ) {
		$type = trim( (string) $type );

		$map = [
			'themes|header'  => [ 'condition_a' => 'entire_site', 'condition_singular' => 'all',          'condition_archive' => 'all',    'condition_woo' => 'all' ],
			'themes|footer'  => [ 'condition_a' => 'entire_site', 'condition_singular' => 'all',          'condition_archive' => 'all',    'condition_woo' => 'all' ],
			'post|single'    => [ 'condition_a' => 'singular',    'condition_singular' => 'all',          'condition_archive' => 'all',    'condition_woo' => 'all' ],
			'page|single'    => [ 'condition_a' => 'singular',    'condition_singular' => 'all_pages',    'condition_archive' => 'all',    'condition_woo' => 'all' ],
			'post|archive'   => [ 'condition_a' => 'archive',     'condition_singular' => 'all',          'condition_archive' => 'all',    'condition_woo' => 'all' ],
			'page|search'    => [ 'condition_a' => 'archive',     'condition_singular' => 'all',          'condition_archive' => 'search', 'condition_woo' => 'all' ],
			'product|single' => [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all',          'condition_archive' => 'all',    'condition_woo' => 'single_product' ],
			'product|shop'   => [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all',          'condition_archive' => 'all',    'condition_woo' => 'product_archive' ],
			'product|archive'=> [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all',          'condition_archive' => 'all',    'condition_woo' => 'product_archive' ],
			// WooCommerce storefront page templates → exact WooCommerce display conditions (not “Entire site”).
			'product|cart'      => [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all', 'condition_archive' => 'all', 'condition_woo' => 'cart' ],
			'product|checkout'  => [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all', 'condition_archive' => 'all', 'condition_woo' => 'checkout' ],
			'product|myaccount' => [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all', 'condition_archive' => 'all', 'condition_woo' => 'myaccount' ],
			'product|orders'    => [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all', 'condition_archive' => 'all', 'condition_woo' => 'orders' ],
			'product|thankyou'  => [ 'condition_a' => 'woocommerce', 'condition_singular' => 'all', 'condition_archive' => 'all', 'condition_woo' => 'thankyou' ],
			'page|404'       => [ 'condition_a' => 'singular',    'condition_singular' => 'not_found404', 'condition_archive' => 'all',    'condition_woo' => 'all' ],
		];

		if ( ! isset( $map[ $type ] ) ) {
			return null;
		}

		return [
			'polarity'              => 'include',
			'condition_a'           => $map[ $type ]['condition_a'],
			'condition_singular'    => $map[ $type ]['condition_singular'],
			'condition_archive'     => $map[ $type ]['condition_archive'],
			'condition_woo'         => $map[ $type ]['condition_woo'],
			'condition_singular_id' => '',
		];
	}

	/**
	 * Persist rows to post meta and mirror first include row into legacy keys for compatibility.
	 *
	 * @param int   $post_id
	 * @param array $rows
	 */
	public static function save_rows( $post_id, array $rows ) {
		$rows = self::normalize_rows( $rows );
		update_post_meta( $post_id, Meta::TEMPLATE_CONDITIONS, wp_json_encode( $rows ) );

		foreach ( $rows as $row ) {
			if ( 'include' === $row['polarity'] ) {
				update_post_meta( $post_id, '_bdthemes_builder_template_condition_a', $row['condition_a'] );
				update_post_meta( $post_id, '_bdthemes_builder_template_condition_singular', $row['condition_singular'] );
				update_post_meta( $post_id, '_bdthemes_builder_template_condition_archive', $row['condition_archive'] );
				update_post_meta( $post_id, '_bdthemes_builder_template_condition_woo', $row['condition_woo'] );
				update_post_meta( $post_id, '_bdthemes_builder_template_condition_singular_id', $row['condition_singular_id'] );
				return;
			}
		}
	}

	/**
	 * Whether this template's conditions match the current front-end request (header/footer/single/archive builder templates).
	 *
	 * @param int $post_id Template post ID.
	 */
	public static function template_applies_to_current_request( $post_id ) {
		$rows         = self::get_rows( $post_id );
		$has_included = false;

		foreach ( $rows as $row ) {
			if ( 'exclude' === $row['polarity'] ) {
				continue;
			}
			if ( self::row_matches_request( $row ) ) {
				$has_included = true;
				break;
			}
		}

		if ( ! $has_included ) {
			return false;
		}

		foreach ( $rows as $row ) {
			if ( 'exclude' !== $row['polarity'] ) {
				continue;
			}
			if ( self::row_matches_request( $row ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * @param array<string, mixed> $row
	 */
	private static function row_matches_request( array $row ) {
		$a = $row['condition_a'] ?: 'entire_site';

		if ( 'entire_site' === $a ) {
			return true;
		}

		if ( 'archive' === $a ) {
			return self::match_archive_row( $row );
		}

		if ( 'singular' === $a ) {
			return self::match_singular_row( $row );
		}

		if ( 'woocommerce' === $a ) {
			return self::match_woo_row( $row );
		}

		return false;
	}

	/**
	 * @param array<string, mixed> $row
	 */
	private static function match_archive_row( array $row ) {
		$sub = isset( $row['condition_archive'] ) ? $row['condition_archive'] : 'all';
		$ids = self::parse_ids( isset( $row['condition_singular_id'] ) ? $row['condition_singular_id'] : '' );

		if ( 'all' === $sub ) {
			if ( ! ( is_archive() || is_home() || is_search() ) ) {
				return false;
			}
			if ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) {
				return false;
			}
			return true;
		}

		if ( 'author' === $sub ) {
			return is_author();
		}

		if ( 'date' === $sub ) {
			return is_date();
		}

		if ( 'search' === $sub ) {
			return is_search();
		}

		if ( 'posts_archive' === $sub ) {
			return is_home() || is_post_type_archive( 'post' );
		}

		if ( 'category' === $sub ) {
			if ( ! is_category() ) {
				return false;
			}
			$qid = get_queried_object_id();
			return ! $ids || in_array( (string) $qid, $ids, true );
		}

		if ( 'category_child' === $sub ) {
			if ( ! is_category() ) {
				return false;
			}
			$term = get_queried_object();
			if ( ! $term instanceof \WP_Term || 'category' !== $term->taxonomy ) {
				return false;
			}
			if ( ! $ids ) {
				return (int) $term->parent > 0;
			}
			return in_array( (string) $term->parent, $ids, true );
		}

		if ( 'category_any_child' === $sub ) {
			if ( ! is_category() || ! $ids ) {
				return false;
			}
			$term = get_queried_object();
			if ( ! $term instanceof \WP_Term || 'category' !== $term->taxonomy ) {
				return false;
			}
			$parent = (int) $term->parent;
			while ( $parent ) {
				if ( in_array( (string) $parent, $ids, true ) ) {
					return true;
				}
				$pterm = get_term( $parent, 'category' );
				if ( ! $pterm || $pterm instanceof \WP_Error ) {
					break;
				}
				$parent = (int) $pterm->parent;
			}
			return false;
		}

		if ( 'tag' === $sub ) {
			if ( ! is_tag() ) {
				return false;
			}
			$qid = get_queried_object_id();
			return ! $ids || in_array( (string) $qid, $ids, true );
		}

		if ( 'tag_child' === $sub || 'tag_any_child' === $sub ) {
			if ( ! is_tag() ) {
				return false;
			}
			$term = get_queried_object();
			if ( ! $term instanceof \WP_Term || 'post_tag' !== $term->taxonomy ) {
				return false;
			}
			if ( 'tag_child' === $sub ) {
				if ( ! $ids ) {
					return (int) $term->parent > 0;
				}
				return in_array( (string) $term->parent, $ids, true );
			}
			if ( ! $ids ) {
				return false;
			}
			$ancestor = $term;
			while ( $ancestor && isset( $ancestor->parent ) && $ancestor->parent ) {
				if ( in_array( (string) $ancestor->parent, $ids, true ) ) {
					return true;
				}
				$ancestor = get_term( $ancestor->parent, 'post_tag' );
				if ( $ancestor instanceof \WP_Error || ! $ancestor ) {
					break;
				}
			}
			return false;
		}

		if ( is_string( $sub ) && strlen( $sub ) > 8 && '_archive' === substr( $sub, -8 ) ) {
			$pt = substr( $sub, 0, -8 );
			if ( $pt && post_type_exists( $pt ) ) {
				return is_post_type_archive( $pt ) || ( 'post' === $pt && is_home() );
			}
		}

		return false;
	}

	/**
	 * @param array<string, mixed> $row
	 */
	private static function match_singular_row( array $row ) {
		$sub = isset( $row['condition_singular'] ) ? $row['condition_singular'] : 'all';
		$ids = self::parse_ids( isset( $row['condition_singular_id'] ) ? $row['condition_singular_id'] : '' );

		if ( 'all' === $sub ) {
			return ( is_singular() && ! is_embed() ) || is_404();
		}

		if ( '404page' === $sub || 'not_found404' === $sub ) {
			return is_404();
		}

		if ( 'front_page' === $sub ) {
			return is_front_page();
		}

		if ( 'all_posts' === $sub ) {
			if ( ! is_singular( 'post' ) ) {
				return false;
			}
			$pid = (string) get_queried_object_id();
			return ! $ids || in_array( $pid, $ids, true );
		}

		if ( 'all_pages' === $sub ) {
			if ( ! is_singular( 'page' ) ) {
				return false;
			}
			$pid = (string) get_queried_object_id();
			return ! $ids || in_array( $pid, $ids, true );
		}

		if ( 'attachment' === $sub ) {
			if ( ! is_attachment() ) {
				return false;
			}
			$pid = (string) get_queried_object_id();
			return ! $ids || in_array( $pid, $ids, true );
		}

		if ( is_string( $sub ) && strlen( $sub ) > 3 && 'pt_' === substr( $sub, 0, 3 ) ) {
			$pt = substr( $sub, 3 );
			if ( $pt && post_type_exists( $pt ) ) {
				if ( ! is_singular( $pt ) ) {
					return false;
				}
				$pid = (string) get_queried_object_id();
				return ! $ids || in_array( $pid, $ids, true );
			}
			return false;
		}

		if ( 'in_category' === $sub ) {
			if ( ! is_singular() || ! $ids ) {
				return false;
			}
			foreach ( $ids as $tid ) {
				if ( has_term( (int) $tid, 'category' ) ) {
					return true;
				}
			}
			return false;
		}

		if ( 'in_category_children' === $sub ) {
			if ( ! is_singular() || ! $ids ) {
				return false;
			}
			foreach ( $ids as $tid ) {
				$tid = (int) $tid;
				if ( ! $tid ) {
					continue;
				}
				$child_terms = get_term_children( $tid, 'category' );
				if ( is_wp_error( $child_terms ) || empty( $child_terms ) ) {
					continue;
				}
				if ( has_term( $child_terms, 'category' ) ) {
					return true;
				}
			}
			return false;
		}

		if ( 'in_post_tag' === $sub ) {
			if ( ! is_singular() || ! $ids ) {
				return false;
			}
			foreach ( $ids as $tid ) {
				if ( has_term( (int) $tid, 'post_tag' ) ) {
					return true;
				}
			}
			return false;
		}

		if ( 'post_by_author' === $sub ) {
			if ( ! is_singular( 'post' ) || ! $ids ) {
				return false;
			}
			$author = (string) get_post_field( 'post_author', get_queried_object_id() );
			return in_array( $author, $ids, true );
		}

		if ( 'page_by_author' === $sub ) {
			if ( ! is_singular( 'page' ) || ! $ids ) {
				return false;
			}
			$author = (string) get_post_field( 'post_author', get_queried_object_id() );
			return in_array( $author, $ids, true );
		}

		if ( 'attachment_by_author' === $sub ) {
			if ( ! is_attachment() || ! $ids ) {
				return false;
			}
			$author = (string) get_post_field( 'post_author', get_queried_object_id() );
			return in_array( $author, $ids, true );
		}

		if ( 'child_of' === $sub ) {
			if ( ! is_singular() ) {
				return false;
			}
			$pid       = get_queried_object_id();
			$parent_id = wp_get_post_parent_id( $pid );
			if ( ! $ids ) {
				return $parent_id > 0;
			}
			foreach ( $ids as $id ) {
				$id = (int) $id;
				if ( ! $id && $parent_id > 0 ) {
					return true;
				}
				if ( $parent_id === $id ) {
					return true;
				}
			}
			return false;
		}

		if ( 'any_child_of' === $sub ) {
			if ( ! is_singular() || ! $ids ) {
				return false;
			}
			$pid = get_queried_object_id();
			foreach ( $ids as $aid ) {
				$aid = (int) $aid;
				if ( ! $aid ) {
					continue;
				}
				$anc = $pid;
				while ( $anc ) {
					$p = wp_get_post_parent_id( $anc );
					if ( $p === $aid ) {
						return true;
					}
					$anc = $p;
				}
			}
			return false;
		}

		if ( 'by_author' === $sub ) {
			if ( ! is_singular() || is_attachment() || ! $ids ) {
				return false;
			}
			$author = (string) get_post_field( 'post_author', get_queried_object_id() );
			return in_array( $author, $ids, true );
		}

		if ( 'selective' === $sub ) {
			if ( ! is_singular() || ! $ids ) {
				return false;
			}
			$pid = (string) get_queried_object_id();
			return in_array( $pid, $ids, true );
		}

		return false;
	}

	/**
	 * @param array<string, mixed> $row
	 */
	/**
	 * WooCommerce catalog search (post_type=product), same as Elementor Pro.
	 */
	private static function is_wc_product_search(): bool {
		return is_search() && 'product' === get_query_var( 'post_type' );
	}

	private static function match_woo_row( array $row ) {
		if ( ! class_exists( 'WooCommerce' ) ) {
			return false;
		}

		$sub = isset( $row['condition_woo'] ) ? $row['condition_woo'] : 'all';
		$ids = self::parse_ids( isset( $row['condition_singular_id'] ) ? $row['condition_singular_id'] : '' );

		// Elementor \ElementorPro\Modules\Woocommerce\Conditions\Woocommerce::check().
		if ( 'all' === $sub ) {
			return ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) || self::is_wc_product_search();
		}

		// Shop page (Elementor shop_page); legacy key `shop` kept for older saves.
		if ( 'shop' === $sub || 'shop_page' === $sub ) {
			return function_exists( 'is_shop' ) && is_shop();
		}

		if ( 'single_product' === $sub ) {
			if ( ! is_singular( 'product' ) ) {
				return false;
			}
			$pid = (string) get_queried_object_id();
			return ! $ids || in_array( $pid, $ids, true );
		}

		// Elementor Product_Archive::check() — shop, any product taxonomy archive, or product search.
		if ( 'product_archive' === $sub ) {
			return ( function_exists( 'is_shop' ) && is_shop() )
				|| ( function_exists( 'is_product_taxonomy' ) && is_product_taxonomy() )
				|| self::is_wc_product_search();
		}

		if ( 'product_search' === $sub ) {
			return self::is_wc_product_search();
		}

		if ( 'product_category' === $sub ) {
			if ( ! is_tax( 'product_cat' ) ) {
				return false;
			}
			$qid = get_queried_object_id();
			return ! $ids || in_array( (string) $qid, $ids, true );
		}

		if ( 'product_tag' === $sub ) {
			if ( ! is_tax( 'product_tag' ) ) {
				return false;
			}
			$qid = get_queried_object_id();
			return ! $ids || in_array( (string) $qid, $ids, true );
		}

		if ( is_string( $sub ) && strlen( $sub ) > 8 && 'woo_tax_' === substr( $sub, 0, 8 ) ) {
			$tax = substr( $sub, 8 );
			if ( ! taxonomy_exists( $tax ) ) {
				return false;
			}
			if ( ! is_tax( $tax ) ) {
				return false;
			}
			$qid = get_queried_object_id();
			return ! $ids || in_array( (string) $qid, $ids, true );
		}

		if ( 'cart' === $sub ) {
			return function_exists( 'is_cart' ) && is_cart();
		}

		// Checkout funnel only (exclude order-received “Thank you”; use `thankyou` for that).
		if ( 'checkout' === $sub ) {
			if ( ! function_exists( 'is_checkout' ) || ! is_checkout() ) {
				return false;
			}
			if ( function_exists( 'is_order_received_page' ) && is_order_received_page() ) {
				return false;
			}
			return true;
		}

		if ( 'myaccount' === $sub ) {
			return function_exists( 'is_account_page' ) && is_account_page();
		}

		// My Account › Orders endpoint (slug is configurable via WC account settings; `orders` is the internal key).
		if ( 'orders' === $sub ) {
			return function_exists( 'is_account_page' ) && is_account_page()
				&& function_exists( 'is_wc_endpoint_url' ) && is_wc_endpoint_url( 'orders' );
		}

		if ( 'thankyou' === $sub ) {
			return function_exists( 'is_order_received_page' ) && is_order_received_page();
		}

		return false;
	}

	/**
	 * @return array<int, string>
	 */
	private static function parse_ids( $csv ) {
		if ( ! is_string( $csv ) || '' === $csv ) {
			return [];
		}
		$parts = array_map( 'trim', explode( ',', $csv ) );
		return array_values( array_filter( $parts ) );
	}

	/**
	 * @deprecated Retained for backward compatibility; prefer template_applies_to_current_request().
	 *
	 * @param int   $post_id
	 * @param array $filters List of [ 'key' => string, 'value' => scalar ].
	 */
	public static function matches_context( $post_id, array $filters ) {
		$rows         = self::get_rows( $post_id );
		$has_included = false;

		foreach ( $rows as $row ) {
			if ( 'exclude' === $row['polarity'] ) {
				continue;
			}
			if ( self::row_matches_legacy_filters( $row, $filters ) ) {
				$has_included = true;
				break;
			}
		}

		if ( ! $has_included ) {
			return false;
		}

		foreach ( $rows as $row ) {
			if ( 'exclude' !== $row['polarity'] ) {
				continue;
			}
			if ( self::row_matches_legacy_filters( $row, $filters ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * @param array<string, mixed> $row
	 * @param array                $filters
	 */
	private static function row_matches_legacy_filters( array $row, array $filters ) {
		$dish = [
			'condition_a'           => $row['condition_a'],
			'condition_singular'    => $row['condition_singular'],
			'condition_singular_id' => $row['condition_singular_id'],
		];

		$match_found = true;

		foreach ( $filters as $filter ) {
			if ( 'condition_singular_id' === $filter['key'] ) {
				$ids = array_map( 'trim', explode( ',', $dish['condition_singular_id'] ) );
				if ( ! in_array( (string) $filter['value'], $ids, true ) ) {
					$match_found = false;
				}
			} elseif ( $dish[ $filter['key'] ] != $filter['value'] ) {
				$match_found = false;
			}

			if ( 'condition_a' === $filter['key'] && 'singular' === $dish['condition_a'] && count( $filters ) < 2 ) {
				$match_found = false;
			}
		}

		return $match_found;
	}

	/**
	 * Human-readable summary for Theme Builder cards (similar to Elementor "Instances").
	 *
	 * @param int $post_id
	 */
	public static function get_instances_label( $post_id ) {
		$rows  = self::get_rows( $post_id );
		$parts = [];
		foreach ( $rows as $row ) {
			$parts[] = self::describe_row( $row );
		}

		return implode( ' · ', $parts );
	}

	/**
	 * @param array<string, mixed> $row
	 */
	private static function describe_row( array $row ) {
		$prefix = 'exclude' === $row['polarity'] ? __( 'Exclude:', 'bdthemes-element-pack' ) . ' ' : '';

		if ( 'entire_site' === $row['condition_a'] ) {
			return $prefix . __( 'Entire site', 'bdthemes-element-pack' );
		}

		if ( 'archive' === $row['condition_a'] ) {
			$map = self::archive_suboption_labels();
			$sub = isset( $row['condition_archive'] ) ? $row['condition_archive'] : 'all';
			$lbl = isset( $map[ $sub ] ) ? $map[ $sub ] : ucwords( str_replace( '_', ' ', $sub ) );
			return $prefix . $lbl;
		}

		if ( 'woocommerce' === $row['condition_a'] ) {
			$map = self::woo_suboption_labels();
			$sub = isset( $row['condition_woo'] ) ? $row['condition_woo'] : 'all';
			$lbl = isset( $map[ $sub ] ) ? $map[ $sub ] : ucwords( str_replace( '_', ' ', $sub ) );
			return $prefix . $lbl;
		}

		if ( 'singular' !== $row['condition_a'] ) {
			return $prefix . ucwords( str_replace( '_', ' ', $row['condition_a'] ) );
		}

		$map  = self::singular_suboption_labels();
		$base = isset( $map[ $row['condition_singular'] ] )
			? $map[ $row['condition_singular'] ]
			: ucwords( str_replace( '_', ' ', $row['condition_singular'] ) );

		return $prefix . $base;
	}

	/**
	 * Labels used in summaries (keep in sync with Theme_Builder_Admin option methods).
	 *
	 * @return array<string, string>
	 */
	public static function archive_suboption_labels() {
		$base = [
			'all'                => __( 'All archives', 'bdthemes-element-pack' ),
			'author'             => __( 'Author archive', 'bdthemes-element-pack' ),
			'date'               => __( 'Date archive', 'bdthemes-element-pack' ),
			'search'             => __( 'Search results', 'bdthemes-element-pack' ),
			'posts_archive'      => __( 'Posts archive', 'bdthemes-element-pack' ),
			'category'           => __( 'Categories', 'bdthemes-element-pack' ),
			'category_child'     => __( 'Direct child category of', 'bdthemes-element-pack' ),
			'category_any_child' => __( 'Any child category of', 'bdthemes-element-pack' ),
			'tag'                => __( 'Tags', 'bdthemes-element-pack' ),
			'tag_child'          => __( 'Direct child tag of', 'bdthemes-element-pack' ),
			'tag_any_child'      => __( 'Any child tag of', 'bdthemes-element-pack' ),
		];

		foreach ( self::dynamic_archive_post_types() as $pt => $label ) {
			$base[ $pt . '_archive' ] = sprintf(
				/* translators: %s: post type plural name */
				__( '%s archive', 'bdthemes-element-pack' ),
				$label
			);
		}

		return $base;
	}

	/**
	 * @return array<string, string>
	 */
	public static function woo_suboption_labels() {
		if ( ! class_exists( 'WooCommerce' ) ) {
			return [];
		}

		$product_obj = get_post_type_object( 'product' );
		$product_lbl = ( $product_obj && ! empty( $product_obj->labels->singular_name ) )
			? $product_obj->labels->singular_name
			: __( 'Product', 'bdthemes-element-pack' );

		// Order and strings aligned with Elementor Pro Theme Builder › WooCommerce conditions.
		$base = [
			'all'              => __( 'Entire Shop', 'bdthemes-element-pack' ),
			'product_archive'  => __( 'All Product Archives', 'bdthemes-element-pack' ),
			'shop_page'        => __( 'Shop Page', 'bdthemes-element-pack' ),
			'product_search'   => __( 'Search Results', 'bdthemes-element-pack' ),
			'single_product'   => $product_lbl,
			'product_category' => __( 'Product categories', 'bdthemes-element-pack' ),
			'product_tag'      => __( 'Product tags', 'bdthemes-element-pack' ),
		];

		$taxonomies = get_object_taxonomies( 'product', 'objects' );
		foreach ( $taxonomies as $slug => $tax ) {
			if ( ! $tax instanceof \WP_Taxonomy ) {
				continue;
			}
			if ( in_array( $slug, [ 'product_type', 'product_visibility', 'product_shipping_class' ], true ) ) {
				continue;
			}
			if ( in_array( $slug, [ 'product_cat', 'product_tag' ], true ) ) {
				continue;
			}
			if ( empty( $tax->public ) ) {
				continue;
			}
			$base[ 'woo_tax_' . $slug ] = $tax->label;
		}

		$base['cart']      = __( 'Cart Page', 'bdthemes-element-pack' );
		$base['checkout']  = __( 'Checkout Page', 'bdthemes-element-pack' );
		$base['myaccount'] = __( 'My Account Page', 'bdthemes-element-pack' );
		$base['orders']    = __( 'Orders Page', 'bdthemes-element-pack' );
		$base['thankyou']  = __( 'Thank You Page', 'bdthemes-element-pack' );

		return $base;
	}

	/**
	 * @return array<string, string> slug => label
	 */
	public static function dynamic_archive_post_types() {
		$types = get_post_types(
			[
				'public'      => true,
				'has_archive' => true,
			],
			'objects'
		);

		$out = [];
		foreach ( $types as $slug => $obj ) {
			if ( ! $obj instanceof \WP_Post_Type ) {
				continue;
			}
			// Product archives are under Theme Builder › WooCommerce in Elementor, not general Archives.
			if ( class_exists( 'WooCommerce' ) && 'product' === $slug ) {
				continue;
			}
			$link = get_post_type_archive_link( $slug );
			if ( ! $link && 'post' !== $slug ) {
				continue;
			}
			$out[ $slug ] = $obj->labels->name;
		}

		return $out;
	}

	/**
	 * Static singular labels (pt_* merged at runtime in admin).
	 *
	 * @return array<string, string>
	 */
	public static function singular_suboption_labels() {
		// Order matches Elementor Site Editor › Singular (see elementor-pro theme-builder conditions).
		return [
			'all'                  => __( 'All singular', 'bdthemes-element-pack' ),
			'front_page'           => __( 'Front page', 'bdthemes-element-pack' ),
			'all_posts'            => __( 'Posts', 'bdthemes-element-pack' ),
			'in_category'          => __( 'In Category', 'bdthemes-element-pack' ),
			'in_category_children' => __( 'In child Categories', 'bdthemes-element-pack' ),
			'in_post_tag'          => __( 'In Tag', 'bdthemes-element-pack' ),
			'post_by_author'       => __( 'Posts by author', 'bdthemes-element-pack' ),
			'all_pages'            => __( 'Pages', 'bdthemes-element-pack' ),
			'page_by_author'       => __( 'Pages by author', 'bdthemes-element-pack' ),
			'attachment'           => __( 'Media', 'bdthemes-element-pack' ),
			'attachment_by_author' => __( 'Media by author', 'bdthemes-element-pack' ),
			'child_of'             => __( 'Direct child of', 'bdthemes-element-pack' ),
			'any_child_of'         => __( 'Any child of', 'bdthemes-element-pack' ),
			'by_author'            => __( 'By author', 'bdthemes-element-pack' ),
			'selective'            => __( 'Selective singular', 'bdthemes-element-pack' ),
			'404page'              => __( '404 Page', 'bdthemes-element-pack' ),
			'not_found404'         => __( '404 Page', 'bdthemes-element-pack' ),
		];
	}

	/**
	 * Grouped Singular sub-options for the dropdown (Elementor Site Editor style).
	 * Returned as an ordered array of items / { group, options } blocks.
	 *
	 * @param array<string, string> $cpt_extra Additional pt_* entries from Theme_Builder_Admin.
	 * @return array<int, array<string, mixed>>
	 */
	public static function singular_grouped_options( array $cpt_extra = [] ) {
		$labels = self::singular_suboption_labels();
		$out    = [
			[ 'value' => 'all',          'label' => $labels['all'] ],
			[ 'value' => 'front_page',   'label' => $labels['front_page'] ],
			[ 'value' => 'not_found404', 'label' => $labels['not_found404'] ],
			[
				'group'   => __( 'Posts', 'bdthemes-element-pack' ),
				'options' => [
					[ 'value' => 'all_posts',            'label' => $labels['all_posts'] ],
					[ 'value' => 'in_category',          'label' => $labels['in_category'] ],
					[ 'value' => 'in_category_children', 'label' => $labels['in_category_children'] ],
					[ 'value' => 'in_post_tag',          'label' => $labels['in_post_tag'] ],
					[ 'value' => 'post_by_author',       'label' => $labels['post_by_author'] ],
				],
			],
			[
				'group'   => __( 'Pages', 'bdthemes-element-pack' ),
				'options' => [
					[ 'value' => 'all_pages',      'label' => $labels['all_pages'] ],
					[ 'value' => 'page_by_author', 'label' => $labels['page_by_author'] ],
					[ 'value' => 'child_of',       'label' => $labels['child_of'] ],
					[ 'value' => 'any_child_of',   'label' => $labels['any_child_of'] ],
				],
			],
			[
				'group'   => __( 'Media', 'bdthemes-element-pack' ),
				'options' => [
					[ 'value' => 'attachment',           'label' => $labels['attachment'] ],
					[ 'value' => 'attachment_by_author', 'label' => $labels['attachment_by_author'] ],
				],
			],
		];

		if ( ! empty( $cpt_extra ) ) {
			$cpt_options = [];
			foreach ( $cpt_extra as $value => $label ) {
				$cpt_options[] = [ 'value' => $value, 'label' => $label ];
			}
			$out[] = [
				'group'   => __( 'Custom Post Types', 'bdthemes-element-pack' ),
				'options' => $cpt_options,
			];
		}

		$out[] = [
			'group'   => __( 'Other', 'bdthemes-element-pack' ),
			'options' => [
				[ 'value' => 'by_author', 'label' => $labels['by_author'] ],
				[ 'value' => 'selective', 'label' => $labels['selective'] ],
			],
		];

		return $out;
	}

	/**
	 * Grouped Archive sub-options (Elementor-style).
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function archive_grouped_options() {
		$labels = self::archive_suboption_labels();

		// Layout aligned with Elementor Pro Theme Builder › Archive conditions:
		//   All archives
		//   Author archive
		//   Date archive
		//   Search results
		//   ── Posts archive ─────────────
		//     Posts archive
		//     Categories
		//     Direct child Category of
		//     Any child Category of
		//     Tags
		$out = [
			[ 'value' => 'all',    'label' => $labels['all'] ],
			[ 'value' => 'author', 'label' => $labels['author'] ],
			[ 'value' => 'date',   'label' => $labels['date'] ],
			[ 'value' => 'search', 'label' => $labels['search'] ],
			[
				'group'   => $labels['posts_archive'],
				'options' => [
					[ 'value' => 'posts_archive',      'label' => $labels['posts_archive'] ],
					[ 'value' => 'category',           'label' => $labels['category'] ],
					[ 'value' => 'category_child',     'label' => $labels['category_child'] ],
					[ 'value' => 'category_any_child', 'label' => $labels['category_any_child'] ],
					[ 'value' => 'tag',                'label' => $labels['tag'] ],
					[ 'value' => 'tag_child',          'label' => $labels['tag_child'] ],
					[ 'value' => 'tag_any_child',      'label' => $labels['tag_any_child'] ],
				],
			],
		];

		$cpts = self::dynamic_archive_post_types();
		if ( ! empty( $cpts ) ) {
			$cpt_options = [];
			foreach ( $cpts as $pt => $label ) {
				$cpt_options[] = [
					'value' => $pt . '_archive',
					'label' => isset( $labels[ $pt . '_archive' ] ) ? $labels[ $pt . '_archive' ] : $label,
				];
			}
			$out[] = [
				'group'   => __( 'Post type archives', 'bdthemes-element-pack' ),
				'options' => $cpt_options,
			];
		}

		return $out;
	}

	/**
	 * Grouped WooCommerce sub-options (Elementor-style).
	 *
	 * @return array<int, array<string, mixed>>
	 */
	public static function woo_grouped_options() {
		if ( ! class_exists( 'WooCommerce' ) ) {
			return [];
		}

		$labels = self::woo_suboption_labels();
		$out    = [
			[ 'value' => 'all',             'label' => $labels['all'] ],
			[ 'value' => 'product_archive', 'label' => $labels['product_archive'] ],
			[ 'value' => 'shop_page',       'label' => $labels['shop_page'] ],
			[ 'value' => 'product_search',  'label' => $labels['product_search'] ],
			[ 'value' => 'single_product',  'label' => $labels['single_product'] ],
		];

		$tax_options = [];
		if ( isset( $labels['product_category'] ) ) {
			$tax_options[] = [ 'value' => 'product_category', 'label' => $labels['product_category'] ];
		}
		if ( isset( $labels['product_tag'] ) ) {
			$tax_options[] = [ 'value' => 'product_tag', 'label' => $labels['product_tag'] ];
		}
		foreach ( $labels as $key => $label ) {
			if ( strpos( (string) $key, 'woo_tax_' ) === 0 ) {
				$tax_options[] = [ 'value' => $key, 'label' => $label ];
			}
		}
		if ( ! empty( $tax_options ) ) {
			$out[] = [
				'group'   => __( 'Taxonomies', 'bdthemes-element-pack' ),
				'options' => $tax_options,
			];
		}

		$out[] = [
			'group'   => __( 'Pages', 'bdthemes-element-pack' ),
			'options' => [
				[ 'value' => 'cart',      'label' => $labels['cart'] ],
				[ 'value' => 'checkout',  'label' => $labels['checkout'] ],
				[ 'value' => 'myaccount', 'label' => $labels['myaccount'] ],
				[ 'value' => 'orders',    'label' => $labels['orders'] ],
				[ 'value' => 'thankyou',  'label' => $labels['thankyou'] ],
			],
		];

		return $out;
	}

	/**
	 * Build WP_Query vars for Theme Builder archive/search preview (editor + current query widgets).
	 *
	 * Mirrors Elementor Pro Theme Document preview query switching.
	 *
	 * @param int $post_id bdt-template-builder post ID.
	 * @return array<string, mixed>
	 */
	public static function get_preview_query_args( $post_id ) {
		$post_id = absint( $post_id );
		if ( $post_id < 1 ) {
			return [ 'post_type' => 'post' ];
		}

		$template_type = (string) get_post_meta( $post_id, Meta::TEMPLATE_TYPE, true );
		$sep           = Builder_Template_Helper::separator();
		$parts         = explode( $sep, $template_type, 2 );
		$slug          = isset( $parts[1] ) ? $parts[1] : '';

		if ( 'page' . $sep . 'search' === $template_type || 'search' === $slug ) {
			return [
				's' => 'elementor',
			];
		}

		if ( class_exists( 'WooCommerce' ) && ( 'product' . $sep . 'shop' === $template_type || in_array( $slug, [ 'shop', 'archive', 'category', 'tag' ], true ) ) ) {
			return self::get_woo_preview_query_args( $post_id, $slug );
		}

		foreach ( self::get_rows( $post_id ) as $row ) {
			if ( 'exclude' === $row['polarity'] ) {
				continue;
			}
			if ( 'archive' === $row['condition_a'] ) {
				$args = self::query_args_from_archive_condition( $row );
				if ( ! empty( $args ) ) {
					return $args;
				}
			}
		}

		if ( 'archive' === $slug ) {
			return [ 'post_type' => 'post' ];
		}

		return self::query_args_from_archive_slug( $slug );
	}

	/**
	 * @param array<string, mixed> $row Condition row.
	 * @return array<string, mixed>
	 */
	private static function query_args_from_archive_condition( array $row ) {
		$sub = isset( $row['condition_archive'] ) ? (string) $row['condition_archive'] : 'all';
		$ids = self::parse_ids( isset( $row['condition_singular_id'] ) ? $row['condition_singular_id'] : '' );

		switch ( $sub ) {
			case 'all':
			case 'posts_archive':
				return [ 'post_type' => 'post' ];

			case 'author':
				$author_id = ! empty( $ids ) ? absint( $ids[0] ) : 0;
				if ( ! $author_id ) {
					$users = get_users(
						[
							'number'     => 1,
							'orderby'    => 'post_count',
							'order'      => 'DESC',
							'capability' => 'edit_posts',
						]
					);
					$author_id = ! empty( $users ) ? (int) $users[0]->ID : get_current_user_id();
				}
				return [ 'author' => $author_id ];

			case 'date':
				return [ 'year' => (int) gmdate( 'Y' ) ];

			case 'category':
				$term_id = ! empty( $ids ) ? absint( $ids[0] ) : 0;
				if ( ! $term_id ) {
					$terms = get_terms(
						[
							'taxonomy'   => 'category',
							'hide_empty' => true,
							'number'     => 1,
						]
					);
					if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
						$term_id = (int) $terms[0]->term_id;
					}
				}
				if ( $term_id ) {
					return [
						'tax_query' => [
							[
								'taxonomy' => 'category',
								'field'    => 'term_id',
								'terms'    => [ $term_id ],
							],
						],
					];
				}
				break;

			case 'tag':
				$term_id = ! empty( $ids ) ? absint( $ids[0] ) : 0;
				if ( ! $term_id ) {
					$terms = get_terms(
						[
							'taxonomy'   => 'post_tag',
							'hide_empty' => true,
							'number'     => 1,
						]
					);
					if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
						$term_id = (int) $terms[0]->term_id;
					}
				}
				if ( $term_id ) {
					return [
						'tax_query' => [
							[
								'taxonomy' => 'post_tag',
								'field'    => 'term_id',
								'terms'    => [ $term_id ],
							],
						],
					];
				}
				break;

			default:
				if ( is_string( $sub ) && strlen( $sub ) > 8 && '_archive' === substr( $sub, -8 ) ) {
					$pt = substr( $sub, 0, -8 );
					if ( $pt && post_type_exists( $pt ) ) {
						return [ 'post_type' => $pt ];
					}
				}
				break;
		}

		return [ 'post_type' => 'post' ];
	}

	/**
	 * Legacy template slug preview (post|category, post|tag, …).
	 *
	 * @param string $slug Template slug segment.
	 * @return array<string, mixed>
	 */
	private static function query_args_from_archive_slug( $slug ) {
		switch ( $slug ) {
			case 'category':
				return self::query_args_from_archive_condition(
					[
						'condition_archive'     => 'category',
						'condition_singular_id' => '',
					]
				);
			case 'tag':
				return self::query_args_from_archive_condition(
					[
						'condition_archive'     => 'tag',
						'condition_singular_id' => '',
					]
				);
			case 'author':
				return self::query_args_from_archive_condition(
					[
						'condition_archive'     => 'author',
						'condition_singular_id' => '',
					]
				);
			case 'date':
				return [ 'year' => (int) gmdate( 'Y' ) ];
			case 'archive':
			default:
				return [ 'post_type' => 'post' ];
		}
	}

	/**
	 * @param int    $post_id Template post ID.
	 * @param string $slug    Template slug segment.
	 * @return array<string, mixed>
	 */
	private static function get_woo_preview_query_args( $post_id, $slug ) {
		foreach ( self::get_rows( $post_id ) as $row ) {
			if ( 'exclude' === $row['polarity'] || 'woocommerce' !== $row['condition_a'] ) {
				continue;
			}

			$sub = isset( $row['condition_woo'] ) ? (string) $row['condition_woo'] : 'product_archive';
			$ids = self::parse_ids( isset( $row['condition_singular_id'] ) ? $row['condition_singular_id'] : '' );

			if ( 'product_category' === $sub || ( 'category' === $slug && 'product_archive' !== $sub ) ) {
				$term_id = ! empty( $ids ) ? absint( $ids[0] ) : 0;
				if ( ! $term_id ) {
					$terms = get_terms(
						[
							'taxonomy'   => 'product_cat',
							'hide_empty' => true,
							'number'     => 1,
						]
					);
					if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
						$term_id = (int) $terms[0]->term_id;
					}
				}
				if ( $term_id ) {
					return [
						'post_type' => 'product',
						'tax_query' => [
							[
								'taxonomy' => 'product_cat',
								'field'    => 'term_id',
								'terms'    => [ $term_id ],
							],
						],
					];
				}
			}

			if ( 'product_tag' === $sub || 'tag' === $slug ) {
				$term_id = ! empty( $ids ) ? absint( $ids[0] ) : 0;
				if ( ! $term_id ) {
					$terms = get_terms(
						[
							'taxonomy'   => 'product_tag',
							'hide_empty' => true,
							'number'     => 1,
						]
					);
					if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
						$term_id = (int) $terms[0]->term_id;
					}
				}
				if ( $term_id ) {
					return [
						'post_type' => 'product',
						'tax_query' => [
							[
								'taxonomy' => 'product_tag',
								'field'    => 'term_id',
								'terms'    => [ $term_id ],
							],
						],
					];
				}
			}

			if ( is_string( $sub ) && strlen( $sub ) > 8 && 'woo_tax_' === substr( $sub, 0, 8 ) ) {
				$tax = substr( $sub, 8 );
				if ( taxonomy_exists( $tax ) ) {
					$term_id = ! empty( $ids ) ? absint( $ids[0] ) : 0;
					if ( ! $term_id ) {
						$terms = get_terms(
							[
								'taxonomy'   => $tax,
								'hide_empty' => true,
								'number'     => 1,
							]
						);
						if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
							$term_id = (int) $terms[0]->term_id;
						}
					}
					if ( $term_id ) {
						return [
							'post_type' => 'product',
							'tax_query' => [
								[
									'taxonomy' => $tax,
									'field'    => 'term_id',
									'terms'    => [ $term_id ],
								],
							],
						];
					}
				}
			}
		}

		return [ 'post_type' => 'product' ];
	}
}
