<?php
/**
 * Tests for the Cron class
 *
 * @package DragonCronManager
 */

use DragonCronManager\Cron;

/**
 * Test case for Cron class
 */
class Test_Cron extends WP_UnitTestCase {

	/**
	 * Cron instance
	 *
	 * @var Cron
	 */
	private Cron $cron;

	/**
	 * Set up test fixtures
	 */
	public function set_up(): void {
		parent::set_up();
		$this->cron = new Cron();

		// Clear any existing trash
		delete_option( 'dcm_trashed_crons' );
	}

	/**
	 * Tear down test fixtures
	 */
	public function tear_down(): void {
		// Clean up trash option
		delete_option( 'dcm_trashed_crons' );

		// Unschedule any test crons
		wp_clear_scheduled_hook( 'dcm_test_hook' );

		parent::tear_down();
	}

	/**
	 * Test 1: is_core_hook identifies known WordPress core hooks
	 */
	public function test_is_core_hook_identifies_known_core_hooks(): void {
		$core_hooks = [
			'wp_scheduled_delete',
			'wp_update_plugins',
			'wp_update_themes',
			'wp_version_check',
			'delete_expired_transients',
		];

		foreach ( $core_hooks as $hook ) {
			$this->assertTrue(
				$this->cron->is_core_hook( $hook ),
				"Hook '$hook' should be identified as core"
			);
		}
	}

	/**
	 * Test 2: is_core_hook identifies wp_ prefixed hooks as core
	 */
	public function test_is_core_hook_identifies_wp_prefix_as_core(): void {
		$this->assertTrue( $this->cron->is_core_hook( 'wp_something_custom' ) );
		$this->assertTrue( $this->cron->is_core_hook( 'wp_cron_test' ) );
	}

	/**
	 * Test 3: is_core_hook returns false for plugin hooks
	 */
	public function test_is_core_hook_returns_false_for_plugin_hooks(): void {
		$plugin_hooks = [
			'dcm_cleanup_logs',
			'my_custom_hook',
			'woocommerce_scheduled_sales',
			'akismet_schedule_cron_recheck',
		];

		foreach ( $plugin_hooks as $hook ) {
			$this->assertFalse(
				$this->cron->is_core_hook( $hook ),
				"Hook '$hook' should NOT be identified as core"
			);
		}
	}

	/**
	 * Test 4: get_trash_count returns zero when trash is empty
	 */
	public function test_get_trash_count_returns_zero_when_empty(): void {
		$this->assertSame( 0, $this->cron->get_trash_count() );
	}

	/**
	 * Test 5: get_trashed_events returns empty array when no trash
	 */
	public function test_get_trashed_events_returns_empty_array(): void {
		$trashed = $this->cron->get_trashed_events();

		$this->assertIsArray( $trashed );
		$this->assertEmpty( $trashed );
	}

	/**
	 * Test 6: trash_event moves cron to trash and increments count
	 */
	public function test_trash_event_stores_in_trash(): void {
		// Schedule a test cron event
		$timestamp = time() + 3600;
		wp_schedule_single_event( $timestamp, 'dcm_test_hook', [ 'test_arg' ] );

		// Get the event key
		$crons = _get_cron_array();
		$key   = array_key_first( $crons[ $timestamp ]['dcm_test_hook'] );

		// Trash the event
		$result = $this->cron->trash_event( 'dcm_test_hook', $key, $timestamp );

		$this->assertTrue( $result );
		$this->assertSame( 1, $this->cron->get_trash_count() );

		// Verify the cron was unscheduled
		$this->assertFalse( wp_next_scheduled( 'dcm_test_hook', [ 'test_arg' ] ) );
	}

