<?php
/**
 * Known-forms registry: one option, keyed source:form_id. Holds identity,
 * last activity and the per-form overrides. Baselines are computed from the
 * events tape, never stored stale.
 *
 * @package Form_Flight_Recorder
 */

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

/**
 * Registry over the ffrd_forms option.
 */
class Ffrd_Registry {

	const OPTION    = 'ffrd_forms';
	const MAX_FORMS = 200;

	/**
	 * All known forms.
	 *
	 * @return array key => { source, form_id, title, first_seen, last_submission, expects (null|'on'|'off'), silence_hours (0=default) }
	 */
	public function all() {
		$forms = get_option( self::OPTION, array() );
		return is_array( $forms ) ? $forms : array();
	}

	/**
	 * Key helper.
	 *
	 * @param string $source  Adapter slug (cf7, wpforms, gf, ninja, fluent).
	 * @param string $form_id Form id.
	 * @return string
	 */
	public static function key( $source, $form_id ) {
		return sanitize_key( $source ) . ':' . sanitize_key( (string) $form_id );
	}

	/**
	 * Ensure a form exists in the registry (discovery or first submission).
	 *
	 * @param string $source  Adapter slug.
	 * @param string $form_id Form id.
	 * @param string $title   Title.
	 */
	public function ensure( $source, $form_id, $title ) {
		$forms = $this->all();
		$key   = self::key( $source, $form_id );

		if ( ! isset( $forms[ $key ] ) ) {
			if ( count( $forms ) >= self::MAX_FORMS ) {
				return;
			}
			$forms[ $key ] = array(
				'source'          => sanitize_key( $source ),
				'form_id'         => (string) $form_id,
				'title'           => sanitize_text_field( $title ),
				'first_seen'      => time(),
				'last_submission' => 0,
				'expects'         => null,
				'silence_hours'   => 0,
			);
		} elseif ( $title && $forms[ $key ]['title'] !== $title ) {
			$forms[ $key ]['title'] = sanitize_text_field( $title );
		} else {
			return;
		}

		update_option( self::OPTION, $forms, false );
	}

	/**
	 * Record a submission moment.
	 *
	 * @param string $source  Adapter slug.
	 * @param string $form_id Form id.
	 * @param string $title   Title.
	 */
	public function touch( $source, $form_id, $title ) {
		$this->ensure( $source, $form_id, $title );

		$forms = $this->all();
		$key   = self::key( $source, $form_id );
		if ( isset( $forms[ $key ] ) ) {
			$forms[ $key ]['last_submission'] = time();
			update_option( self::OPTION, $forms, false );
		}
	}

	/**
	 * Save per-form overrides.
	 *
	 * @param string $key           Registry key.
	 * @param string $expects       ''|'on'|'off' ('' = auto).
	 * @param int    $silence_hours 0 = site default.
	 * @return bool
	 */
	public function set_overrides( $key, $expects, $silence_hours ) {
		$forms = $this->all();
		if ( ! isset( $forms[ $key ] ) ) {
			return false;
		}
		$forms[ $key ]['expects']       = in_array( $expects, array( 'on', 'off' ), true ) ? $expects : null;
		$forms[ $key ]['silence_hours'] = max( 0, min( 24 * 90, (int) $silence_hours ) );
		update_option( self::OPTION, $forms, false );
		return true;
	}

	/**
	 * 28-day submission stats for a form, from the events tape.
	 *
	 * @param Ffrd_Events $events Event store.
	 * @param string      $key    Registry key.
	 * @return array { count: int, per_week: float, typical_gap: int (seconds) }
	 */
	public function baseline( $events, $key ) {
		$count = $events->count( 'submission', time() - 28 * DAY_IN_SECONDS, $key );
		return array(
			'count'       => $count,
			'per_week'    => round( $count / 4, 1 ),
			'typical_gap' => $count > 0 ? (int) ( 28 * DAY_IN_SECONDS / $count ) : 0,
		);
	}
}
