<?php
/**
 * Hourly sweeps: (1) submissions that expected a notification but produced
 * zero mail evidence → notification_missing; (2) forms with a real baseline
 * that have gone silent → form_silent.
 *
 * @package Form_Flight_Recorder
 */

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

/**
 * Reconciler.
 */
class Ffrd_Reconciler {

	const GRACE = 30 * MINUTE_IN_SECONDS;

	/**
	 * Container.
	 *
	 * @var Ffrd_Plugin
	 */
	private $plugin;

	/**
	 * Constructor.
	 *
	 * @param Ffrd_Plugin $plugin Container.
	 */
	public function __construct( $plugin ) {
		$this->plugin = $plugin;
	}

	/**
	 * Run both sweeps.
	 *
	 * @return array { missing: int, silent: int }
	 */
	public function sweep() {
		return array(
			'missing' => $this->sweep_missing(),
			'silent'  => $this->sweep_silence(),
		);
	}

	/**
	 * Missing-notification sweep (grace 30min; deduped per submission).
	 *
	 * @return int New missing events.
	 */
	public function sweep_missing() {
		$events = $this->plugin->get( 'events' );
		$forms  = $this->plugin->get( 'registry' )->all();

		$submissions = $events->query(
			array(
				'type'  => 'submission',
				'since' => time() - DAY_IN_SECONDS,
				'until' => time() - self::GRACE,
				'limit' => 200,
			)
		);

		$new = 0;
		foreach ( $submissions as $row ) {
			$ref = 'sub-' . $row->id;
			if ( $events->latest( 'notification_missing', $ref ) ) {
				continue;
			}

			$expects  = ! empty( $row->data['expects'] );
			$override = isset( $forms[ $row->object_ref ]['expects'] ) ? $forms[ $row->object_ref ]['expects'] : null;
			if ( 'on' === $override ) {
				$expects = true;
			} elseif ( 'off' === $override ) {
				$expects = false;
			}

			if ( ! $expects ) {
				continue;
			}
			if ( ! empty( $row->data['attempts'] ) || ! empty( $row->data['sent_confirmed'] ) ) {
				continue;
			}

			$events->insert(
				'notification_missing',
				$ref,
				'critical',
				array(
					'form_key'     => $row->object_ref,
					'title'        => isset( $row->data['title'] ) ? $row->data['title'] : '',
					'plugin'       => isset( $row->data['plugin'] ) ? $row->data['plugin'] : '',
					'form_id'      => isset( $row->data['form_id'] ) ? $row->data['form_id'] : '',
					'submission'   => $row->id,
					'submitted_at' => $row->created_at,
				)
			);
			$new++;
		}
		return $new;
	}

	/**
	 * Silence sweep: a form with a meaningful baseline that stopped submitting.
	 *
	 * @return int New silent events.
	 */
	public function sweep_silence() {
		$events   = $this->plugin->get( 'events' );
		$registry = $this->plugin->get( 'registry' );
		$settings = $this->plugin->settings();

		$new = 0;
		foreach ( $registry->all() as $key => $form ) {
			$last = (int) $form['last_submission'];
			if ( $last <= 0 ) {
				continue; // Never submitted — nothing to compare against.
			}

			$baseline = $registry->baseline( $events, $key );
			if ( $baseline['per_week'] < 3 ) {
				continue; // Too quiet to distinguish "broken" from "slow week".
			}

			$hours     = ! empty( $form['silence_hours'] ) ? (int) $form['silence_hours'] : (int) $settings['silence_hours'];
			$threshold = max( $hours * HOUR_IN_SECONDS, 3 * $baseline['typical_gap'] );

			/**
			 * Filters the allowed silence window for a form (Pro extends it
			 * across weekends).
			 *
			 * @param int   $threshold Seconds of allowed silence.
			 * @param array $form      Registry entry.
			 * @param array $baseline  Baseline stats.
			 */
			$threshold = (int) apply_filters( 'ffrd_silence_threshold', $threshold, $form, $baseline );

			$silent_for = time() - $last;
			if ( $silent_for < $threshold ) {
				continue;
			}

			$existing = $events->latest( 'form_silent', $key );
			if ( $existing && ( time() - strtotime( $existing->created_at . ' UTC' ) ) < 2 * DAY_IN_SECONDS ) {
				continue;
			}

			$events->insert(
				'form_silent',
				$key,
				'warning',
				array(
					'form_key'     => $key,
					'title'        => $form['title'],
					'plugin'       => $form['source'],
					'form_id'      => $form['form_id'],
					'last_at'      => gmdate( 'Y-m-d H:i:s', $last ),
					'hours_silent' => (int) round( $silent_for / HOUR_IN_SECONDS ),
					'per_week'     => $baseline['per_week'],
				)
			);
			$new++;
		}
		return $new;
	}

	/**
	 * Cockpit numbers.
	 *
	 * @return array
	 */
	public function counts() {
		$events = $this->plugin->get( 'events' );
		$week   = time() - 7 * DAY_IN_SECONDS;

		$silent = array();
		foreach ( $events->query(
			array(
				'type'  => 'form_silent',
				'since' => time() - 2 * DAY_IN_SECONDS,
				'limit' => 50,
			)
		) as $row ) {
			$silent[ $row->object_ref ] = $row->data;
		}

		return array(
			'forms'           => count( $this->plugin->get( 'registry' )->all() ),
			'detected'        => array_keys( $this->plugin->get( 'adapters' )->detected() ),
			'submissions_24h' => $events->count( 'submission', time() - DAY_IN_SECONDS ),
			'submissions_7d'  => $events->count( 'submission', $week ),
			'missing_7d'      => $events->count( 'notification_missing', $week ),
			'failed_7d'       => $events->count( 'notification_failed', $week ),
			'silent'          => array_values( $silent ),
		);
	}
}
