<?php
/**
 * Site Registration Manager
 *
 * Handles registration of WordPress sites with SiteStaffr middleware.
 * Each site registers its unique secret hash for JWT authentication.
 *
 * @link       https://github.com/sitestaffr/sitestaffr-plugin
 * @since      0.11.77
 *
 * @package    SiteStaffr
 * @subpackage SiteStaffr/includes
 */

/**
 * Site Registration Manager class.
 *
 * Manages the registration flow between WordPress and middleware:
 * 1. Generate unique site secret (32 bytes)
 * 2. Register with middleware (site_url + secret_hash)
 * 3. Middleware stores hash for JWT verification
 *
 * SECURITY:
 * - Secret is stored locally in WordPress options (encrypted at rest)
 * - Only the hash of the secret is sent to middleware
 * - Used for signing JWT tokens for WebSocket authentication
 *
 * @since      0.11.77
 * @package    SiteStaffr
 * @subpackage SiteStaffr/includes
 * @author     SiteStaffr
 */
class SiteStaffr_Site_Registration {

	/**
	 * Middleware registration endpoint path.
	 */
	const REGISTRATION_ENDPOINT = '/api/sites/register';

	/**
	 * Middleware billing registration endpoint path.
	 */
	const BILLING_REGISTRATION_ENDPOINT = '/api/billing/site-registration';

	/**
	 * Register this WordPress site with SiteStaffr middleware.
	 *
	 * Called automatically on plugin activation. Can also be called manually
	 * to re-register after secret rotation.
	 *
	 * @since    0.11.77
	 * @updated  0.13.72 - Store per-site API secret returned from middleware
	 * @return   bool    True on success, false on failure.
	 */
	public static function register_site() {
		// Get site URL and secret
		$site_url    = get_site_url();
		$site_secret = self::get_or_create_secret();
		$secret_hash = hash( 'sha256', $site_secret );

		// Get middleware URL
		$middleware_url = SITESTAFFR_MIDDLEWARE_URL;

		// Build registration payload
		$payload = array(
			'site_url'       => $site_url,
			'secret_hash'    => $secret_hash,
			'plugin_version' => SITESTAFFR_VERSION,
		);

		// Send registration request
		$response = wp_remote_post(
			$middleware_url . self::REGISTRATION_ENDPOINT,
			array(
				// Keep timeout short to avoid blocking WordPress execution on connectivity issues.
				'timeout' => 8,
				'headers' => array( 'Content-Type' => 'application/json' ),
				'body'    => wp_json_encode( $payload ),
			)
		);

		// Check for HTTP errors
		if ( is_wp_error( $response ) ) {
			SiteStaffr_Logger::legacy( 'SiteStaffr: Site registration failed - ' . $response->get_error_message() );
			return false;
		}

		// Check response code
		$code = wp_remote_retrieve_response_code( $response );
		if ( $code === 200 || $code === 201 ) {
			// Parse response body
			$body = wp_remote_retrieve_body( $response );
			$data = json_decode( $body, true );

			// Store per-site API secret returned from middleware (v0.13.72+)
			if ( isset( $data['site_api_secret'] ) ) {
				update_option( 'sitestaffr_site_api_secret', sanitize_text_field( $data['site_api_secret'] ), false );
				SiteStaffr_Logger::legacy( 'SiteStaffr: Stored per-site API secret from middleware' );
			}

			// Registration successful
			update_option( 'sitestaffr_site_registered', true );
			update_option( 'sitestaffr_registration_date', current_time( 'mysql' ) );
			SiteStaffr_Logger::legacy( "SiteStaffr: Site registered successfully with middleware ({$site_url})" );

			// Phase 1 billing registration is intentionally non-fatal to preserve existing behavior.
			$billing_registered = self::register_billing_site( $site_url );
			if ( ! $billing_registered ) {
				SiteStaffr_Logger::legacy( 'SiteStaffr: Billing registration failed (non-fatal, auth registration remains active)' );
			}

			return true;
		}

		// Registration failed
		$body = wp_remote_retrieve_body( $response );
		SiteStaffr_Logger::legacy( "SiteStaffr: Site registration failed with code {$code} (body_length=" . strlen( $body ) . ')' );
		return false;
	}

	/**
	 * Check if this site is registered with middleware.
	 *
	 * @since    0.11.77
	 * @return   bool    True if registered, false otherwise.
	 */
	public static function is_registered() {
		return (bool) get_option( 'sitestaffr_site_registered', false );
	}

