<?php
/**
 * Log Purger — automated retention policy for automation log tables.
 *
 * Deletes rows older than the configured threshold from:
 *   - wp_ambh_rule_instances
 *   - wp_ambh_action_instances
 *
 * The threshold is stored in wp_options under 'ambh_automations_log_retention_days'
 * (default 90). Administrators can adjust it on the Advanced settings tab.
 *
 * Scheduling is handled by AutomationsModule::schedule_recurring_actions() which
 * registers a daily Action Scheduler job ('ambh_purge_automation_logs'). This class
 * only registers the handler and executes the actual delete queries.
 *
 * @package AmbassadorHub\Privacy
 */

namespace AmbassadorHub\Privacy;

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

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- retention-policy DELETE on custom tables; caching is irrelevant for DELETE operations.

/**
 * Handles scheduled deletion of old automation log rows.
 */
final class LogPurger {

	/**
	 * Minimum allowed retention period in days.
	 */
	private const MIN_RETENTION_DAYS = 30;

	/**
	 * Default retention period in days.
	 */
	public const DEFAULT_RETENTION_DAYS = 90;

	/**
	 * Option key where the admin-configured threshold is stored.
	 */
	public const OPTION_KEY = 'ambh_automations_log_retention_days';

	/**
	 * Register the Action Scheduler handler for the purge job.
	 *
	 * Called from AutomationsModule::init(). The recurring job itself is
	 * scheduled in AutomationsModule::schedule_recurring_actions().
	 *
	 * @return void
	 */
	public static function init(): void {
		add_action( 'ambh_purge_automation_logs', array( self::class, 'purge' ) );
	}

	/**
	 * Delete log rows that exceed the configured retention threshold.
	 *
	 * Runs once per day via Action Scheduler. The deletion is done in two
	 * separate queries so each table can be indexed independently:
	 * - wp_ambh_rule_instances   — has idx on created_at
	 * - wp_ambh_action_instances — has idx on created_at
	 *
	 * @return void
	 */
	public static function purge(): void {
		global $wpdb;

		$days = max( self::MIN_RETENTION_DAYS, (int) get_option( self::OPTION_KEY, self::DEFAULT_RETENTION_DAYS ) );

		$threshold = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days} days" ) );

		$rule_instances_table   = $wpdb->prefix . 'ambh_rule_instances';
		$action_instances_table = $wpdb->prefix . 'ambh_action_instances';

		// Delete old rule instances (completed or failed — never pending/running).
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- table names built from $wpdb->prefix (safe).
		$wpdb->query(
			$wpdb->prepare(
				"DELETE FROM {$rule_instances_table}
				  WHERE status IN ('completed', 'failed', 'cancelled')
				    AND created_at < %s",
				$threshold
			)
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

		// Delete old action instances — cascade-safe because rule instance FK
		// rows are removed first above, but action instances for still-active
		// rule instances are not deleted (status guard on the outer query).
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- table names built from $wpdb->prefix (safe).
		$wpdb->query(
			$wpdb->prepare(
				"DELETE ai FROM {$action_instances_table} ai
				  LEFT JOIN {$rule_instances_table} ri ON ri.id = ai.rule_instance_id
				 WHERE ai.created_at < %s
				   AND ( ri.id IS NULL OR ri.status IN ('completed', 'failed', 'cancelled') )",
				$threshold
			)
		);
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter

		/**
		 * Fires after the automation log purge has run.
		 *
		 * @since 0.1.0
		 *
		 * @param string $threshold GMT datetime string used as the cut-off.
		 * @param int    $days      Retention period in days.
		 */
		do_action( 'ambh_automation_logs_purged', $threshold, $days );
	}

	/**
	 * Return the configured retention period in days (clamped to minimum).
	 *
	 * @return int
	 */
	public static function get_retention_days(): int {
		return max( self::MIN_RETENTION_DAYS, (int) get_option( self::OPTION_KEY, self::DEFAULT_RETENTION_DAYS ) );
	}
}
