<?php

/**
 * Plugin Name: TrackSure Cloud – Server-Side Tracking, Meta CAPI & GA4 for WooCommerce
 * Plugin URI: https://tracksure.cloud
 * Description: Server side tracking & Meta Pixel setup for WooCommerce. Meta CAPI, GA4, Google Ads — no GTM required. First-party analytics, funnels & attribution.
 * Version: 1.2.7
 * Author: TrackSure Team
 * Author URI: https://profiles.wordpress.org/tracksure/
 * License: GPL v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: tracksure
 * Domain Path: /languages
 * Requires at least: 6.0
 * Requires PHP: 7.4
 *
 * @package TrackSure
 */

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

// Define plugin constants.
define( 'TRACKSURE_VERSION', '1.2.7' );
define( 'TRACKSURE_DB_VERSION', '1.0.2' );
define( 'TRACKSURE_PLUGIN_FILE', __FILE__ );
define( 'TRACKSURE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'TRACKSURE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
define( 'TRACKSURE_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

// Core paths (bundled inside free plugin).
define( 'TRACKSURE_CORE_DIR', TRACKSURE_PLUGIN_DIR . 'includes/core/' );
define( 'TRACKSURE_CORE_URL', TRACKSURE_PLUGIN_URL . 'includes/core/' );
define( 'TRACKSURE_FREE_DIR', TRACKSURE_PLUGIN_DIR . 'includes/free/' );
define( 'TRACKSURE_REGISTRY_DIR', TRACKSURE_PLUGIN_DIR . 'registry/' );
define( 'TRACKSURE_ADMIN_DIR', TRACKSURE_PLUGIN_DIR . 'admin/' );

/**
 * Main TrackSure Plugin Class.
 *
 * Bootstrap pattern: Loads Core engine + Free module pack.
 * Fires 'tracksure_loaded' hook for Pro/3rd-party extensions.
 *
 * @since 1.0.0
 */
final class TrackSure {








	/**
	 * Plugin instance.
	 *
	 * @since 1.0.0
	 * @var TrackSure|null
	 */
	private static $instance = null;

	/**
	 * Core engine instance.
	 *
	 * @since 1.0.0
	 * @var TrackSure_Core|null
	 */
	public $core = null;

	/**
	 * Free module instance.
	 *
	 * @since 1.0.0
	 * @var TrackSure_Free|null
	 */
	public $free = null;

	/**
	 * Get plugin instance (Singleton).
	 *
	 * @since 1.0.0
	 * @return TrackSure
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor - Initialize plugin.
	 *
	 * @since 1.0.0
	 */
	private function __construct() {
		add_action( 'plugins_loaded', array( $this, 'init' ), 10 );
	}

	/**
	 * Initialize plugin: Load Core engine + Free module.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function init() {
		// Check if database needs upgrade (runs on plugin update, not just activation).
		$this->maybe_upgrade_db();

		// 1. Load Core engine (bundled inside).
		if ( ! file_exists( TRACKSURE_CORE_DIR . 'class-tracksure-core.php' ) ) {
			add_action(
				'admin_notices',
				static function () {
					printf(
						'<div class="notice notice-error"><p>%s</p></div>',
						esc_html__( 'TrackSure Error: Core engine file not found. Please reinstall the plugin.', 'tracksure' )
					);
				}
			);
			return;
		}

		require_once TRACKSURE_CORE_DIR . 'class-tracksure-core.php';
		$this->core = TrackSure_Core::get_instance();

		// 2. Load Free module pack.
		if ( ! file_exists( TRACKSURE_FREE_DIR . 'class-tracksure-free.php' ) ) {
			add_action(
				'admin_notices',
				static function () {
					printf(
						'<div class="notice notice-error"><p>%s</p></div>',
						esc_html__( 'TrackSure Error: Free module file not found. Please reinstall the plugin.', 'tracksure' )
					);
				}
			);
			return;
		}

		require_once TRACKSURE_FREE_DIR . 'class-tracksure-free.php';
		$this->free = new TrackSure_Free( $this->core );

		// 3. Fire extension hook (Pro/3rd-party can register features).
		do_action( 'tracksure_loaded', $this->core );
	}

	/**
	 * Check and record milestone achievements (visitors, conversions).
	 *
	 * Runs once per admin page load, cached for 1 hour via transient.
	 * Queries total unique visitors and total conversions from DB,
	 * then flags any newly crossed thresholds.
	 *
	 * @since 1.0.5
	 * @return array Milestones data (all milestones with reached_at and seen status).
	 */
	public function check_milestones() {
		// Return cached result if available (1 hour TTL).
		$cached = get_transient( 'tracksure_milestones_cache' );
		if ( false !== $cached ) {
			return $cached;
		}

		global $wpdb;
		$prefix = $wpdb->prefix;

		// Get current milestones from DB option.
		$milestones = get_option( 'tracksure_milestones', array() );
		if ( ! is_array( $milestones ) ) {
			$milestones = array();
		}

		// Define thresholds.
		$visitor_thresholds    = array( 1, 50, 100, 500, 1000, 5000, 10000 );
		$conversion_thresholds = array( 1, 10, 50, 100, 500 );

		// Count total unique visitors (lightweight aggregate query).
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
		$total_visitors = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$prefix}tracksure_visitors"
		);

		// Count total conversions.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
		$total_conversions = (int) $wpdb->get_var(
			"SELECT COUNT(*) FROM {$prefix}tracksure_conversions"
		);

		$now     = gmdate( 'Y-m-d H:i:s' );
		$changed = false;

		// Check visitor milestones.
		foreach ( $visitor_thresholds as $threshold ) {
			$key = 'visitors_' . $threshold;
			if ( ! isset( $milestones[ $key ] ) && $total_visitors >= $threshold ) {
				$milestones[ $key ] = array(
					'type'       => 'visitors',
					'threshold'  => $threshold,
					'reached_at' => $now,
					'seen'       => false,
				);
				$changed            = true;
			}
		}

		// Check conversion milestones.
		foreach ( $conversion_thresholds as $threshold ) {
			$key = 'conversions_' . $threshold;
			if ( ! isset( $milestones[ $key ] ) && $total_conversions >= $threshold ) {
				$milestones[ $key ] = array(
					'type'       => 'conversions',
					'threshold'  => $threshold,
					'reached_at' => $now,
					'seen'       => false,
				);
				$changed            = true;
			}
		}

		// Save if new milestones were reached.
		if ( $changed ) {
			update_option( 'tracksure_milestones', $milestones, false );
		}

		// Cache for 1 hour.
		set_transient( 'tracksure_milestones_cache', $milestones, HOUR_IN_SECONDS );

		return $milestones;
	}

	/**
	 * Check if database schema needs upgrading after a plugin update.
	 *
	 * WordPress only fires register_activation_hook on manual activation,
	 * not on auto-updates. This method runs on every plugins_loaded and
	 * re-runs dbDelta() when TRACKSURE_DB_VERSION changes.
	 *
	 * @since 1.0.4
	 * @return void
	 */
	private function maybe_upgrade_db() {
		$installed_db_version = get_option( 'tracksure_db_version', '0' );

		if ( version_compare( $installed_db_version, TRACKSURE_DB_VERSION, '<' ) ) {
			require_once TRACKSURE_CORE_DIR . 'class-tracksure-db.php';
			require_once TRACKSURE_CORE_DIR . 'class-tracksure-settings-schema.php';
			require_once TRACKSURE_CORE_DIR . 'class-tracksure-installer.php';

			TrackSure_Installer::install();
		}
	}

	/**
	 * Activation callback: Create database tables and schedule cron jobs.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public static function activate() {
		// Load required core files.
		require_once TRACKSURE_CORE_DIR . 'class-tracksure-db.php';
		require_once TRACKSURE_CORE_DIR . 'class-tracksure-settings-schema.php';
		require_once TRACKSURE_CORE_DIR . 'class-tracksure-installer.php';

		// Create database tables.
		TrackSure_Installer::install();

		// Set onboarding flag for first activation (don't overwrite if already dismissed).
		add_option( 'tracksure_onboarding_dismissed', '0', '', 'no' );

		// Set flag to flush permalinks on next admin load.
		// Can't flush here because TrackSure_Core isn't loaded yet.
		update_option( 'tracksure_needs_permalink_flush', '1' );

		// Set flag to redirect to settings page on first admin load.
		set_transient( 'tracksure_activation_redirect', true, 60 );

		// Clear all scheduled cron jobs to prevent duplicates.
		wp_clear_scheduled_hook( 'tracksure_aggregate_hourly' );
		wp_clear_scheduled_hook( 'tracksure_aggregate_daily' );
		wp_clear_scheduled_hook( 'tracksure_delivery_worker' );
		wp_clear_scheduled_hook( 'tracksure_cleanup_data' );
		wp_clear_scheduled_hook( 'tracksure_cleanup_logs' );

		// Clear Action Scheduler delivery tasks (prevents duplicates on reactivation).
		wp_clear_scheduled_hook( 'tracksure_deliver_events' );
		if ( function_exists( 'as_unschedule_all_actions' ) ) {
			as_unschedule_all_actions( 'tracksure_deliver_events', array(), 'tracksure' );
		}

		// Flush rewrite rules.
		flush_rewrite_rules();
	}

	/**
	 * Deactivation callback: Clean up scheduled tasks.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public static function deactivate() {
		// Clear all WP-Cron scheduled hooks.
		wp_clear_scheduled_hook( 'tracksure_aggregate_hourly' );
		wp_clear_scheduled_hook( 'tracksure_aggregate_daily' );
		wp_clear_scheduled_hook( 'tracksure_delivery_worker' );
		wp_clear_scheduled_hook( 'tracksure_cleanup_data' );
		wp_clear_scheduled_hook( 'tracksure_cleanup_logs' );

		// Clean up Action Scheduler tasks (if AS is available).
		// Action Scheduler is NOT part of WordPress core — it comes from
		// WooCommerce, EDD, or standalone plugin. Must check before calling.
		require_once TRACKSURE_CORE_DIR . 'services/class-tracksure-action-scheduler.php';
		TrackSure_Action_Scheduler::unschedule_all();

		// Flush rewrite rules to remove REST API routes.
		flush_rewrite_rules();
	}
}

// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- Standard WordPress plugin pattern: main class + helper functions.

/**
 * Get main plugin instance.
 *
 * @since 1.0.0
 * @return TrackSure
 */
function tracksure() {
	return TrackSure::instance();
}

/**
 * Redirect to TrackSure settings page after plugin activation.
 *
 * Uses a transient flag set during activate() to ensure redirect only happens once.
 * Skips redirect during bulk activations, WP-CLI, and AJAX requests.
 *
 * @since 1.0.0
 * @return void
 */
function tracksure_activation_redirect() {
	// Check if we need to redirect.
	if ( ! get_transient( 'tracksure_activation_redirect' ) ) {
		return;
	}

	// Delete the transient so this only runs once.
	delete_transient( 'tracksure_activation_redirect' );

	// Don't redirect during bulk activation (activating multiple plugins at once).
	// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check, no data processed.
	if ( isset( $_GET['activate-multi'] ) ) {
		return;
	}

	// Don't redirect during AJAX, WP-CLI, or REST API requests.
	if ( wp_doing_ajax() || ( defined( 'WP_CLI' ) && constant( 'WP_CLI' ) ) || defined( 'REST_REQUEST' ) ) {
		return;
	}

	// Only redirect if user has permission to access TrackSure.
	if ( ! current_user_can( 'manage_options' ) ) {
		return;
	}

	// Redirect to TrackSure dashboard (React app overview with onboarding banner).
	wp_safe_redirect( admin_url( 'admin.php?page=tracksure#/overview' ) );
	exit;
}

// Register activation/deactivation hooks BEFORE initializing plugin.
register_activation_hook( TRACKSURE_PLUGIN_FILE, array( 'TrackSure', 'activate' ) );
register_deactivation_hook( TRACKSURE_PLUGIN_FILE, array( 'TrackSure', 'deactivate' ) );

// Redirect to settings page after first activation.
add_action( 'admin_init', 'tracksure_activation_redirect' );

// Initialize plugin.
tracksure();