	/**
	 * Get or create site-specific secret.
	 *
	 * This secret is used to sign JWT tokens and is never sent to middleware.
	 * Only the hash of this secret is sent during registration.
	 *
	 * @since    0.11.77
	 * @return   string    Site secret (64 hex characters = 32 bytes).
	 */
	public static function get_or_create_secret() {
		$secret = get_option( 'sitestaffr_site_secret' );
		if ( ! $secret ) {
			// Generate new cryptographically secure secret
			$secret = bin2hex( random_bytes( 32 ) );
			update_option( 'sitestaffr_site_secret', $secret, false ); // Don't autoload
		}
		return $secret;
	}

	/**
	 * Get or create immutable installation ID.
	 *
	 * @since 0.14.1
	 * @return string UUID v4 installation ID.
	 */
	public static function get_or_create_installation_id() {
		$installation_id = get_option( 'sitestaffr_installation_id' );
		if ( ! empty( $installation_id ) && self::is_valid_uuid_v4( $installation_id ) ) {
			return $installation_id;
		}

		if ( function_exists( 'wp_generate_uuid4' ) ) {
			$installation_id = wp_generate_uuid4();
		} else {
			$data            = random_bytes( 16 );
			$data[6]         = chr( ( ord( $data[6] ) & 0x0f ) | 0x40 );
			$data[8]         = chr( ( ord( $data[8] ) & 0x3f ) | 0x80 );
			$installation_id = vsprintf(
				'%s%s-%s-%s-%s-%s%s%s',
				str_split( bin2hex( $data ), 4 )
			);
		}

		update_option( 'sitestaffr_installation_id', $installation_id, false );
		SiteStaffr_Logger::legacy( "SiteStaffr: Generated immutable installation_id: {$installation_id}" );

		return $installation_id;
	}

	/**
	 * Rotate site secret and re-register with middleware.
	 *
	 * Use this for security rotation or if secret is compromised.
	 * This will invalidate all existing JWT tokens.
	 *
	 * @since    0.11.77
	 * @return   bool    True on success, false on failure.
	 */
	public static function rotate_secret() {
		// Delete existing secret and registration status
		delete_option( 'sitestaffr_site_secret' );
		delete_option( 'sitestaffr_site_registered' );

		// Re-register with new secret
		$result = self::register_site();

		if ( $result ) {
			SiteStaffr_Logger::legacy( 'SiteStaffr: Secret rotated successfully' );
		} else {
			SiteStaffr_Logger::legacy( 'SiteStaffr: Secret rotation failed' );
		}

		return $result;
	}

	/**
	 * Get registration status information.
	 *
	 * Returns an array with registration details for admin display.
	 *
	 * @since    0.11.77
	 * @return   array    Registration status information.
	 */
	public static function get_registration_info() {
		return array(
			'registered'        => self::is_registered(),
			'site_url'          => get_site_url(),
			'registration_date' => get_option( 'sitestaffr_registration_date', 'Not registered' ),
			'has_secret'        => ! empty( get_option( 'sitestaffr_site_secret' ) ),
		);
	}

	/**
	 * Register site in billing system (Phase 1 foundation).
	 *
	 * @since 0.14.1
	 * @param string $site_url Site URL.
	 * @return bool True on success, false on failure.
	 */
	/**
	 * Build the billing registration payload.
	 *
	 * Kept separate from the HTTP call so the contract that billing registers the
	 * OWNER's account email (not just the WordPress admin email) stays testable —
	 * tests/dev/account-contact-self-test.php asserts exactly this.
	 *
	 * @param string $site_url Site URL being registered.
	 * @return array
	 */
	private static function build_billing_registration_payload( $site_url ) {
		return array(
			'installation_id' => self::get_or_create_installation_id(),
			'site_url'        => $site_url,
			'admin_email'     => get_option( 'sitestaffr_account_email', get_option( 'admin_email' ) ),
		);
	}

