<?php

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

trait WPMR_Account_Mgt {

	/**
	 * Check whether the site has completed registration.
	 *
	 * Registration state is stored in the `user` setting.
	 *
	 * @return mixed Truthy registration payload when registered, otherwise falsy.
	 * 
	 * // has_registration or get_registration_payload
	 */
	function is_registered() {
		return $this->get_setting( 'user' );
	}

	/**
	 * Send a weekly security digest email to the registered user.
	 *
	 * Hooked to the `wpmr_daily` cron event. This method rate-limits sending to once
	 * per 7 days using the `wpmr_last_digest_sent` setting.
	 * 
	 * // maybe_send_weekly_digest
	 *
	 * @return void
	 */
	function send_weekly_digest() {
		$last_sent = $this->get_setting( 'wpmr_last_digest_sent' );
		if ( $last_sent && ( time() - $last_sent ) < 7 * DAY_IN_SECONDS ) {
			return;
		}

		$user_settings = $this->get_setting( 'user' );
		if ( empty( $user_settings['email'] ) ) {
			return;
		}

		$to      = $user_settings['email'];
		$subject = 'Weekly Security Digest - ' . get_bloginfo( 'name' );

		// Get last scan status
		$infected = $this->get_setting( 'infected' );
		$status   = $infected ? 'Infected' : 'Clean';
		$color    = $infected ? '#d63638' : '#46b450';

		$message = "
        <html>
        <body style='font-family: sans-serif; line-height: 1.6; color: #333;'>
            <h2 style='color: #23282d;'>Weekly Security Digest</h2>
            <p>Here is the security status for your website: <strong>" . get_bloginfo( 'name' ) . "</strong></p>
            <div style='padding: 20px; background-color: #f0f0f1; border-left: 5px solid {$color}; margin-bottom: 20px;'>
                <h3 style='margin: 0;'>Status: <span style='color: {$color};'>{$status}</span></h3>
            </div>
            <p>To view full details, please <a href='" . admin_url( 'admin.php?page=wpmr' ) . "' style='color: #0073aa; text-decoration: none;'>login to your dashboard</a>.</p>
            
            <div style='margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;'>
                <p><strong>Stay Safe with Malcure Advanced Edition</strong></p>
                <p>Get real-time definition updates, auto-repair capabilities, and WP-CLI integration.</p>
                <p><a href='https://malcure.com/?p=116&utm_source=weekly_digest&utm_medium=email&utm_campaign=wpmr' style='background-color: #0073aa; color: #fff; padding: 10px 15px; text-decoration: none; border-radius: 3px;'>Upgrade to Pro</a></p>
            </div>

            <p style='margin-top: 30px; font-size: 12px; color: #666;'><small>Powered by <a href='https://malcure.com/?utm_source=weekly_digest_footer&utm_medium=email&utm_campaign=wpmr' style='color: #666;'>Malcure Malware Scanner</a></small></p>
        </body>
        </html>
        ";

		$headers = array( 'Content-Type: text/html; charset=UTF-8' );
		wp_mail( $to, $subject, $message, $headers );

		$this->update_setting( 'wpmr_last_digest_sent', time() );
	}

	/**
	 * Register a site/user via WP-CLI.
	 *
	 * Called from CLI commands and internal helpers. When `$echo` is true, outputs
	 * feedback using WP-CLI (when available) or via plain output.
	 *
	 * @param string $email Email address.
	 * @param string $fn    First name.
	 * @param string $ln    Last name.
	 * @param bool   $echo  Whether to output feedback.
	 * @return bool True on successful registration, false otherwise.
	 */
	function wpmr_cli_register( $email, $fn, $ln, $echo = false ) {
		$sanitized_user = array(
			'email' => sanitize_email( $email ),
			'fn'    => sanitize_text_field( $fn ),
			'ln'    => sanitize_text_field( $ln ),
		);

		if ( empty( $sanitized_user['email'] ) || ! is_email( $sanitized_user['email'] ) ) {
			return $this->wpmr_cli_registration_feedback( 'Invalid email address.', $echo, false );
		}

		if ( empty( $sanitized_user['fn'] ) || empty( $sanitized_user['ln'] ) ) {
			return $this->wpmr_cli_registration_feedback( 'Please provide first and last name for registration.', $echo, false );
		}

		$result = $this->wpmr_send_registration_state( $sanitized_user, 'cli' );

		if ( is_wp_error( $result ) ) {
			return $this->wpmr_cli_registration_feedback( $result->get_error_message(), $echo, false );
		}

		return $this->wpmr_cli_registration_feedback( sprintf( 'Registration complete. Please use %s as your USER ID.', $result['user_email'] ), $echo, true );
	}

