<?php

/**
 * SiteContextHandler — Domain handler for structural site introspection.
 *
 * Owns: get_site_render_context
 *
 * Exposes the structural facts Lola needs to design and iterate plugins that
 * fit THE host environment — never by rendering a page (LolaCore has no render
 * engine and is model-agnostic, so visual truth always comes from the user),
 * but by reading admin/DB facts that decide an integration strategy:
 *
 *   - Block theme vs classic theme  → do classic hooks fire, or must we use
 *     render_block / block templates?
 *   - Block editor vs Classic Editor → generate a block or a metabox/shortcode?
 *   - Page builder present            → integration changes radically.
 *   - WooCommerce cart/checkout page  → block-based or shortcode-based?
 *
 * The payload is two-layered on purpose:
 *   1. An agnostic core that is ALWAYS present (serves blogs, editorial sites,
 *      agencies — ICP #2 — as well as stores).
 *   2. A WooCommerce block that is ONLY present when WooCommerce is active
 *      (serves the store-owner ICP #1) — mirroring how
 *      AiClientHelper::buildWooCommerceContext() already omits itself otherwise.
 *
 * Read-only. No confirmation. Reusable beyond the Plugin Builder.
 *
 * @package LolaCore\Actions\Handlers
 * @license GPL-2.0-or-later
 */

declare( strict_types=1 );

namespace LolaCore\Actions\Handlers;

use LolaCore\Actions\Contracts\ActionHandlerInterface;

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

/**
 * Handles structural, render-free introspection of the host site.
 */
class SiteContextHandler implements ActionHandlerInterface {

	/**
	 * Known page builders, keyed by the label we report → list of signals.
	 *
	 * 'plugins' match against active plugin slugs; 'theme' matches against the
	 * active theme template/stylesheet (Divi and Bricks ship as themes).
	 *
	 * @var array<string, array{plugins?: array<string>, theme?: array<string>}>
	 */
	private const PAGE_BUILDERS = array(
		'elementor'      => array( 'plugins' => array( 'elementor', 'elementor-pro' ) ),
		'beaver-builder' => array( 'plugins' => array( 'beaver-builder-lite-version', 'bb-plugin' ) ),
		'wpbakery'       => array( 'plugins' => array( 'js_composer' ) ),
		'brizy'          => array( 'plugins' => array( 'brizy' ) ),
		'oxygen'         => array( 'plugins' => array( 'oxygen' ) ),
		'siteorigin'     => array( 'plugins' => array( 'siteorigin-panels' ) ),
		'seedprod'       => array( 'plugins' => array( 'seedprod', 'coming-soon' ) ),
		'divi'           => array( 'theme' => array( 'divi' ) ),
		'bricks'         => array( 'theme' => array( 'bricks' ) ),
	);

	/** @inheritDoc */
	public function getSupportedActions(): array {
		return array(
			'get_site_render_context',
		);
	}

	/** @inheritDoc */
	public function handle( string $actionId, array $params, bool $dryRun ): array {
		return match ( $actionId ) {
			'get_site_render_context' => $this->getSiteRenderContext( $params ),
			default                   => throw new \Exception( esc_html( "SiteContextHandler: unknown action '{$actionId}'." ) ),
		};
	}

	// --- READ ACTIONS ---

	/**
	 * Returns the structural facts that decide a plugin's integration strategy.
	 *
	 * @param array $params Unused (the tool takes no parameters).
	 * @return array Two-layer context: agnostic core (always) + WooCommerce (if active).
	 */
	private function getSiteRenderContext( array $params ): array {
		$context = array(
			'wordpress' => array(
				'version'      => get_bloginfo( 'version' ),
				'is_multisite' => is_multisite(),
			),
			'theme'  => $this->getThemeContext(),
			'editor' => array(
				'block_editor_active' => $this->isBlockEditorActive(),
				'page_builder'        => $this->detectPageBuilder(),
			),
		);

		$woocommerce = $this->getWooCommerceContext();
		if ( $woocommerce !== array() ) {
			$context['woocommerce'] = $woocommerce;
		}

		return $context;
	}

	// --- AGNOSTIC CORE ---

	/**
	 * Active theme facts. is_block_theme is the single most decisive fact:
	 * it tells Lola whether classic WP/WC hooks fire or whether it must
	 * integrate through blocks / block templates.
	 *
	 * @return array<string, mixed>
	 */
	private function getThemeContext(): array {
		$theme    = wp_get_theme();
		$is_child = $theme->get_stylesheet() !== $theme->get_template();

		return array(
			'name'           => $theme->get( 'Name' ),
			'slug'           => $theme->get_stylesheet(),
			'version'        => $theme->get( 'Version' ),
			'is_block_theme' => function_exists( 'wp_is_block_theme' ) ? wp_is_block_theme() : false,
			'is_child_theme' => $is_child,
			'parent'         => $is_child ? $theme->get_template() : null,
		);
	}

