<?php

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

/**
 * Plain local free-analysis counter and fail-soft email activation.
 */
class Content_Miner_Free_Scan_Limit {
	const ANALYSIS_LIMIT   = 3;
	const USAGE_OPTION     = 'content_miner_free_analysis_usage';
	const ACTIVATION_OPTION = 'content_miner_free_activation';
	const RETRY_HOOK       = 'content_miner_retry_free_activation';
	const SUBSCRIBE_URL    = 'https://www.corristonconsulting.com/api/subscribe';
	const PRIVACY_URL      = 'https://www.corristonconsulting.com/privacy';

	/** @var object */
	private $license;

	/** @var object|null */
	private $freemius;

	/**
	 * @param object      $license  Live license-policy object.
	 * @param object|null $freemius Freemius SDK instance or test double.
	 */
	public function __construct( $license, $freemius = null ) {
		$this->license  = $license;
		$this->freemius = $freemius;

		add_action( self::RETRY_HOOK, array( $this, 'retry_subscription' ) );
		add_action( 'admin_init', array( $this, 'maybe_capture_freemius_activation' ) );
	}

	/** Return a live UI and enforcement snapshot. @return array<string,mixed> */
	public function get_state() {
		$is_pro     = $this->is_pro();
		$usage      = $this->get_usage();
		$activation = $this->get_activation();
		$used       = min( self::ANALYSIS_LIMIT, max( 0, (int) $usage['completed_analyses'] ) );
		$activated  = $is_pro || ( '' !== $activation['email'] && is_email( $activation['email'] ) );
		$exhausted  = ! $is_pro && $used >= self::ANALYSIS_LIMIT;

		return array(
			'is_pro'              => $is_pro,
			'used'                => $used,
			'limit'               => self::ANALYSIS_LIMIT,
			'remaining'           => max( 0, self::ANALYSIS_LIMIT - $used ),
			'exhausted'           => $exhausted,
			'activated'           => $activated,
			'can_sync'            => $is_pro || ! $exhausted,
			'can_start_analysis'  => $is_pro || ( $activated && ! $exhausted ),
			'email'               => $activation['email'],
			'subscription_status' => $activation['subscription_status'],
			'upgrade_url'         => method_exists( $this->license, 'get_upgrade_url' ) ? $this->license->get_upgrade_url() : 'https://www.corristonconsulting.com/products/content-miner#pricing',
		);
	}

	/** Whether a manual full-library analysis may begin now. */
	public function can_start_analysis() {
		return ! empty( $this->get_state()['can_start_analysis'] );
	}

	/** Whether the manual synchronization control remains available. */
	public function can_sync() {
		return ! empty( $this->get_state()['can_sync'] );
	}

	/**
	 * Persist explicit consent before attempting network delivery.
	 *
	 * @param string $email          Submitted email.
	 * @param string $consent_source plugin_form or freemius_opt_in.
	 * @return array<string,mixed>|WP_Error
	 */
	public function activate( $email, $consent_source = 'plugin_form' ) {
		$email = strtolower( sanitize_email( $email ) );
		if ( ! is_email( $email ) ) {
			return new WP_Error( 'content_miner_invalid_activation_email', __( 'Enter a valid email address to activate your three free analyses.', 'content-miner' ) );
		}

		$current = $this->get_activation();
		$changed = ! hash_equals( (string) $current['email'], $email );
		$state   = array(
			'email'               => $email,
			'consented_at'        => $changed || empty( $current['consented_at'] ) ? gmdate( 'c' ) : $current['consented_at'],
			'consent_source'      => sanitize_key( $consent_source ),
			'subscription_status' => $changed ? 'pending' : $current['subscription_status'],
			'attempts'            => $changed ? 0 : (int) $current['attempts'],
			'last_attempt_at'     => $changed ? '' : $current['last_attempt_at'],
			'subscribed_at'       => $changed ? '' : $current['subscribed_at'],
			'last_error'          => $changed ? '' : $current['last_error'],
		);
		update_option( self::ACTIVATION_OPTION, $state, false );

		if ( 'subscribed' !== $state['subscription_status'] ) {
			$this->deliver_subscription();
		}

		return $this->get_state();
	}

	/**
	 * Reuse an active Freemius opt-in instead of asking for the same consent twice.
	 */
	public function maybe_capture_freemius_activation() {
		if ( $this->is_pro() || ! is_object( $this->freemius ) ) {
			return false;
		}
		$current = $this->get_activation();
		if ( '' !== $current['email'] && is_email( $current['email'] ) ) {
			return false;
		}
		if ( ! method_exists( $this->freemius, 'is_registered' )
			|| ! method_exists( $this->freemius, 'is_tracking_allowed' )
			|| ! $this->freemius->is_registered()
			|| ! $this->freemius->is_tracking_allowed()
			|| ! method_exists( $this->freemius, 'get_user' )
		) {
			return false;
		}
		$user  = $this->freemius->get_user();
		$email = is_object( $user ) && isset( $user->email ) ? sanitize_email( $user->email ) : '';
		if ( ! is_email( $email ) ) {
			return false;
		}

		$state = array(
			'email'               => strtolower( $email ),
			'consented_at'        => gmdate( 'c' ),
			'consent_source'      => 'freemius_opt_in',
			'subscription_status' => 'pending',
			'attempts'            => 0,
			'last_attempt_at'     => '',
			'subscribed_at'       => '',
			'last_error'          => '',
		);
		update_option( self::ACTIVATION_OPTION, $state, false );
		$this->schedule_retry( 1 );
		return true;
	}