	/**
	 * AJAX handler to register a user/site from the admin UI.
	 *
	 * Validates nonce + capability, sanitizes fields, sends state to the SaaS backend,
	 * then triggers a definitions refresh.
	 *
	 * @return void Sends JSON response and exits.
	 * 
	 * // ajax_register_user or ajax_web_register
	 */
	function wpmr_web_register() {
		check_ajax_referer( 'wpmr_web_register', 'wpmr_web_register_nonce' );
		if ( ! current_user_can( $this->cap ) ) {
			return;
		}
		$this->flog( 'Starting web registration' );
		$start_time = microtime( true );

		$user_data = isset( $_REQUEST['user'] ) ? wp_unslash( $_REQUEST['user'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array is sanitized field by field below

		if ( empty( $user_data ) || empty( $user_data['email'] ) || empty( $user_data['fn'] ) || empty( $user_data['ln'] ) ) {
			wp_send_json( array( 'error' => 'Please fill all details' ) );
		}
		if ( ! filter_var( sanitize_email( $user_data['email'] ), FILTER_VALIDATE_EMAIL ) ) {
			wp_send_json( array( 'error' => 'Invalid email' ) );
		}

		// Sanitize user data
		$sanitized_user = array(
			'email' => sanitize_email( $user_data['email'] ),
			'fn'    => sanitize_text_field( $user_data['fn'] ),
			'ln'    => sanitize_text_field( $user_data['ln'] ),
		);

		$result = $this->wpmr_send_registration_state( $sanitized_user, 'web' );
		$this->flog( 'Web registration completed in ' . ( microtime( true ) - $start_time ) . ' seconds' );
		$start_time = microtime( true );

		if ( is_wp_error( $result ) ) {
			wp_send_json( array( 'error' => $result->get_error_message() ) );
		}

		// $this->refresh_checksums_async(); // trigger in background so user doesn't have to wait
		// $this->flog( 'Checksums fetched in ' . ( microtime( true ) - $start_time ) . ' seconds' );
		$start_time = microtime( true );
		$this->update_definitions_cli( false );
		$this->flog( 'Definitions updated in ' . ( microtime( true ) - $start_time ) . ' seconds' );

		wp_send_json( $result );
	}

	/**
	 * Send registration state to the SaaS backend and persist the returned payload.
	 *
	 * @param array{email:string,fn:string,ln:string} $user   Sanitized user registration data.
	 * @param string                                $source Source identifier (e.g. web|cli).
	 * @return array|WP_Error Registration payload on success, or WP_Error on failure.
	 * 
	 * // send_registration_to_saas or register_with_saas
	 */
	private function wpmr_send_registration_state( $user, $source = 'web' ) {
		$user_payload = array_merge(
			$user,
			array(
				'key'    => site_url(),
				'source' => $source,
			)
		);

		$request = $this->saas_request(
			'saas_registration',
			array(
				'method'      => 'GET',
				'send_state'  => 'query',
				'state_extra' => array( 'user' => $user_payload ),
				'timeout'     => $this->timeout,
			)
		);

		if ( is_wp_error( $request ) ) {
			return $request;
		}

		$data = isset( $request['response'] ) ? $request['response'] : null;
		if ( empty( $data ) || ! is_array( $data ) ) {
			return new WP_Error( 'registration_invalid_response', 'Invalid response from server.' );
		}

		if ( ! empty( $data['error'] ) ) {
			return new WP_Error( 'registration_error', is_string( $data['error'] ) ? $data['error'] : wp_json_encode( $data['error'] ) );
		}

		$payload = isset( $request['payload'] ) ? $request['payload'] : null;

		if ( empty( $payload ) || ! is_array( $payload ) ) {
			return new WP_Error( 'registration_invalid_payload', 'Registration payload missing in response.' );
		}

		$this->update_setting( 'user', $payload );

		return $payload;
	}

	/**
	 * Output/return registration feedback for CLI flows.
	 *
	 * @param string $message Message to display.
	 * @param bool   $echo    Whether to output feedback.
	 * @param bool   $success Whether the operation succeeded.
	 * @return bool True when successful, false otherwise.
	 * 
	 * // output_cli_registration_feedback
	 */
	private function wpmr_cli_registration_feedback( $message, $echo, $success ) {
		if ( ! $echo ) {
			return $success;
		}

		if ( $this->wpmr_iscli() ) {
			if ( $success ) {
				WP_CLI::success( $message );
			} else {
				WP_CLI::error( $message, false );
			}
		} else {
			echo wp_kses_post( $message );
		}

		return $success;
	}

	/**
	 * Clear the cached license status.
	 * 
	 * // clear_license_status_cache
	 *
	 * @return true Always returns true.
	 */
	function clear_license_status() {
		delete_transient( 'WPMR_license_status' );
		return true;
	}

	/**
	 * Deactivate the stored license key and clear local state.
	 *
	 * This is used during plugin deactivation and other reset flows. It intentionally
	 * fails silently (no user-facing messaging).
	 * 
	 * // deactivate_license_key
	 *
	 * @param bool $silent Whether to run silently (accepted for compatibility).
	 * @return void
	 */
	function deactivate_license() {
		$key      = $this->get_setting( 'license_key' );
		$response = $key ? $this->get_license_api_response( 'deactivate_license', $key ) : new WP_Error( 'error', 'No license key found for ' . __FUNCTION__ );
		// fail silently as we are only called during plugin deactivation;
		$this->delete_setting( 'license_key' );
		$this->clear_license_status();
	}

	/**
	 * Build a formatted JSON blob describing current license status.
	 *
	 * Used by the AJAX endpoint that populates license status UI. This method reads the
	 * `WPMR_license_status` transient when available, optionally refreshing via the license API.
	 * 
	 * // get_license_status_json() or format_license_status_json()
	 *
	 * @param int|bool $from_cache When truthy, prefer cached transient.
	 * @return string JSON-encoded license status payload.
	 */
	function format_license_status_data( $from_cache = 0 ) {
		if ( ! $this->get_setting( 'license_key' ) ) {
			return json_encode( array( 'error' => 'No license key found to format.' ) );
		}

		$status = get_transient( 'WPMR_license_status' );

		if ( ! $from_cache || ! $status ) {
			$status = $this->get_license_api_response( 'check_license', $this->get_setting( 'license_key' ) );
		}

		if ( ! $status ) {
			return json_encode( array( 'error' => 'Unable to retrieve license status' ) );
		}

		// Build the response data
		$response_data = array(
			'success'  => true,
			'license'  => isset( $status['license'] ) ? $status['license'] : 'unknown',
			'raw_data' => $status,
		);

		// Add formatted status information
		if ( isset( $status['license'] ) ) {
			$response_data['license_status'] = ucwords( preg_replace( '/[^A-Za-z0-9 ]/', ' ', $status['license'] ) );
		}

		// Add error/reason if present
		if ( isset( $status['error'] ) ) {
			$response_data['error_reason'] = ucwords( preg_replace( '/[^A-Za-z0-9 ]/', ' ', $status['error'] ) );
		}

		// Add customer information
		if ( ! empty( $status['customer_email'] ) ) {
			$response_data['customer_email'] = $status['customer_email'];
		}

		if ( ! empty( $status['customer_name'] ) ) {
			$response_data['customer_name'] = $status['customer_name'];
		}

		// Add activation information
		if ( ! empty( $status['site_count'] ) && isset( $status['activations_left'] ) ) {
			$limit = 'Unlimited';
			if ( isset( $status['license_limit'] ) && $status['license_limit'] !== null ) {
				$limit = $status['license_limit'] == 0 ? 'Unlimited' : $status['license_limit'];
			}
			$response_data['activations'] = array(
				'used'      => $status['site_count'],
				'limit'     => $limit,
				'remaining' => $status['activations_left'],
			);
		}

		// Add expiration information
		if ( ! empty( $status['expires'] ) ) {
			if ( $status['expires'] == 'lifetime' ) {
				$response_data['expires']           = 'Never';
				$response_data['expires_formatted'] = 'Lifetime';
			} else {
				$response_data['expires'] = $status['expires'];
				try {
					$timezone_string = function_exists( 'wp_timezone_string' ) ? wp_timezone_string() : $this->timezone_string_compat();
					$timezone        = new DateTimeZone( $timezone_string );
					$date            = new DateTime( $status['expires'], new DateTimeZone( 'UTC' ) );
					$date->setTimezone( $timezone );
					$response_data['expires_formatted'] = $date->format( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) . ' ' . $timezone_string;
				} catch ( Exception $e ) {
					$response_data['expires_formatted'] = $status['expires'];
				}
			}
		}

		// Add renewal link if expired
		if ( isset( $status['license'] ) && $status['license'] == 'expired' ) {
			$response_data['renewal_link'] = 'https://malcure.com/?p=168&edd_license_key=' . $this->get_setting( 'license_key' ) . '&edd_action=apply_license_renewal&utm_source=pluginexpired&utm_medium=web&utm_campaign=wpmr';
		}

		return json_encode( $response_data );
	}

	/**
	 * Call the Malcure licensing API (EDD) and return the decoded response.
	 *
	 * @param string $action      EDD action (e.g. check_license|activate_license|deactivate_license).
	 * @param string $license_key License key (defaults to stored key).
	 * @param bool   $silent      Whether to suppress logging/user messaging (currently unused).
	 * 
	 * // request_license_api() or fetch_license_api_response()
	 * 
	 * @return array|null Decoded JSON response array, or null on error.
	 */
	function get_license_api_response( $action, $license_key = '', $silent = false ) {
		if ( empty( $license_key ) ) {
			$license_key = $this->get_setting( 'license_key' );
		}
		if ( empty( $license_key ) ) {
			$this->flog( 'License key not found.' );
			return null;
		}
		$url      = MALCURE_API . '?edd_action=' . $action . '&item_id=1725&license=' . urlencode( $license_key ) . '&url=' . site_url() . '&cachebust=' . microtime( true );
		$response = wp_safe_remote_request( $url, array( 'timeout' => $this->timeout ) );

		$this->flog( 'License API Request: ' . $url );

		if ( is_wp_error( $response ) ) {
			$this->flog( 'API Error: ' . $response->get_error_message() );
			return null;
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		if ( 200 !== $status_code ) {
			$this->flog( 'HTTP Error: ' . $status_code );
			return null;
		}

		$body = wp_remote_retrieve_body( $response );
		$this->flog( 'License API Response Body: ' );
		$this->flog( $body );
		$data = json_decode( $body, true );

		if ( is_null( $data ) ) {
			$this->flog( 'Unparsable response: ' . $body );
			return null;
		}
		return $data;
	}

	/**
	 * Determine whether Advanced Edition features should be enabled.
	 *
	 * This validates the stored license status (cached in a transient) and refreshes
	 * it via the license API when needed.
	 * 
	 * // has_valid_license() or is_license_valid()
	 *
	 * @return bool True if the license is present and valid.
	 */
	function is_advanced_edition() {
		$status = get_transient( 'WPMR_license_status' );
		if ( ! $status ) {
			$key = $this->get_setting( 'license_key' );
			if ( empty( $key ) ) {
				return false;
			}

			$status = $this->get_license_api_response( 'check_license', $key );
			if ( is_wp_error( $status ) || empty( $status['success'] ) ) {
				return false;
			}

			$this->save_license_status( $status );
		}

		return $status['license'] === 'valid';
	}

	/**
	 * Check if the Advanced Edition license is expired.
	 *
	 * Ensures the cached status is refreshed when missing.
	 * 
	 * // is_license_expired()
	 *
	 * @return bool True if the cached license status indicates expiration.
	 */
	function is_advanced_edition_expired() {
		$this->is_advanced_edition(); // Ensure the transient is refreshed if necessary
		$status = get_transient( 'WPMR_license_status' );
		return ( ! empty( $status['license'] ) && $status['license'] === 'expired' );
	}

	/**
	 * Persist license status into a transient.
	 *
	 * @param array $status License status payload.
	 * 
	 * // cache_license_status
	 * 
	 * @return array The same status payload.
	 */
	function save_license_status( $status ) {
		// $this->flog( $status );
		set_transient( 'WPMR_license_status', $status, 24 * HOUR_IN_SECONDS );
		return $status;
	}

	/**
	 * Persist Google Search Console (GSC) OAuth status into registration settings.
	 *
	 * Hooked to `admin_init`. Reads request parameters for OAuth callback and persists
	 * a `gsc` flag inside the stored `user` setting.
	 * 
	 * // handle_gsc_oauth_callback
	 *
	 * @return void
	 */
	function save_gsc_profile() {
		if ( isset( $_REQUEST['wpmr-action'] ) && $_REQUEST['wpmr-action'] == 'oauth' ) {
			$origin_nonce = isset( $_REQUEST['origin_nonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['origin_nonce'] ) ) : '';
			if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['origin_nonce'] ) ), 'wpmr_gscapi' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- Validated via wp_verify_nonce
				return;
			}
			$success = isset( $_REQUEST['success'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['success'] ) ) : '';
			if ( current_user_can( $this->cap ) && $success ) {
				$wpmr_reg = $this->get_setting( 'user' );
				if ( $wpmr_reg ) {
					$wpmr_reg['gsc'] = true;
					$this->update_setting( 'user', $wpmr_reg );
				}
			} else {
			}
			wp_redirect( esc_url( get_admin_url( null, 'options-general.php?page=wpmr' ) ), 302 );
			exit;
		}
		if ( isset( $_REQUEST['wpmr-action'] ) && $_REQUEST['wpmr-action'] == 'revoke' && isset( $_REQUEST['success'] ) && $_REQUEST['success'] == '1' ) {
			$wpmr_reg = $this->get_setting( 'user' );
			if ( $wpmr_reg && ! empty( $wpmr_reg['gsc'] ) ) {
				unset( $wpmr_reg['gsc'] );
				$this->update_setting( 'user', $wpmr_reg );
			}
		}
	}

	/**
	 * AJAX endpoint to fetch the current license status.
	 *
	 * Returns a structured JSON response (success/error) used by the admin UI.
	 * 
	 * // ajax_fetch_license_status
	 *
	 * @return void Sends JSON response and exits.
	 */
	function ajax_get_license_status() {
		check_ajax_referer( 'wpmr_fetch_license', 'wpmr_fetch_license_nonce' );
		if ( ! current_user_can( $this->cap ) ) {
			wp_send_json_error( 'Insufficient permissions.' );
		}

		$license_status_json = $this->format_license_status_data();
		$license_data        = json_decode( $license_status_json, true );

		if ( $license_data && isset( $license_data['success'] ) && $license_data['success'] ) {
			wp_send_json_success( $license_data );
		} else {
			$error_message = isset( $license_data['error'] ) ? $license_data['error'] : 'Failed to retrieve license status';
			wp_send_json_error( $error_message );
		}
	}
}
