<?php
/**
 * Antimanual Builder
 *
 * @package           Antimanual_Builder
 * @author            Spider Themes
 * @copyright         2026 Spider Themes
 * @license           GPL-2.0-or-later
 *
 * @wordpress-plugin
 * Plugin Name:       Antimanual Builder
 * Plugin URI:        https://wordpress.org/plugins/antimanual-builder
 * Description:       An AI-powered visual page builder for WordPress. Build complete websites through conversational AI or traditional drag-and-drop editing.
 * Version:           0.8.0
 * Requires at least: 6.0
 * Requires PHP:      7.4
 * Author:            Spider Themes
 * Author URI:        https://spider-themes.net
 * Text Domain:       antimanual-builder
 * Domain Path:       /languages
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Plugin version constant.
 */
define( 'AMBLD_VERSION', '0.8.0' );

/**
 * Plugin file path constant.
 */
define( 'AMBLD_PLUGIN_FILE', __FILE__ );

/**
 * Plugin directory path constant.
 */
define( 'AMBLD_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );

/**
 * Plugin directory URL constant.
 */
define( 'AMBLD_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

/**
 * Plugin basename constant.
 */
define( 'AMBLD_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

/**
 * Uploads directory name constant.
 */
define( 'AMBLD_UPLOAD_DIR', 'antimanual-builder' );

/**
 * Minimum PHP version required.
 */
define( 'AMBLD_MIN_PHP', '7.4' );

/**
 * Minimum WordPress version required.
 */
define( 'AMBLD_MIN_WP', '6.0' );

/**
 * Check PHP version compatibility before loading the plugin.
 */
if ( version_compare( PHP_VERSION, AMBLD_MIN_PHP, '<' ) ) {
	add_action( 'admin_notices', 'ambld_php_version_notice' );
	return;
}

/**
 * Display admin notice for incompatible PHP version.
 *
 * @return void
 */
function ambld_php_version_notice() {
	?>
	<div class="notice notice-error">
		<p>
			<?php
			printf(
				/* translators: 1: Required PHP version, 2: Current PHP version. */
				esc_html__( 'Antimanual Builder requires PHP %1$s or higher. You are running PHP %2$s. Please upgrade PHP to use this plugin.', 'antimanual-builder' ),
				esc_html( AMBLD_MIN_PHP ),
				esc_html( PHP_VERSION )
			);
			?>
		</p>
	</div>
	<?php
}

/**
 * Autoload plugin classes.
 *
 * Maps class names to file paths using the WordPress convention:
 * Class_Name → class-class-name.php
 *
 * @param string $class_name The fully qualified class name.
 * @return void
 */
function ambld_autoloader( $class_name ) {
	// Only autoload classes with the AMB prefix.
	if ( 0 !== strpos( $class_name, 'AMBLD_' ) ) {
		return;
	}

	// Map class prefixes to directories.
	$prefix_map = array(
		'AMBLD_API_'         => 'includes/api/',
		'AMBLD_Post_Type_'   => 'includes/post-types/',
		'AMBLD_Render_'      => 'includes/render/',
		'AMBLD_Admin_'       => 'includes/admin/',
		'AMBLD_Agent_'       => 'includes/agent/',
		'AMBLD_Rag_'         => 'includes/rag/',
		'AMBLD_'             => 'includes/',
	);

	$file = '';
	foreach ( $prefix_map as $prefix => $dir ) {
		if ( 0 === strpos( $class_name, $prefix ) ) {
			$relative_class = substr( $class_name, strlen( $prefix ) );
			$slug           = strtolower( str_replace( '_', '-', $relative_class ) );
			// Try `class-{slug}.php` first, then `trait-{slug}.php` for
			// names suffixed with "_Trait" (and as a fallback otherwise),
			// then `interface-{slug}.php`. PHP's spl autoload registry
			// fires for traits and interfaces too — the autoloader has
			// to find them or the `use Foo_Trait` line in a class file
			// will fatal before any controller has a chance to bootstrap
			// shared resources.
			$candidates = array(
				'class-' . $slug . '.php',
				'trait-' . $slug . '.php',
				'interface-' . $slug . '.php',
			);

			// If the symbol name is "_Trait"-suffixed, strip the suffix
			// from the slug so `AMBLD_API_Builder_Meta_Trait` looks up
			// `trait-builder-meta.php`/`trait-builder-meta-api.php` etc.
			if ( '-trait' === substr( $slug, -6 ) ) {
				$base = substr( $slug, 0, -6 );
				array_unshift( $candidates, 'trait-' . $base . '.php' );
				// Some files use a `-api` suffix on the slug (the trait
				// file we ship is `trait-builder-meta-api.php`) — try
				// that variant too.
				array_unshift( $candidates, 'trait-' . $base . '-api.php' );
			}

			foreach ( $candidates as $candidate ) {
				$path = AMBLD_PLUGIN_DIR . $dir . $candidate;
				if ( file_exists( $path ) ) {
					$file = $path;
					break;
				}
			}
			break;
		}
	}

	if ( $file && file_exists( $file ) ) {
		require_once $file;
	}
}
spl_autoload_register( 'ambld_autoloader' );

// Load Composer-managed dependencies (masterminds/html5 HTML5 parser and the
// Freemius SDK that powers the licensing integration below).
$ambld_composer_autoload = AMBLD_PLUGIN_DIR . 'vendor/autoload.php';
if ( file_exists( $ambld_composer_autoload ) ) {
	require_once $ambld_composer_autoload;
}

// Shared helper functions (pro-status gate, upgrade URL, etc.). Not covered by
// the class autoloader above, so load it explicitly and early.
require_once AMBLD_PLUGIN_DIR . 'includes/utils/helper-functions.php';

/**
 * Freemius SDK integration.
 *
 * Mirrors the parent Antimanual plugin's setup, but scoped to this plugin with
 * its own `ab_fs()` accessor and product credentials. The SDK itself is
 * loaded via Composer (`freemius/wordpress-sdk`) in the autoload above.
 *
 * SETUP: fill in THIS plugin's own Freemius Plugin ID and Public Key — create
 * an "Antimanual Builder" product in the Freemius dashboard, then copy them
 * from Settings → Keys. Until both are set, Freemius stays dormant and the
 * plugin keeps working normally, so this never breaks the site.
 */
if ( ! defined( 'AMBLD_FS_ID' ) ) {
	define( 'AMBLD_FS_ID', '33873' );         // e.g. '20196'
}
if ( ! defined( 'AMBLD_FS_PUBLIC_KEY' ) ) {
	define( 'AMBLD_FS_PUBLIC_KEY', 'pk_dc42d2a4f2605f26632faae1f0b3a' ); // e.g. 'pk_xxxxxxxxxxxxxxxxxxxxxxxxx'
}

if ( function_exists( 'ab_fs' ) ) {
	// A premium build already booted Freemius — just re-point it at this file.
	ab_fs()->set_basename( false, __FILE__ );
} elseif ( AMBLD_FS_ID && function_exists( 'fs_dynamic_init' ) ) {
	/**
	 * Create (once) and return the shared Freemius instance for the builder.
	 *
	 * @return Freemius
	 */
	function ab_fs() {
		global $ab_fs;

		if ( ! isset( $ab_fs ) ) {
			$ab_fs = fs_dynamic_init( array(
				'id'                  => AMBLD_FS_ID,
				'slug'                => 'antimanual-builder',
				'premium_slug'        => 'antimanual-builder-pro',
				'type'                => 'plugin',
				'public_key'          => AMBLD_FS_PUBLIC_KEY,
				'is_premium'          => false,
				'premium_suffix'      => 'Pro',
				'has_premium_version' => true,
				'has_addons'          => false,
				'has_paid_plans'      => true,
				'is_org_compliant'    => true,
				'parallel_activation' => array(
					'enabled'                  => true,
					'premium_version_basename' => 'antimanual-builder-pro/antimanual-builder.php',
				),
                'trial'               => array(
                    'days'               => 14,
                    'is_require_payment' => true,
                ),
				'menu'                => array(
					'slug'       => 'ambld-dashboard',
					'first-path' => 'admin.php?page=ambld-dashboard',
					'contact'    => false,
					'support'    => false,
				),
			) );
		}

		return $ab_fs;
	}

	// Boot Freemius, mirroring the parent plugin's global tweaks.
	ab_fs()->add_filter( 'deactivate_on_activation', '__return_false' );
	ab_fs()->add_filter( 'hide_freemius_powered_by', '__return_true' );

	do_action( 'ab_fs_loaded' );
}

/**
 * Verify a builder post still has authorization to emit privileged
 * content (custom JS, imported head/footer markup) on the frontend.
 *
 * The post stores the user ID who saved the privileged content while
 * holding the `unfiltered_html` capability. At render time we check
 * whether that user STILL has the capability — if they were demoted,
 * removed, or the cap was revoked at the role level, the content
 * stays inert. This mirrors how WordPress core enforces unfiltered_html
 * across edit/publish events.
 *
 * @since  1.5.0
 * @param  int    $post_id  Post ID.
 * @param  string $meta_key Authorization meta key (e.g. `_ambld_custom_js_authorized_by`).
 * @return bool   True when the saver is still allowed to publish unfiltered HTML.
 */
function ambld_unfiltered_authorized( $post_id, $meta_key ) {
	$user_id = (int) get_post_meta( $post_id, $meta_key, true );
	if ( $user_id <= 0 ) {
		return false;
	}
	return user_can( $user_id, 'unfiltered_html' );
}

/**
 * Run activation hooks.
 *
 * @return void
 */
function ambld_activate() {
	AMBLD_Activator::activate();
}
register_activation_hook( __FILE__, 'ambld_activate' );

/**
 * Run deactivation hooks.
 *
 * @return void
 */
function ambld_deactivate() {
	AMBLD_Deactivator::deactivate();
}
register_deactivation_hook( __FILE__, 'ambld_deactivate' );

/**
 * Initialize the plugin.
 *
 * @return AMBLD_Antimanual_Builder The singleton instance of the plugin.
 */
function ambld_init() {
	// Flag premium-code availability, mirroring the parent plugin's
	// ANTIMANUAL_PRO. Defined only when Freemius reports a usable license AND
	// the Pro build is actually loaded, so the free build can gate Pro-only
	// features with `defined( 'AMBLD_PRO' )` / `! defined( 'AMBLD_PRO' )`.
	// (Rename `AMBLD_Pro` if the Pro plugin's bootstrap class differs.)
	if (
		! defined( 'AMBLD_PRO' )
		&& function_exists( 'ab_fs' )
		&& ab_fs()->can_use_premium_code()
		&& class_exists( 'AMBLD_Pro' )
	) {
		define( 'AMBLD_PRO', true );
	}
	
	return AMBLD_Antimanual_Builder::get_instance();
}
add_action( 'plugins_loaded', 'ambld_init' );

/**
 * One-shot purge of any legacy custom CSS/JS storage from versions of the
 * plugin prior to 2.1.0. Custom code storage was removed to comply with the
 * WordPress.org plugin guidelines on arbitrary code insertion. The purge
 * runs once per site and self-disables via an `ambld_legacy_code_purged`
 * option flag.
 *
 * @since 2.1.0
 * @return void
 */
function ambld_purge_legacy_custom_code() {
	if ( get_option( 'ambld_legacy_code_purged' ) ) {
		return;
	}

	global $wpdb;

	// `_ambld_custom_css` is retained: page-scoped CSS extracted from the
	// AI-generated `<style>` block is sanitized and persisted as page
	// content, not arbitrary author CSS. Only the JS-related legacy
	// storage is purged.
	$meta_keys = array( '_ambld_custom_js', '_ambld_custom_js_authorized_by', '_ambld_imported_markup_authorized_by' );
	foreach ( $meta_keys as $meta_key ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$wpdb->delete( $wpdb->postmeta, array( 'meta_key' => $meta_key ) );
	}

	$upload_dir = wp_upload_dir();
	if ( ! empty( $upload_dir['basedir'] ) ) {
		$src_dir = trailingslashit( $upload_dir['basedir'] ) . AMBLD_UPLOAD_DIR . '/src';
		if ( is_dir( $src_dir ) ) {
			$entries = glob( trailingslashit( $src_dir ) . 'post-*.{css,js}', GLOB_BRACE );
			if ( is_array( $entries ) ) {
				foreach ( $entries as $entry ) {
					wp_delete_file( $entry );
				}
			}
		}
	}

	update_option( 'ambld_legacy_code_purged', 1, false );
}
add_action( 'admin_init', 'ambld_purge_legacy_custom_code' );