	/**
	 * Test 7: restore_event brings cron back from trash
	 */
	public function test_restore_event_reschedules_cron(): void {
		// Schedule and then trash a cron event
		$timestamp = time() + 3600;
		wp_schedule_event( $timestamp, 'hourly', 'dcm_test_hook', [] );

		$crons = _get_cron_array();
		$key   = array_key_first( $crons[ $timestamp ]['dcm_test_hook'] );

		$this->cron->trash_event( 'dcm_test_hook', $key, $timestamp );
		$this->assertSame( 1, $this->cron->get_trash_count() );

		// Get the trash ID
		$trashed  = $this->cron->get_trashed_events();
		$trash_id = $trashed[0]['trash_id'];

		// Restore the event
		$result = $this->cron->restore_event( $trash_id );

		$this->assertTrue( $result['success'] );
		$this->assertSame( 0, $this->cron->get_trash_count() );

		// Verify the cron was rescheduled
		$next = wp_next_scheduled( 'dcm_test_hook', [] );
		$this->assertNotFalse( $next );
	}

	/**
	 * Test 8: empty_trash clears all trashed events
	 */
	public function test_empty_trash_clears_all(): void {
		// Add some items to trash manually
		$trashed = [
			'trash_1' => [
				'hook'       => 'test_hook_1',
				'args'       => [],
				'schedule'   => 'hourly',
				'interval'   => 3600,
				'trashed_at' => time(),
				'expires_at' => time() + ( 30 * DAY_IN_SECONDS ),
			],
			'trash_2' => [
				'hook'       => 'test_hook_2',
				'args'       => [],
				'schedule'   => 'daily',
				'interval'   => 86400,
				'trashed_at' => time(),
				'expires_at' => time() + ( 30 * DAY_IN_SECONDS ),
			],
		];
		update_option( 'dcm_trashed_crons', $trashed );

		$this->assertSame( 2, $this->cron->get_trash_count() );

		// Empty trash
		$count = $this->cron->empty_trash();

		$this->assertSame( 2, $count );
		$this->assertSame( 0, $this->cron->get_trash_count() );
	}

	/**
	 * Test 9: get_events returns array with expected structure
	 */
	public function test_get_events_returns_expected_structure(): void {
		// Schedule a test event
		$timestamp = time() + 3600;
		wp_schedule_event( $timestamp, 'hourly', 'dcm_test_hook', [ 'arg1' ] );

		$events = $this->cron->get_events();

		$this->assertIsArray( $events );
		$this->assertNotEmpty( $events );

		// Find our test event
		$test_event = null;
		foreach ( $events as $event ) {
			if ( 'dcm_test_hook' === $event['hook'] ) {
				$test_event = $event;
				break;
			}
		}

		$this->assertNotNull( $test_event );
		$this->assertArrayHasKey( 'timestamp', $test_event );
		$this->assertArrayHasKey( 'hook', $test_event );
		$this->assertArrayHasKey( 'schedule', $test_event );
		$this->assertArrayHasKey( 'interval', $test_event );
		$this->assertArrayHasKey( 'args', $test_event );
		$this->assertArrayHasKey( 'is_core', $test_event );
		$this->assertArrayHasKey( 'is_recurring', $test_event );
		$this->assertArrayHasKey( 'is_overdue', $test_event );

		$this->assertSame( 'dcm_test_hook', $test_event['hook'] );
		$this->assertSame( 'hourly', $test_event['schedule'] );
		$this->assertTrue( $test_event['is_recurring'] );
		$this->assertFalse( $test_event['is_core'] );
	}

	/**
	 * Test 10: get_summary returns correct counts
	 */
	public function test_get_summary_returns_correct_counts(): void {
		// Clear existing crons first
		$initial_summary = $this->cron->get_summary();

		// Schedule test events
		wp_schedule_event( time() + 3600, 'hourly', 'dcm_test_hook', [] );
		wp_schedule_single_event( time() + 7200, 'dcm_test_single', [] );

		$summary = $this->cron->get_summary();

		$this->assertArrayHasKey( 'total', $summary );
		$this->assertArrayHasKey( 'recurring', $summary );
		$this->assertArrayHasKey( 'single', $summary );
		$this->assertArrayHasKey( 'overdue', $summary );
		$this->assertArrayHasKey( 'core', $summary );
		$this->assertArrayHasKey( 'plugins', $summary );

		// We added 2 events (1 recurring, 1 single)
		$this->assertGreaterThanOrEqual( $initial_summary['total'] + 2, $summary['total'] );

		// Clean up
		wp_clear_scheduled_hook( 'dcm_test_single' );
	}
}