	/**
	 * Count a successfully completed analysis once per job.
	 *
	 * @param string $job_id Analysis job UUID.
	 * @return bool Whether this call consumed one free analysis.
	 */
	public function record_completed_analysis( $job_id ) {
		if ( $this->is_pro() ) {
			return false;
		}
		$job_id = sanitize_text_field( $job_id );
		if ( '' === $job_id ) {
			return false;
		}
		$usage = $this->get_usage();
		if ( in_array( $job_id, $usage['completed_job_ids'], true ) ) {
			return false;
		}

		$usage['completed_job_ids'][] = $job_id;
		$usage['completed_job_ids']   = array_slice( array_values( array_unique( $usage['completed_job_ids'] ) ), -self::ANALYSIS_LIMIT );
		$usage['completed_analyses']  = min( self::ANALYSIS_LIMIT, (int) $usage['completed_analyses'] + 1 );
		$usage['updated_at']          = gmdate( 'c' );
		update_option( self::USAGE_OPTION, $usage, false );
		return true;
	}

	/** Retry a locally accepted activation without gating analysis. */
	public function retry_subscription() {
		$activation = $this->get_activation();
		if ( 'pending' !== $activation['subscription_status'] || ! is_email( $activation['email'] ) ) {
			return false;
		}
		return $this->deliver_subscription();
	}

	/** @return bool */
	private function deliver_subscription() {
		$activation = $this->get_activation();
		if ( 'subscribed' === $activation['subscription_status'] || ! is_email( $activation['email'] ) ) {
			return false;
		}

		$payload = array(
			'email'          => $activation['email'],
			'source'         => 'plugin_free_activation',
			'site_domain'    => (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ),
			'plugin_version' => defined( 'CONTENT_MINER_VERSION' ) ? CONTENT_MINER_VERSION : '',
			'consent_source' => $activation['consent_source'],
		);
		$url      = apply_filters( 'content_miner_subscribe_url', self::SUBSCRIBE_URL );
		$response = wp_remote_post(
			$url,
			array(
				'timeout'     => 5,
				'headers'     => array( 'Content-Type' => 'application/json' ),
				'body'        => wp_json_encode( $payload ),
				'data_format' => 'body',
			)
		);

		$activation['attempts']        = (int) $activation['attempts'] + 1;
		$activation['last_attempt_at'] = gmdate( 'c' );
		if ( ! is_wp_error( $response ) ) {
			$code = (int) wp_remote_retrieve_response_code( $response );
			$body = json_decode( wp_remote_retrieve_body( $response ), true );
			if ( $code >= 200 && $code < 300 && is_array( $body ) && ! empty( $body['ok'] ) ) {
				$activation['subscription_status'] = 'subscribed';
				$activation['subscribed_at']       = gmdate( 'c' );
				$activation['last_error']          = '';
				update_option( self::ACTIVATION_OPTION, $activation, false );
				return true;
			}
			$activation['last_error'] = 'http_' . $code;
		} else {
			$activation['last_error'] = sanitize_key( $response->get_error_code() );
		}

		$activation['subscription_status'] = 'pending';
		update_option( self::ACTIVATION_OPTION, $activation, false );
		$delay = min( DAY_IN_SECONDS, HOUR_IN_SECONDS * max( 1, 2 ** min( 4, $activation['attempts'] - 1 ) ) );
		$this->schedule_retry( $delay );
		return false;
	}

	/** @param int $delay Seconds before retry. */
	private function schedule_retry( $delay ) {
		if ( ! wp_next_scheduled( self::RETRY_HOOK ) ) {
			wp_schedule_single_event( time() + max( 1, (int) $delay ), self::RETRY_HOOK );
		}
	}

	/** @return array<string,mixed> */
	private function get_usage() {
		$usage = get_option( self::USAGE_OPTION, array() );
		$usage = wp_parse_args(
			is_array( $usage ) ? $usage : array(),
			array(
				'completed_analyses' => 0,
				'completed_job_ids'  => array(),
				'updated_at'         => '',
			)
		);
		$usage['completed_analyses'] = max( 0, (int) $usage['completed_analyses'] );
		$usage['completed_job_ids']  = is_array( $usage['completed_job_ids'] ) ? array_values( array_map( 'strval', $usage['completed_job_ids'] ) ) : array();
		$usage['updated_at']         = sanitize_text_field( $usage['updated_at'] );
		return $usage;
	}

	/** @return array<string,mixed> */
	private function get_activation() {
		$activation = get_option( self::ACTIVATION_OPTION, array() );
		$activation = wp_parse_args(
			is_array( $activation ) ? $activation : array(),
			array(
				'email'               => '',
				'consented_at'        => '',
				'consent_source'      => '',
				'subscription_status' => 'not_started',
				'attempts'            => 0,
				'last_attempt_at'     => '',
				'subscribed_at'       => '',
				'last_error'          => '',
			)
		);
		$activation['email']               = strtolower( sanitize_email( $activation['email'] ) );
		$activation['consented_at']        = sanitize_text_field( $activation['consented_at'] );
		$activation['consent_source']      = sanitize_key( $activation['consent_source'] );
		$activation['subscription_status'] = in_array( $activation['subscription_status'], array( 'not_started', 'pending', 'subscribed' ), true ) ? $activation['subscription_status'] : 'pending';
		$activation['attempts']            = max( 0, (int) $activation['attempts'] );
		$activation['last_attempt_at']     = sanitize_text_field( $activation['last_attempt_at'] );
		$activation['subscribed_at']       = sanitize_text_field( $activation['subscribed_at'] );
		$activation['last_error']          = sanitize_key( $activation['last_error'] );
		return $activation;
	}

	/** Return the paid-license state live, never from a cached UI property. */
	private function is_pro() {
		return is_object( $this->license ) && method_exists( $this->license, 'is_pro' ) && (bool) $this->license->is_pro();
	}
}
