<?php
/**
 * Plugin Name:       Stocktake – Physical Inventory Count for WooCommerce
 * Plugin URI:        https://erenlabs.net/plugins/stocktake/
 * Description:       Physical inventory counting for WooCommerce: scan barcodes, blind-count stock, review variances, and post adjustments in one click.
 * Version:           1.1.26
 * Requires at least: 6.2
 * Requires PHP:      7.4
 * Requires Plugins:  woocommerce
 * Author:            Eren Labs
 * Author URI:        https://erenlabs.net
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       stocktake-for-woocommerce
 * Domain Path:       /languages
 * WC requires at least: 7.0
 * WC tested up to:   11.0
 *
 *
 * @package Stocktake_For_WooCommerce
 */

defined( 'ABSPATH' ) || exit;

// defined()/function_exists() guards throughout this file: the free and Pro
// builds are separate installable artifacts of the SAME plugin. While Freemius
// switches a site from free to Pro, both can briefly be installed together (and
// a customer can install both by hand); the guards keep that window fatal-free
// so the plugins_loaded yield (Bootstrap::should_boot) can run. The yield fires
// at plugins_loaded, which is far too late for FILE-LOAD-time redeclarations —
// hence the guards here rather than a single boot-time check.
if ( ! defined( 'STWC_VERSION' ) ) {
	define( 'STWC_VERSION', '1.1.26' );
	define( 'STWC_FILE', __FILE__ );
	define( 'STWC_PATH', plugin_dir_path( __FILE__ ) );
	define( 'STWC_URL', plugin_dir_url( __FILE__ ) );
	define( 'STWC_BASENAME', plugin_basename( __FILE__ ) );
}


// Freemius SDK — ships in BOTH builds (GPL, wp.org-compliant). It powers
// opt-in, licence checks, automatic updates and the checkout/upgrade UI.
// is_premium is true only in the pro build (which defines PRO_ACTIVE); the
// pro gate itself is bound to the licence inside the premium loader, so a
// licence never flips features in the free build (it has no premium code).
if ( ! function_exists( 'stwc_fs' ) ) {
	function stwc_fs() {
		global $stwc_fs;
		if ( ! isset( $stwc_fs ) ) {
			require_once __DIR__ . '/freemius/start.php';
			$stwc_fs = fs_dynamic_init(
				array(
					'id'                  => '33525',
					'slug'                => 'stocktake-for-woocommerce',
					'type'                => 'plugin',
					'public_key'          => 'pk_88c7a408f615afb012961b02d0feb',
					'is_premium'          => false,
					'premium_suffix'      => 'Pro',
					'has_premium_version' => true,
					'has_addons'          => false,
					'has_paid_plans'      => true,
					'is_org_compliant'    => true,
					'menu'                => array(
						'slug'       => 'stwc-sessions',
						'first-path' => 'admin.php?page=stwc-sessions',
						'account'    => true,
						'contact'    => true,
						'support'    => false,
					),
				)
			);
		}
		return $stwc_fs;
	}
	// Init Freemius.
	stwc_fs();
	// Brand icon for the SDK's own screens (opt-in, pricing header) — bundled
	// so it never depends on an API sync.
	stwc_fs()->add_filter(
		'plugin_icon',
		function () {
			return __DIR__ . '/assets/img/icon-256.png';
		}
	);
	// Signal that the SDK was initiated.
	do_action( 'stwc_fs_loaded' );

	// SDK chrome (menu items, opt-in, licence and contact screens) in the
	// plugin's selected language — catalogue rerouting + gap-filling maps.
	require_once __DIR__ . '/includes/stwc-fs-i18n.php';

	// Menu policy: the SDK's English-only pricing submenu is replaced by our
	// localized Upgrade screen (its checkout route stays reachable for
	// purchases), and the direct-support Contact page is part of the paid
	// offering — free users get the community forum via Help & FAQ.
	stwc_fs()->add_filter(
		'is_submenu_visible',
		function ( $visible, $id ) {
			if ( 'pricing' === $id ) {
				return false;
			}
			if ( 'contact' === $id ) {
				return function_exists( 'stwc_is_pro' ) ? stwc_is_pro() : $visible;
			}
			return $visible;
		},
		10,
		2
	);
}

// Uninstall cleanup. The free build also ships uninstall.php (WordPress.org
// runs it standalone); the pro build ships none — the Freemius deployment
// processor requires uninstall.php be absent — so it cleans up through the
// SDK's after_uninstall hook instead. The routine is idempotent.
require_once __DIR__ . '/includes/uninstall-cleanup.php';
if ( function_exists( 'stwc_fs' ) ) {
	stwc_fs()->add_action( 'after_uninstall', 'stwc_uninstall_cleanup' );
}

// Factory autoloader (shared core under the Stocktake\Vendor prefix + the
// classmap for includes/). Optional at runtime: a dev tree deployed without
// composer still works through the manual requires below.
$stwc_autoload = __DIR__ . '/vendor/autoload.php';
if ( is_readable( $stwc_autoload ) ) {
	require $stwc_autoload;
}