	/**
	 * Whether the block editor is active for posts.
	 *
	 * use_block_editor_for_post_type() respects the Classic Editor plugin and
	 * any third-party filters, so it is more honest than a raw plugin check.
	 * Falls back to true (WP default since 5.0) when the admin helper is absent.
	 */
	private function isBlockEditorActive(): bool {
		if ( ! function_exists( 'use_block_editor_for_post_type' ) ) {
			require_once ABSPATH . 'wp-admin/includes/post.php';
		}

		if ( function_exists( 'use_block_editor_for_post_type' ) ) {
			return use_block_editor_for_post_type( 'post' );
		}

		return true;
	}

	/**
	 * Detects a known page builder from active plugins or the active theme.
	 *
	 * The agnostic equivalent of the cart-block question: a page builder
	 * changes the integration strategy for stores AND content sites alike.
	 *
	 * @return string|null Builder label (e.g. 'elementor'), or null if none.
	 */
	private function detectPageBuilder(): ?string {
		$active_slugs = $this->getActivePluginSlugs();
		$theme        = wp_get_theme();
		$theme_ids    = array(
			strtolower( $theme->get_template() ),
			strtolower( $theme->get_stylesheet() ),
		);

		foreach ( self::PAGE_BUILDERS as $label => $signals ) {
			foreach ( $signals['plugins'] ?? array() as $slug ) {
				if ( in_array( $slug, $active_slugs, true ) ) {
					return $label;
				}
			}
			foreach ( $signals['theme'] ?? array() as $theme_id ) {
				if ( in_array( $theme_id, $theme_ids, true ) ) {
					return $label;
				}
			}
		}

		return null;
	}

	// --- WOOCOMMERCE LAYER (only emitted when WooCommerce is active) ---

	/**
	 * WooCommerce structural facts, or an empty array when WC is inactive.
	 *
	 * @return array<string, mixed>
	 */
	private function getWooCommerceContext(): array {
		if ( ! $this->isWooCommerceActive() ) {
			return array();
		}

		$context = array(
			'active'       => true,
			'version'      => defined( 'WC_VERSION' ) ? WC_VERSION : null,
			'hpos_enabled' => $this->isHposEnabled(),
			'currency'     => function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : '',
			'pages'        => array(
				'cart'     => $this->getPageIntegration( 'woocommerce_cart_page_id', 'woocommerce/cart', 'woocommerce_cart' ),
				'checkout' => $this->getPageIntegration( 'woocommerce_checkout_page_id', 'woocommerce/checkout', 'woocommerce_checkout' ),
			),
		);

		return $context;
	}

	/**
	 * Inspects a WooCommerce page and reports how it renders its core content:
	 * via the modern block, the legacy shortcode, or neither ('unknown').
	 *
	 * This is the fact that diagnosed the Free Shipping Progress Bar bug at
	 * design time: a Cart Block page does not fire woocommerce_before_cart.
	 *
	 * @param string $option_name WC option holding the page ID.
	 * @param string $block_name  Core block name (e.g. 'woocommerce/cart').
	 * @param string $shortcode   Legacy shortcode tag (e.g. 'woocommerce_cart').
	 * @return array{page_id: int, integration: string}
	 */
	private function getPageIntegration( string $option_name, string $block_name, string $shortcode ): array {
		$page_id = (int) get_option( $option_name, 0 );

		$integration = 'unknown';
		if ( $page_id > 0 ) {
			$post = get_post( $page_id );
			if ( $post instanceof \WP_Post ) {
				if ( has_block( $block_name, $post ) ) {
					$integration = 'block';
				} elseif ( has_shortcode( (string) $post->post_content, $shortcode ) ) {
					$integration = 'shortcode';
				}
			}
		}

		return array(
			'page_id'     => $page_id,
			'integration' => $integration,
		);
	}

	// --- HELPERS ---

	private function isWooCommerceActive(): bool {
		if ( ! function_exists( 'is_plugin_active' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		return function_exists( 'is_plugin_active' ) && is_plugin_active( 'woocommerce/woocommerce.php' );
	}

	/**
	 * Whether WooCommerce High-Performance Order Storage (HPOS) is enabled.
	 * Decides whether order code must use the orders table or legacy post-meta.
	 */
	private function isHposEnabled(): bool {
		if ( class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) ) {
			return \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
		}

		return false;
	}

	/**
	 * Active plugin slugs (directory name), mirroring AiClientHelper's logic.
	 *
	 * @return array<string>
	 */
	private function getActivePluginSlugs(): array {
		$slugs  = array();
		$active = (array) get_option( 'active_plugins', array() );

		foreach ( $active as $plugin_file ) {
			$slug = dirname( (string) $plugin_file );
			if ( $slug === '.' || $slug === '' ) {
				$slug = basename( (string) $plugin_file, '.php' );
			}
			$slugs[] = $slug;
		}

		return array_values( array_unique( $slugs ) );
	}
}
