<?php
/**
 * Paid Memberships Pro ↔ Square Hosted Checkout integration.
 *
 * @package SHC4WC
 */

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

/**
 * PMPro bridge using Square payment link redirects (same pattern as forms).
 */
class SHC4WC_PMPro {

	const RETURN_TYPE = 'pmpro';

	/** @var self|null */
	private static $instance = null;

	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		add_action( 'pmpro_membership_level_after_other_settings', array( $this, 'render_level_plan_field' ) );
		add_action( 'pmpro_save_membership_level', array( $this, 'save_level_plan_field' ), 10, 1 );
	}

	/**
	 * @return string
	 */
	public static function get_currency() {
		global $pmpro_currency;
		if ( ! empty( $pmpro_currency ) ) {
			return strtoupper( sanitize_text_field( (string) $pmpro_currency ) );
		}
		return SHC4WC_Hosted_Checkout_Flow::default_currency();
	}

	/**
	 * Start Square hosted checkout for a PMPro order.
	 *
	 * @param MemberOrder $morder PMPro order.
	 * @return true|WP_Error
	 */
	public static function start_checkout( $morder ) {
		if ( ! $morder instanceof MemberOrder ) {
			return new WP_Error( 'shc4wc_pmpro_invalid_order', __( 'Invalid membership order.', 'payments-connect-square' ) );
		}

		$error = SHC4WC_Hosted_Checkout_Flow::connection_error_message();
		if ( null !== $error ) {
			return new WP_Error( 'shc4wc_not_connected', $error );
		}

		$level = $morder->membership_level;
		if ( ! $level && ! empty( $morder->membership_id ) && function_exists( 'pmpro_getLevel' ) ) {
			$level = pmpro_getLevel( (int) $morder->membership_id );
		}

		if ( function_exists( 'pmpro_isLevelFree' ) && pmpro_isLevelFree( $level ) ) {
			$morder->status = 'success';
			$morder->saveOrder();
			return true;
		}

		$currency     = self::get_currency();
		$is_recurring = function_exists( 'pmpro_isLevelRecurring' ) && pmpro_isLevelRecurring( $level );
		$initial      = isset( $level->initial_payment ) ? (float) $level->initial_payment : (float) $morder->InitialPayment;
		$billing      = isset( $level->billing_amount ) ? (float) $level->billing_amount : (float) $morder->PaymentAmount;
		$charge_now   = $initial > 0 ? $initial : ( $is_recurring ? $billing : 0 );

		if ( $charge_now <= 0 ) {
			return new WP_Error( 'shc4wc_pmpro_invalid_amount', __( 'This membership level has no payable amount.', 'payments-connect-square' ) );
		}

		$level_name  = isset( $level->name ) ? (string) $level->name : __( 'Membership', 'payments-connect-square' );
		$description = sprintf(
			/* translators: 1: membership level name, 2: order code. */
			__( 'PMPro: %1$s (%2$s)', 'payments-connect-square' ),
			$level_name,
			$morder->code
		);

		$customer = self::extract_buyer_from_order( $morder );
		$payload  = array(
			'order_id'          => (int) $morder->id,
			'order_code'        => (string) $morder->code,
			'user_id'           => (int) $morder->user_id,
			'level_id'          => isset( $level->id ) ? (int) $level->id : (int) $morder->membership_id,
			'is_recurring'      => $is_recurring,
			'discount_code_id'  => self::get_active_discount_code_id(),
			'return_url'        => self::confirmation_url( isset( $level->id ) ? (int) $level->id : (int) $morder->membership_id ),
		);

		if ( $is_recurring ) {
			$variation_id = SHC4WC_PMPro_Plans::resolve_variation_for_level( $level, $currency );
			if ( is_wp_error( $variation_id ) ) {
				return $variation_id;
			}

			$started = SHC4WC_Hosted_Checkout_Flow::start_subscription_payment(
				array(
					'type'              => self::RETURN_TYPE,
					'amount'            => $charge_now,
					'currency'          => $currency,
					'description'       => $description,
					'line_name'         => $level_name,
					'plan_variation_id' => $variation_id,
					'customer'          => $customer,
					'payload'           => $payload,
				)
			);
		} else {
			$started = SHC4WC_Hosted_Checkout_Flow::start_one_time_payment(
				array(
					'type'        => self::RETURN_TYPE,
					'amount'      => $charge_now,
					'currency'    => $currency,
					'description' => $description,
					'line_name'   => $level_name,
					'customer'    => $customer,
					'payload'     => $payload,
				)
			);
		}

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

		set_transient(
			'shc4wc_pmpro_redirect_' . sanitize_text_field( (string) $morder->code ),
			esc_url_raw( $started['redirect'] ),
			DAY_IN_SECONDS
		);

		$morder->status      = 'pending';
		$morder->payment_type = 'Square Hosted Checkout';
		$morder->saveOrder();

		return true;
	}

	/**
	 * Complete PMPro checkout after Square hosted return.
	 *
	 * @param string $ref Reference id.
	 * @param string $key Return key.
	 * @return void
	 */
	public static function handle_return( $ref, $key ) {
		$stored = SHC4WC_Hosted_Checkout_Flow::pull_pending( self::RETURN_TYPE, $ref, $key );
		if ( ! is_array( $stored ) ) {
			wp_safe_redirect( function_exists( 'pmpro_url' ) ? pmpro_url( 'levels' ) : home_url( '/' ) );
			exit;
		}

		$order_id = isset( $stored['order_id'] ) ? (int) $stored['order_id'] : 0;
		$morder   = $order_id > 0 ? new MemberOrder( $order_id ) : new MemberOrder();

		if ( empty( $morder->id ) && ! empty( $stored['order_code'] ) ) {
			$morder->code = sanitize_text_field( (string) $stored['order_code'] );
			$morder->getMemberOrderByCode( $morder->code );
		}

		if ( empty( $morder->id ) ) {
			wp_safe_redirect( function_exists( 'pmpro_url' ) ? pmpro_url( 'levels' ) : home_url( '/' ) );
			exit;
		}

		$settings        = SHC4WC_Plugin::get_settings();
		$location_id     = isset( $settings['location_id'] ) ? trim( (string) $settings['location_id'] ) : '';
		$square_order_id = ! empty( $stored['square_order_id'] ) ? sanitize_text_field( (string) $stored['square_order_id'] ) : '';
		$payment_link_id = ! empty( $stored['link_id'] ) ? sanitize_text_field( (string) $stored['link_id'] ) : '';
		$payment_id      = '';

		if ( '' !== $square_order_id ) {
			$payment_id = SHC4WC_Plugin::instance()->retrieve_square_payment_id_for_hosted_checkout(
				$square_order_id,
				$payment_link_id,
				$location_id,
				time()
			);
		}

		$morder->payment_transaction_id = $payment_id ? $payment_id : 'square_hosted_' . sanitize_text_field( (string) $ref );
		$morder->status                 = 'success';
		$morder->payment_type           = 'Square Hosted Checkout';

		if ( ! empty( $stored['is_recurring'] ) ) {
			$subscription_id = self::lookup_subscription_id_for_reference( $ref, $morder );
			if ( '' !== $subscription_id ) {
				$morder->subscription_transaction_id = $subscription_id;
			}
		}

		$morder->saveOrder();
		self::maybe_record_discount_code_use( $stored, $morder );

		$level_id = isset( $stored['level_id'] ) ? (int) $stored['level_id'] : (int) $morder->membership_id;
		$user_id  = isset( $stored['user_id'] ) ? (int) $stored['user_id'] : (int) $morder->user_id;

		if ( $level_id > 0 && $user_id > 0 && function_exists( 'pmpro_changeMembershipLevel' ) ) {
			pmpro_changeMembershipLevel( $level_id, $user_id );
		}

		/**
		 * Fires after PMPro checkout is completed via Square hosted return.
		 *
		 * @param int         $user_id User id.
		 * @param MemberOrder $morder  PMPro order.
		 * @param array       $stored  Pending session payload.
		 */
		do_action( 'shc4wc_pmpro_checkout_completed', $user_id, $morder, $stored );
		do_action( 'pmpro_after_checkout', $user_id, $morder );

		$redirect = ! empty( $stored['return_url'] ) ? (string) $stored['return_url'] : self::confirmation_url( $level_id );
		$redirect = add_query_arg( 'shc4wc_pmpro_paid', '1', $redirect );

		wp_safe_redirect( $redirect );
		exit;
	}

	/**
	 * @param MemberOrder $morder PMPro order.
	 * @return array<string,mixed>
	 */
	public static function extract_buyer_from_order( $morder ) {
		$user_id = $morder instanceof MemberOrder ? (int) $morder->user_id : 0;
		$user    = $user_id > 0 ? get_userdata( $user_id ) : false;

		$customer = array(
			'email'       => $user ? (string) $user->user_email : '',
			'given_name'  => $user ? (string) $user->first_name : '',
			'family_name' => $user ? (string) $user->last_name : '',
			'phone'       => '',
			'wp_user_id'  => $user_id,
		);

		if ( ! empty( $_REQUEST['bemail'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$customer['email'] = sanitize_email( wp_unslash( $_REQUEST['bemail'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}
		if ( ! empty( $_REQUEST['bfirstname'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$customer['given_name'] = sanitize_text_field( wp_unslash( $_REQUEST['bfirstname'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}
		if ( ! empty( $_REQUEST['blastname'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$customer['family_name'] = sanitize_text_field( wp_unslash( $_REQUEST['blastname'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}
		if ( ! empty( $_REQUEST['bphone'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$customer['phone'] = sanitize_text_field( wp_unslash( $_REQUEST['bphone'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}
		if ( ! empty( $_REQUEST['bcountry'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$customer['country'] = sanitize_text_field( wp_unslash( $_REQUEST['bcountry'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}

		return $customer;
	}

	/**
	 * @param int $level_id Membership level id.
	 * @return string
	 */
	public static function confirmation_url( $level_id ) {
		if ( function_exists( 'pmpro_url' ) ) {
			return pmpro_url( 'confirmation', '?level=' . (int) $level_id );
		}
		return home_url( '/' );
	}

	/**
	 * @return int
	 */
	private static function get_active_discount_code_id() {
		global $discount_code_id;
		return ! empty( $discount_code_id ) ? (int) $discount_code_id : 0;
	}

	/**
	 * @param array       $stored Session payload.
	 * @param MemberOrder $morder PMPro order.
	 * @return void
	 */
	private static function maybe_record_discount_code_use( array $stored, $morder ) {
		global $wpdb;

		$discount_code_id = ! empty( $stored['discount_code_id'] ) ? (int) $stored['discount_code_id'] : 0;
		if ( $discount_code_id <= 0 || empty( $morder->user_id ) || empty( $morder->id ) ) {
			return;
		}

		$wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$wpdb->pmpro_discount_codes_uses,
			array(
				'code_id'   => $discount_code_id,
				'user_id'   => (int) $morder->user_id,
				'order_id'  => (int) $morder->id,
				'timestamp' => current_time( 'mysql' ),
			),
			array( '%d', '%d', '%d', '%s' )
		);
	}

	/**
	 * Try to resolve a Square subscription id saved during webhook processing.
	 *
	 * @param string      $ref    Hosted checkout reference.
	 * @param MemberOrder $morder PMPro order.
	 * @return string
	 */
	private static function lookup_subscription_id_for_reference( $ref, $morder ) {
		$mapped = get_transient( 'shc4wc_pmpro_subscription_' . sanitize_text_field( (string) $ref ) );
		if ( is_string( $mapped ) && '' !== $mapped ) {
			delete_transient( 'shc4wc_pmpro_subscription_' . sanitize_text_field( (string) $ref ) );
			return sanitize_text_field( $mapped );
		}

		if ( ! empty( $morder->user_id ) && class_exists( 'PMPro_Subscription' ) ) {
			$subs = PMPro_Subscription::get_subscriptions_for_user( (int) $morder->user_id, (int) $morder->membership_id, 'square_connect' );
			if ( ! empty( $subs[0] ) && is_object( $subs[0] ) && method_exists( $subs[0], 'get_subscription_transaction_id' ) ) {
				return sanitize_text_field( (string) $subs[0]->get_subscription_transaction_id() );
			}
		}

		return '';
	}

	/**
	 * Optional per-level Square plan variation picker in PMPro admin.
	 *
	 * @param int $level_id Level id.
	 * @return void
	 */
	public function render_level_plan_field( $level_id ) {
		if ( ! SHC4WC_Plugin::is_connected() || ! class_exists( 'SHC4WC_Subscriptions' ) ) {
			return;
		}

		$variations = SHC4WC_Subscriptions::get_plan_variations();
		$selected   = SHC4WC_PMPro_Plans::get_level_meta( (int) $level_id, SHC4WC_PMPro_Plans::LEVEL_META_VARIATION );
		?>
		<h3 class="topborder"><?php esc_html_e( 'Square Subscription Plan', 'payments-connect-square' ); ?></h3>
		<table class="form-table">
			<tbody>
				<tr>
					<th scope="row" valign="top">
						<label for="shc4wc_pmpro_square_plan_variation_id"><?php esc_html_e( 'Square Plan Variation', 'payments-connect-square' ); ?></label>
					</th>
					<td>
						<select name="shc4wc_pmpro_square_plan_variation_id" id="shc4wc_pmpro_square_plan_variation_id">
							<option value=""><?php esc_html_e( 'Auto-match or create from level billing cycle', 'payments-connect-square' ); ?></option>
							<?php foreach ( $variations as $variation ) : ?>
								<option value="<?php echo esc_attr( $variation['id'] ); ?>" <?php selected( $selected, $variation['id'] ); ?>>
									<?php echo esc_html( SHC4WC_Subscriptions::format_variation_label( $variation ) ); ?>
								</option>
							<?php endforeach; ?>
						</select>
						<p class="description">
							<?php esc_html_e( 'Used when this level bills recurring through Square Hosted Checkout. Leave blank to auto-map from billing amount and cycle.', 'payments-connect-square' ); ?>
						</p>
					</td>
				</tr>
			</tbody>
		</table>
		<?php
	}

	/**
	 * @param int $level_id Level id.
	 * @return void
	 */
	public function save_level_plan_field( $level_id ) {
		if ( ! isset( $_POST['shc4wc_pmpro_square_plan_variation_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			return;
		}

		$value = sanitize_text_field( wp_unslash( $_POST['shc4wc_pmpro_square_plan_variation_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		if ( '' === $value ) {
			SHC4WC_PMPro_Plans::delete_level_meta( (int) $level_id, SHC4WC_PMPro_Plans::LEVEL_META_VARIATION );
			return;
		}

		SHC4WC_PMPro_Plans::update_level_meta( (int) $level_id, SHC4WC_PMPro_Plans::LEVEL_META_VARIATION, $value );
	}
}