	private static function register_billing_site( $site_url ) {
		$middleware_url   = SITESTAFFR_MIDDLEWARE_URL;
		$installation_id  = self::get_or_create_installation_id();
		$api_secret       = self::get_api_secret();
		$billing_endpoint = $middleware_url . self::BILLING_REGISTRATION_ENDPOINT;

		if ( empty( $api_secret ) ) {
			SiteStaffr_Logger::legacy( 'SiteStaffr: Billing registration skipped - missing API secret for HMAC signing' );
			return false;
		}

		$payload = self::build_billing_registration_payload( $site_url );

		$payload_json   = wp_json_encode( $payload );
		$timestamp      = (string) time();
		$payload_to_sign = $timestamp . '.' . $payload_json;
		$signature_raw  = hash_hmac( 'sha256', $payload_to_sign, $api_secret, true );
		$signature_base = base64_encode( $signature_raw );

		$response = wp_remote_post(
			$billing_endpoint,
			array(
				'timeout' => 8,
				'headers' => array(
					'Content-Type'         => 'application/json',
					'X-SiteStaffr-Signature' => $signature_base,
					'X-SiteStaffr-Timestamp' => $timestamp,
				),
				'body'    => $payload_json,
			)
		);

		if ( is_wp_error( $response ) ) {
			SiteStaffr_Logger::legacy( 'SiteStaffr: Billing registration failed - ' . $response->get_error_message() );
			return false;
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = wp_remote_retrieve_body( $response );

		if ( $code !== 200 && $code !== 201 ) {
			SiteStaffr_Logger::legacy( "SiteStaffr: Billing registration failed with code {$code} (body_length=" . strlen( $body ) . ')' );
			return false;
		}

		$data = json_decode( $body, true );
		if ( empty( $data ) || empty( $data['success'] ) ) {
			SiteStaffr_Logger::legacy( 'SiteStaffr: Billing registration returned invalid payload' );
			return false;
		}

		self::persist_billing_registration_state( $data );
		SiteStaffr_Logger::legacy( 'SiteStaffr: Billing registration successful' );

		return true;
	}

	/**
	 * Persist billing identity and cache seed from middleware response.
	 *
	 * @since 0.14.1
	 * @param array $registration_data Middleware response payload.
	 * @return void
	 */
	private static function persist_billing_registration_state( $registration_data ) {
		$returned_account_id = isset( $registration_data['account_id'] ) ? sanitize_text_field( $registration_data['account_id'] ) : '';
		$existing_account_id = get_option( 'sitestaffr_account_id', '' );

		if ( ! empty( $returned_account_id ) ) {
			if ( empty( $existing_account_id ) ) {
				update_option( 'sitestaffr_account_id', $returned_account_id, false );
			} elseif ( $existing_account_id !== $returned_account_id ) {
				SiteStaffr_Logger::legacy(
					sprintf(
						'SiteStaffr: Account ID mismatch detected. Existing=%s, Returned=%s. Updating local account ID to middleware-authoritative value.',
						$existing_account_id,
						$returned_account_id
					)
				);
				update_option( 'sitestaffr_account_id', $returned_account_id, false );
			}
		}

		$included_seconds = isset( $registration_data['included_seconds_remaining'] ) ? (int) $registration_data['included_seconds_remaining'] : 0;
		$addon_seconds    = isset( $registration_data['addon_seconds_remaining'] ) ? (int) $registration_data['addon_seconds_remaining'] : 0;

		$billing_cache = array(
			'subscription_status'              => isset( $registration_data['subscription_status'] ) ? sanitize_text_field( $registration_data['subscription_status'] ) : 'trial_active',
			'plan_name'                        => isset( $registration_data['plan_name'] ) ? sanitize_text_field( $registration_data['plan_name'] ) : 'trial',
			'included_minutes_remaining'       => isset( $registration_data['included_minutes_remaining'] ) ? (int) $registration_data['included_minutes_remaining'] : (int) ceil( $included_seconds / 60 ),
			'addon_minutes_remaining'          => isset( $registration_data['addon_minutes_remaining'] ) ? (int) $registration_data['addon_minutes_remaining'] : (int) ceil( $addon_seconds / 60 ),
			'subscription_current_period_end'  => isset( $registration_data['subscription_current_period_end'] ) ? sanitize_text_field( $registration_data['subscription_current_period_end'] ) : null,
			'trial_expires_at'                 => isset( $registration_data['trial_expires_at'] ) ? sanitize_text_field( $registration_data['trial_expires_at'] ) : null,
			'last_synced_at'                   => gmdate( 'c' ),
			'cache_ttl'                        => 300,
		);

		update_option( 'sitestaffr_billing_cache', $billing_cache, false );
	}

	/**
	 * Validate UUID v4 format.
	 *
	 * @since 0.14.1
	 * @param string $uuid UUID string.
	 * @return bool True if valid UUID v4.
	 */
	private static function is_valid_uuid_v4( $uuid ) {
		return is_string( $uuid ) && preg_match( '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $uuid );
	}

	/**
	 * Get API secret used for middleware HMAC signing.
	 *
	 * @since 0.14.1
	 * @return string API secret.
	 */
	private static function get_api_secret() {
		$site_api_secret = get_option( 'sitestaffr_site_api_secret' );
		if ( ! empty( $site_api_secret ) ) {
			return $site_api_secret;
		}

		$global_api_secret = get_option( 'sitestaffr_api_secret' );
		if ( ! empty( $global_api_secret ) ) {
			return $global_api_secret;
		}

		return '';
	}
}
