<?php
/**
 * TutorHelper Availability diagnostics.
 *
 * @package TutorHelper_Bookings
 */

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

/**
 * Adds read-only Availability information to the system report.
 */
final class TutorHelper_Availability_Diagnostics {

	/**
	 * Register diagnostics hooks.
	 *
	 * @return void
	 */
	public static function init() {
		add_filter(
			'tutorhelper_system_report_data',
			array( __CLASS__, 'add_report_section' )
		);
	}

	/**
	 * Add Availability diagnostics to the system report.
	 *
	 * @param array $data Existing report data.
	 *
	 * @return array
	 */
	public static function add_report_section( $data ) {
		$data['availability_module'] = array(
			'label' => __(
				'Availability module',
				'tutorhelper-bookings'
			),
			'items' => self::get_status_items(),
		);

		return $data;
	}

	/**
	 * Return read-only Availability status information.
	 *
	 * @return array
	 */
	private static function get_status_items() {
		if (
			! class_exists(
				'TutorHelper_Booking_Availability'
			)
		) {
			return array(
				'Module data class' => 'Not loaded',
			);
		}

		$table_name = TutorHelper_Booking_Availability::get_table_name();

		if ( ! self::table_exists( $table_name ) ) {
			return array(
				'Module data class' => 'Loaded',
				'Availability table' => 'Missing',
			);
		}

		$occurrence_status = self::get_occurrence_status();

		return array(
			'Module data class'        => 'Loaded',
			'Availability table'       => 'Present',
			'Total rules'              => self::count_rules(),
			'Active rules'             => self::count_rules( 1 ),
			'Archived rules'           => self::count_rules( 0 ),
			'Open-ended rules'         => self::count_open_ended_rules(),
			'Rules without weekdays'   => self::count_missing_weekdays(),
			'Invalid date ranges'      => self::count_invalid_date_ranges(),
			'Invalid time ranges'      => self::count_invalid_time_ranges(),
			'30-day recurrence check'  => $occurrence_status['status'],
			'Occurrences in 30 days'   => $occurrence_status['count'],
		);
	}

	/**
	 * Check whether a database table exists.
	 *
	 * @param string $table_name Full table name.
	 *
	 * @return bool
	 */
	private static function table_exists( $table_name ) {
		global $wpdb;

		if ( empty( $table_name ) ) {
			return false;
		}

		return $table_name === $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Diagnostic read intentionally bypasses caching so the report reflects current database state.
			$wpdb->prepare(
				'SHOW TABLES LIKE %s',
				$table_name
			)
		);
	}

	/**
	 * Count Availability rules.
	 *
	 * Pass null to count all records.
	 *
	 * @param int|null $is_active Optional active status.
	 *
	 * @return int
	 */
	private static function count_rules( $is_active = null ) {
		global $wpdb;

		$table_name = TutorHelper_Booking_Availability::get_table_name();

		if ( null === $is_active ) {
			return absint(
				$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Diagnostic read intentionally bypasses caching so the report reflects current database state.
					$wpdb->prepare(
						'SELECT COUNT(*)
						FROM %i',
						$table_name
					)
				)
			);
		}

		return absint(
			$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Diagnostic read intentionally bypasses caching so the report reflects current database state.
				$wpdb->prepare(
					'SELECT COUNT(*)
					FROM %i
					WHERE is_active = %d',
					$table_name,
					absint( $is_active )
				)
			)
		);
	}

	/**
	 * Count rules with no fixed end date.
	 *
	 * @return int
	 */
	private static function count_open_ended_rules() {
		global $wpdb;

		$table_name = TutorHelper_Booking_Availability::get_table_name();

		return absint(
			$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Diagnostic read intentionally bypasses caching so the report reflects current database state.
				$wpdb->prepare(
					"SELECT COUNT(*)
					FROM %i
					WHERE end_date IS NULL
					OR end_date = ''",
					$table_name
				)
			)
		);
	}

	/**
	 * Count rules that have no valid weekday list.
	 *
	 * @return int
	 */
	private static function count_missing_weekdays() {
		global $wpdb;

		$table_name = TutorHelper_Booking_Availability::get_table_name();

		return absint(
			$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Diagnostic read intentionally bypasses caching so the report reflects current database state.
				$wpdb->prepare(
					"SELECT COUNT(*)
					FROM %i
					WHERE weekdays IS NULL
					OR TRIM(weekdays) = ''",
					$table_name
				)
			)
		);
	}

	/**
	 * Count rules whose end date is earlier than the start date.
	 *
	 * @return int
	 */
	private static function count_invalid_date_ranges() {
		global $wpdb;

		$table_name = TutorHelper_Booking_Availability::get_table_name();

		return absint(
			$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Diagnostic read intentionally bypasses caching so the report reflects current database state.
				$wpdb->prepare(
					"SELECT COUNT(*)
					FROM %i
					WHERE end_date IS NOT NULL
					AND end_date <> ''
					AND end_date < start_date",
					$table_name
				)
			)
		);
	}

	/**
	 * Count rules whose end time is not later than the start time.
	 *
	 * @return int
	 */
	private static function count_invalid_time_ranges() {
		global $wpdb;

		$table_name = TutorHelper_Booking_Availability::get_table_name();

		return absint(
			$wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Diagnostic read intentionally bypasses caching so the report reflects current database state.
				$wpdb->prepare(
					'SELECT COUNT(*)
					FROM %i
					WHERE end_time <= start_time',
					$table_name
				)
			)
		);
	}

	/**
	 * Test recurrence generation for the current tutor.
	 *
	 * This only reads Availability for the next 30 days.
	 *
	 * @return array
	 */
	private static function get_occurrence_status() {
		$timezone = wp_timezone();

		$start_date = new DateTimeImmutable(
			'today',
			$timezone
		);

		$end_date = $start_date->modify(
			'+29 days'
		);

		$occurrences = TutorHelper_Booking_Availability::get_occurrences(
			get_current_user_id(),
			$start_date->format( 'Y-m-d' ),
			$end_date->format( 'Y-m-d' )
		);

		if ( is_wp_error( $occurrences ) ) {
			return array(
				'status' => 'Failed: '
					. $occurrences->get_error_code(),
				'count'  => 'Unavailable',
			);
		}

		return array(
			'status' => 'Passed',
			'count'  => count( $occurrences ),
		);
	}
}