/**
 * Whether Pro features are unlocked. The free build defaults to false; the Pro
 * licensing layer (or the STWC_PRO constant) flips it via this filter.
 *
 * @return bool
 */
if ( ! function_exists( 'stwc_is_pro' ) ) :
function stwc_is_pro() {
	$pro = defined( 'STWC_PRO' ) && STWC_PRO;
	return (bool) apply_filters( 'stwc_is_pro', $pro );
}
endif;

/**
 * Where upgrade buttons and locked features point.
 *
 * @return string
 */
if ( ! function_exists( 'stwc_upgrade_url' ) ) :
function stwc_upgrade_url() {
	$url = 'https://erenlabs.net/stocktake-for-woocommerce/';
	if ( function_exists( 'stwc_fs' ) ) {
		$url = stwc_fs()->get_upgrade_url();
	}
	return (string) apply_filters( 'stwc_upgrade_url', $url );
}
endif;

/**
 * Shipped translation languages (native names), in display order.
 *
 * @return array locale => native label
 */
if ( ! function_exists( 'stwc_locales' ) ) :
function stwc_locales() {
	return (array) apply_filters(
		'stwc_locales',
		array(
			'en_US' => 'English',
			'tr_TR' => 'Türkçe',
			'es_ES' => 'Español',
			'de_DE' => 'Deutsch',
			'fr_FR' => 'Français',
			'it_IT' => 'Italiano',
			'pt_BR' => 'Português (Brasil)',
			'ar'    => 'العربية',
		)
	);
}
endif;

/**
 * Valid values for the switcher: 'auto' (follow site) plus every shipped locale.
 *
 * @return array
 */
if ( ! function_exists( 'stwc_available_locales' ) ) :
function stwc_available_locales() {
	return array_merge( array( 'auto' => __( 'Automatic', 'stocktake-for-woocommerce' ) ), stwc_locales() );
}
endif;

/**
 * The locale actually shown to the current user for this plugin: their explicit
 * choice, or the closest shipped match to the site language.
 *
 * @return string
 */
if ( ! function_exists( 'stwc_effective_locale' ) ) :
function stwc_effective_locale() {
	$pref = stwc_user_locale_pref();
	if ( 'auto' !== $pref ) {
		return $pref;
	}
	$known = array_keys( stwc_locales() );
	// determine_locale(), not get_locale(): WordPress's OWN per-user profile
	// language is what actually renders wp-admin, and get_locale() cannot see it.
	// Reading the site language left an admin whose profile is Arabic reading
	// Arabic strings -- the just-in-time loader resolves them under
	// determine_locale() -- inside a left-to-right layout, and told them the
	// wrong language in the switcher. Measured: site en_US, profile ar,
	// get_locale() en_US and determine_locale() ar. The sibling plugin fixed
	// this in the same function.
	$site  = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
	if ( in_array( $site, $known, true ) ) {
		return $site;
	}
	$lang = substr( $site, 0, 2 );
	foreach ( $known as $k ) {
		if ( substr( $k, 0, 2 ) === $lang ) {
			return $k;
		}
	}
	return 'en_US';
}
endif;


/**
 * Where "I need help" goes: the wp.org forum once the plugin is listed
 * (free), the Freemius contact page once the SDK lands (pro). Filterable.
 *
 * @return string
 */
if ( ! function_exists( 'stwc_support_url' ) ) :
function stwc_support_url() {
	$url = 'https://wordpress.org/support/plugin/stocktake-for-woocommerce/';
	if ( function_exists( 'stwc_fs' ) && stwc_is_pro() ) {
		$url = stwc_fs()->contact_url();
	}
	return (string) apply_filters( 'stwc_support_url', $url );
}
endif;

/**
 * @param string $locale
 * @return bool Whether the locale is right-to-left.
 */
if ( ! function_exists( 'stwc_is_rtl_locale' ) ) :
function stwc_is_rtl_locale( $locale ) {
	return in_array( substr( (string) $locale, 0, 2 ), array( 'ar', 'he', 'fa', 'ur', 'ps' ), true );
}
endif;

/**
 * The current user's language preference for THIS plugin ('auto' = follow site).
 *
 * @return string
 */
if ( ! function_exists( 'stwc_user_locale_pref' ) ) :
function stwc_user_locale_pref() {
	$pref = get_user_meta( get_current_user_id(), 'stwc_locale', true );
	return ( $pref && array_key_exists( $pref, stwc_available_locales() ) ) ? $pref : 'auto';
}
endif;

/**
 * Apply the per-user override to OUR textdomain only — every other plugin and
 * the rest of wp-admin keep the site language.
 */
add_filter(
	'plugin_locale',
	function ( $locale, $domain ) {
		if ( 'stocktake-for-woocommerce' === $domain ) {
			$pref = stwc_user_locale_pref();
			if ( 'auto' !== $pref ) {
				return $pref;
			}
		}
		return $locale;
	},
	10,
	2
);

