<?php
/**
 * Container: boots subsystems once, wires the weekly cron + new-exposure email.
 *
 * @package Upload_Vault
 */

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

/**
 * Class Uvgf_Plugin
 */
class Uvgf_Plugin {

	/**
	 * Singleton.
	 *
	 * @var Uvgf_Plugin|null
	 */
	private static $instance = null;

	/**
	 * Admin controller.
	 *
	 * @var Uvgf_Admin|null
	 */
	public $admin = null;

	/**
	 * Accessor.
	 *
	 * @return Uvgf_Plugin
	 */
	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
			self::$instance->boot();
		}
		return self::$instance;
	}

	/**
	 * Boot.
	 */
	private function boot() {
		Uvgf_Install::maybe_upgrade();
		Uvgf_Site_Health::init();
		Uvgf_Proxy::init();

		if ( Uvgf_Locator::gf_active() ) {
			Uvgf_Gf_Bridge::init();
		}

		add_action( 'uvgf_weekly_audit', array( $this, 'cron_audit' ) );
		add_action( 'rest_api_init', array( $this, 'rest' ) );
		self::maybe_schedule();

		if ( is_admin() ) {
			$this->admin = new Uvgf_Admin();
			$this->admin->init();
		}

		if ( defined( 'WP_CLI' ) && WP_CLI ) {
			WP_CLI::add_command( 'upload-vault', 'Uvgf_CLI' );
		}

		do_action( 'uvgf_loaded' );
	}

	/**
	 * The audit cadences offered, in the Settings screen's order.
	 *
	 * Every one of them is free. There is deliberately no filter and no default
	 * to raise here: how often an admin re-audits their own site is a setting,
	 * not a tier.
	 *
	 * @return string[] WP cron recurrence names.
	 */
	public static function recurrences() {
		return array( 'hourly', 'twicedaily', 'daily', 'weekly' );
	}

	/**
	 * The configured cadence, defaulting to weekly.
	 *
	 * @return string
	 */
	public static function recurrence() {
		$settings = get_option( 'uvgf_settings', array() );
		$choice   = isset( $settings['audit_recurrence'] ) ? (string) $settings['audit_recurrence'] : 'weekly';
		return in_array( $choice, self::recurrences(), true ) ? $choice : 'weekly';
	}

	/**
	 * Keep the scheduled audit aligned with the settings — both whether it runs
	 * at all and how often. A cadence change must reschedule: without that the
	 * event would keep the recurrence it was first created with.
	 */
	public static function maybe_schedule() {
		$settings = get_option( 'uvgf_settings', array() );
		$wanted   = self::recurrence();
		$event    = wp_get_scheduled_event( 'uvgf_weekly_audit' );

		if ( empty( $settings['weekly_audit'] ) ) {
			if ( $event ) {
				wp_clear_scheduled_hook( 'uvgf_weekly_audit' );
			}
			return;
		}

		if ( ! $event ) {
			wp_schedule_event( time() + HOUR_IN_SECONDS, $wanted, 'uvgf_weekly_audit' );
			return;
		}

		if ( isset( $event->schedule ) && (string) $event->schedule !== $wanted ) {
			wp_clear_scheduled_hook( 'uvgf_weekly_audit' );
			wp_schedule_event( time() + HOUR_IN_SECONDS, $wanted, 'uvgf_weekly_audit' );
		}
	}

	/**
	 * Read-only status endpoint. Free, exactly like the WP-CLI JSON output it
	 * mirrors — an administrator reading their own audit is not a paid feature.
	 */
	public function rest() {
		register_rest_route(
			'upload-vault/v1',
			'/status',
			array(
				'methods'             => 'GET',
				'permission_callback' => function () {
					return current_user_can( 'manage_options' );
				},
				'callback'            => function ( $request ) {
					if ( $request->get_param( 'fresh' ) ) {
						return rest_ensure_response( Uvgf_Auditor::audit() );
					}
					return rest_ensure_response( get_option( 'uvgf_audit', array() ) );
				},
			)
		);
	}

	/**
	 * Scheduled audit: run, then email if NEW exposure appeared.
	 */
	public function cron_audit() {
		$audit = Uvgf_Auditor::audit();
		if ( ! empty( $audit['new_exposed'] ) ) {
			$this->email_new_exposure( $audit );
		}
	}

	/**
	 * Email the chosen address about newly-exposed form folders (guideline-11
	 * clean: a channel the admin chose; nothing site-wide).
	 *
	 * @param array $audit Audit array.
	 */
	public function email_new_exposure( $audit ) {
		$settings = get_option( 'uvgf_settings', array() );
		if ( empty( $settings['email_on_new'] ) ) {
			return;
		}
		$to = ! empty( $settings['alert_email'] ) && is_email( $settings['alert_email'] ) ? $settings['alert_email'] : get_option( 'admin_email' );

		$host   = wp_parse_url( home_url( '/' ), PHP_URL_HOST );
		$titles = Uvgf_Locator::form_titles();

		$subject = sprintf(
			/* translators: 1: number of folders, 2: site host. */
			__( '[Upload Vault] %1$d Gravity Forms upload folder(s) newly exposed on %2$s', 'buildsbyluke-upload-vault-for-gravity-forms' ),
			count( $audit['new_exposed'] ),
			$host
		);

		$lines = array( __( 'Upload Vault\'s scheduled audit confirmed these upload folders are served to anyone with the URL:', 'buildsbyluke-upload-vault-for-gravity-forms' ), '' );
		foreach ( $audit['new_exposed'] as $folder ) {
			$form_id = Uvgf_Indexer::form_id_from_relpath( $folder . '/x' );
			$name    = isset( $titles[ $form_id ] ) && '' !== $titles[ $form_id ]
				? $titles[ $form_id ]
				/* translators: %d: form id. */
				: sprintf( __( 'Form #%d', 'buildsbyluke-upload-vault-for-gravity-forms' ), $form_id );
			$lines[] = '- ' . $name . ' (' . $folder . ')';
		}
		$lines[] = '';
		$lines[] = __( 'Review and protect:', 'buildsbyluke-upload-vault-for-gravity-forms' ) . ' ' . admin_url( 'admin.php?page=upload-vault-for-gravity-forms' );

		wp_mail( $to, $subject, implode( "\n", $lines ) );
	}
}