/**
 * PSR-ish manual includes (kept simple for an MVP — no autoloader yet).
 */
require_once STWC_PATH . 'includes/class-stwc-activator.php';
require_once STWC_PATH . 'includes/class-stwc-sessions.php';
require_once STWC_PATH . 'includes/class-stwc-log.php';
require_once STWC_PATH . 'includes/class-stwc-backups.php';
require_once STWC_PATH . 'includes/class-stwc-stock.php';
require_once STWC_PATH . 'includes/class-stwc-report.php';
require_once STWC_PATH . 'includes/class-stwc-rest.php';
require_once STWC_PATH . 'includes/class-stwc-admin.php';
require_once STWC_PATH . 'includes/class-stwc-review.php';

register_activation_hook( __FILE__, array( 'STWC_Activator', 'activate' ) );

register_deactivation_hook(
	__FILE__,
	function () {
		// Free/Pro co-install: the scheduled hooks are shared names. While a
		// SIBLING install remains on disk they may still belong to it, so this
		// copy leaves them alone; the final uninstall (which runs the same
		// sibling check) clears them. Disk presence — not the active_plugins
		// list — is the signal, so multisite network activation and bulk
		// deactivation cannot confuse the guard.
		require_once __DIR__ . '/includes/uninstall-cleanup.php';
		if ( stwc_sibling_install_exists() ) {
			return;
		}
		if ( function_exists( 'as_unschedule_all_actions' ) ) {
			as_unschedule_all_actions( 'stwc_populate' );
		}
		wp_clear_scheduled_hook( 'stwc_populate' );
		wp_clear_scheduled_hook( 'stwc_reminder_event' );
	}
);


/**
 * Declare HPOS (High-Performance Order Storage) compatibility. When the
 * factory core is present it declares this (plus checkout-blocks) itself in
 * Bootstrap::core(); this manual fallback covers a dev tree without vendor/.
 */
add_action(
	'before_woocommerce_init',
	function () {
		if ( class_exists( '\\ErenLabs\\Stocktake\\Bootstrap' ) ) {
			return; // Core::boot() declares compatibility.
		}
		if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
			\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
		}
	}
);

/**
 * Boot the plugin once all plugins are loaded so we can check for WooCommerce.
 */
add_action(
	'plugins_loaded',
	function () {
		// Factory core wiring: boot it (declares HPOS/blocks compat) and honour
		// the free-yields-to-pro guard. Both degrade gracefully when the tree
		// was deployed without vendor/ (manual dev deploys).
		if ( class_exists( '\\ErenLabs\\Stocktake\\Bootstrap' ) && ! \ErenLabs\Stocktake\Bootstrap::should_boot() ) {
			return;
		}
		// Translations, resolved at init (not earlier): the per-user language
		// override needs the current user, and WP 6.7+ warns on early loads.
		//
		// load_plugin_textdomain() is deliberately NOT used — wp.org pends it
		// outright ("not needed since WordPress 4.6", ledger R1-3), and it could
		// not serve this plugin's two other cases anyway: the just-in-time loader
		// ignores the `plugin_locale` filter, so the per-user switcher would be
		// inert, and it does not read the catalogs bundled under /languages.
		add_action(
			'init',
			function () {
				$domain = 'stocktake-for-woocommerce';
				$pref   = stwc_user_locale_pref();

				if ( 'auto' !== $pref ) {
					// Explicit choice: drop anything already in place and mark the
					// domain non-reloadable, so the just-in-time loader cannot pull
					// the site language back in — exactly what "English" wants.
					unload_textdomain( $domain );
				}

				$locale = ( 'auto' === $pref )
					? apply_filters( 'plugin_locale', determine_locale(), $domain )
					: $pref;

				if ( 'en_US' === $locale ) {
					return; // English IS the source text; there is no catalog.
				}

				// An official WordPress.org pack wins over the bundled catalog.
				foreach ( array( WP_LANG_DIR . '/plugins/', STWC_PATH . 'languages/' ) as $stwc_lang_dir ) {
					$mofile = $stwc_lang_dir . $domain . '-' . $locale . '.mo';
					if ( file_exists( $mofile ) ) {
						load_textdomain( $domain, $mofile, $locale );
						break;
					}
				}
			},
			0
		);

		if ( ! class_exists( 'WooCommerce' ) ) {
			add_action(
				'admin_notices',
				function () {
					echo '<div class="notice notice-error"><p>';
					echo esc_html__( 'Stocktake for WooCommerce requires WooCommerce to be installed and active.', 'stocktake-for-woocommerce' );
					echo '</p></div>';
				}
			);
			return;
		}

		// Apply any pending schema changes (e.g. new count_mode column).
		STWC_Activator::maybe_upgrade();

		// Background snapshot builder for large catalogues (runs via Action
		// Scheduler / WP-Cron, outside the admin request).
		add_action( 'stwc_populate', array( 'STWC_Sessions', 'populate_batch' ), 10, 2 );

		// Wire up the pieces.
		new STWC_Admin();
		STWC_Review::init();
		new STWC_REST();
	}
);
