<?php
/**
 * Subscription Handler Trait
 *
 * Handles subscription-related functionality for the Square payment gateway.
 *
 * @package FunnelKit_Square
 * @subpackage Traits
 */

namespace FKWCSQ\Includes\Traits;

use Exception;
use FKPay\Includes\API\WC;
use FKWCSQ\Includes\Conversions\Conversion;
use WC_Order;
use WP_Error;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/**
 * Trait Subscription_Handler
 */
trait Subscription {

	/**
	 * Initialize subscription hooks
	 *
	 * @return void
	 */
	/**
	 * Get subscription support features
	 *
	 * @return array Array of subscription features supported by this gateway
	 */
	protected function get_subscription_support_features() {
		return array(
			'subscriptions',
			'subscription_cancellation',
			'subscription_suspension',
			'subscription_reactivation',
			'subscription_amount_changes',
			'subscription_date_changes',
			'subscription_payment_method_change',
			'subscription_payment_method_change_customer',
			'subscription_payment_method_change_admin',
			'multiple_subscriptions',
		);
	}

	/**
	 * Initialize subscription support
	 *
	 * @return void
	 */
	protected function init_subscription_support() {
		if ( empty( $this->supports ) ) {
			$this->supports = array();
		}
		$this->supports = array_merge( $this->supports, $this->get_subscription_support_features() );
	}

	/**
	 * Initialize subscription hooks
	 *
	 * @return void
	 */
	protected function init_subscription_hooks() {
		if ( ! $this->is_subscriptions_enabled() ) {
			return;
		}

		add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'process_subscription_renewal_payment' ), 10, 2 );
		add_action( 'woocommerce_subscription_failing_payment_method_updated_' . $this->id, array( $this, 'update_failing_payment_method' ), 10, 2 );
		add_filter( 'woocommerce_my_subscriptions_payment_method', array( $this, 'maybe_render_subscription_payment_method' ), 10, 2 );
		add_filter( 'fkwcsq_square_display_save_payment_method_checkbox', array( $this, 'display_save_payment_method_checkbox' ) );
	}
	/**
	 * Returns a boolean value indicating whether the save payment checkbox should be
	 * displayed during checkout.
	 *
	 * Returns `false` if the cart currently has a subscriptions or if the request has a
	 * `change_payment_method` GET parameter. Returns the value in `$display` otherwise.
	 *
	 * @param bool $display Bool indicating whether to show the save payment checkbox in the absence of subscriptions.
	 *
	 * @return bool Indicates whether the save payment method checkbox should be displayed or not.
	 */
	public function display_save_payment_method_checkbox( $display ) {
		if ( \WC_Subscriptions_Cart::cart_contains_subscription() || $this->is_changing_payment_method_for_subscription() ) {
			return false;
		}

		// Only render the "Save payment method" checkbox if there are no subscription products in the cart.
		return $display;
	}
	public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
		// Only process if this is our Square gateway
		if ( $subscription->get_payment_method() !== 'fkwcsq_square' ) {
			return $payment_method_to_display;
		}

		// Try to get card details from subscription meta
		$card_last4 = $subscription->get_meta( '_square_card_last4' );

		// If no last4 in subscription meta, try to get from the parent order
		if ( empty( $card_last4 ) ) {
			$parent_order = $subscription->get_parent();
			if ( $parent_order ) {
				$card_last4 = $parent_order->get_meta( '_square_card_last4' );
			}
		}

		// If we have last4, format the display like Stripe does
		if ( ! empty( $card_last4 ) ) {
			/* translators: %s: last 4 digits of the credit card */
			$payment_method_to_display = sprintf( __( 'Via Credit Card (Square) ending in %s', 'funnelkit-payment-gateway-square-for-woocommerce' ), $card_last4 );
		}

		return $payment_method_to_display;
	}
	public function is_subscriptions_enabled() {
		return class_exists( 'WC_Subscriptions' ) && version_compare( \WC_Subscriptions::$version, '2.2.0', '>=' );
	}

	/**
	 * Maybe process payment method change for subscriptions.
	 *
	 * @param int $order_id
	 *
	 * @return bool
	 */
	public function maybe_change_subscription_payment_method( $order_id ) {
		return ( $this->is_subscriptions_enabled() && $this->has_subscription( $order_id ) && $this->is_changing_payment_method_for_subscription( $order_id ) );
	}

	/**
	 * Is $order_id a subscription?
	 *
	 * @param int $order_id
	 *
	 * @return boolean
	 * @since 5.6.0
	 */
	public function has_subscription( $order_id ) {
		return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
	}


	/**
	 * @param $order WC_Order
	 *
	 * @return array|void
	 */
	public function process_change_subscription_payment_method( $order ) {
		try {

			$verification_token = isset( $_POST['fkwcsq_verification_token'] ) ? sanitize_text_field( wp_unslash( $_POST['fkwcsq_verification_token'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing, FKWCS.CodeAnalysis.FKWCSSpecific.MissingCapabilityCheck -- WooCommerce handles nonce verification for payment method changes, guest users allowed
			if ( isset( $_POST['wc-fkwcsq_square-payment-token'] ) && $_POST['wc-fkwcsq_square-payment-token'] !== 'new' ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing, FKWCS.CodeAnalysis.FKWCSSpecific.MissingCapabilityCheck -- WooCommerce handles nonce verification for payment method changes, guest users allowed
				$token_id = sanitize_text_field( wp_unslash( $_POST['wc-fkwcsq_square-payment-token'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Missing, FKWCS.CodeAnalysis.FKWCSSpecific.MissingCapabilityCheck -- WooCommerce handles nonce verification for payment method changes, guest users allowed
				$token    = \WC_Payment_Tokens::get( $token_id );
				if ( ! $token || $token->get_user_id() !== get_current_user_id() ) {
					throw new Exception( __( 'Invalid payment token.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
				}
				// Get Square customer ID and card ID from token
				$customer_id = get_user_meta( get_current_user_id(), '_square_customer_id', true );

				// If we don't have a customer ID stored in user meta, try from token meta
				if ( empty( $customer_id ) ) {
					$customer_id = $token->get_meta( '_square_customer_id' );
				}
				// Get the card ID from token - this should be the token itself for Square
				$payment_nonce = $token->get_token();
				$order->update_meta_data( '_square_customer_id', $customer_id );
				$order->update_meta_data( '_square_payment_token', $payment_nonce );
				// Get last4 from token meta if available
				$last4 = $token->get_meta( 'last4' );
				if ( ! empty( $last4 ) ) {
					$order->update_meta_data( '_square_card_last4', $last4 );
				}
				$order->save_meta_data();
				$order->payment_complete();

				return array(
					'result'   => 'success',
					'redirect' => $this->get_return_url( $order ),
				);

			}

			$payment_nonce = $this->get_payment_nonce();
			$customer_id   = $this->get_or_create_customer( $order );
			if ( empty( $customer_id ) ) {
				throw new Exception( 'Failed to create customer' );
			}
			$card_data = $this->prepare_card_data(
				$order,
				array(
					'id'                 => $payment_nonce,
					'verification_token' => $verification_token,
				),
				$customer_id
			);

			$response = $this->api->create_card( $card_data );
			if ( is_wp_error( $response ) ) {
				$this->log_error( 'Error from Square Cards API: ' . $response->get_error_message() );
				throw new Exception( 'Error from Square Cards API: ' . $response->get_error_message() );
			} elseif ( ! isset( $response['card'] ) || ! isset( $response['card']['id'] ) ) {
				$this->log_error( 'Invalid response from Square Cards API: ' . wp_json_encode( $response, true ) );
				throw new Exception( 'Invalid response from Square Cards API' );
			}
			$square_card = $response['card'];

			$order->update_meta_data( '_square_customer_id', $customer_id );
			$order->update_meta_data( '_square_payment_token', $square_card['id'] );
			$order->update_meta_data( '_square_card_last4', $square_card['last_4'] );
			$order->save_meta_data();
			$order->payment_complete();
			if ( $this->tokenization && is_user_logged_in() && isset( $_POST['fkwcsq_square_save_card'] ) && $_POST['fkwcsq_square_save_card'] === 'true' ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing, FKWCS.CodeAnalysis.FKWCSSpecific.MissingCapabilityCheck -- WooCommerce handles nonce verification for payment method changes, guest users allowed
				// Create WooCommerce payment token
				$token = $this->create_wc_token( $square_card, $square_card, $customer_id );
				// Add token to order
				$order->add_payment_token( $token );
			}

			return array(
				'result'   => 'success',
				'redirect' => $this->get_return_url( $order ),
			);
		} catch ( \Exception $e ) {
			wc_add_notice( $e->getMessage(), 'error' );
		}
	}

	/**
	 * Process subscription renewal payment
	 *
	 * @param float    $amount The amount to charge.
	 * @param WC_Order $renewal_order The renewal order.
	 *
	 * @return void
	 */
	public function process_subscription_renewal_payment( $amount, $renewal_order ) {
		try {
			$this->log_debug( sprintf( 'Processing subscription renewal payment for order %d', $renewal_order->get_id() ) );

			// Get the subscription from the renewal order
			$subscriptions = wcs_get_subscriptions_for_renewal_order( $renewal_order );
			$subscription  = reset( $subscriptions );

			if ( ! $subscription ) {
				throw new Exception( 'Failed to find subscription for renewal order' );
			}

			$convertedController = \FKWCSQ\Includes\Conversions\Controller::get_instance();

			if ( ! empty( $convertedController->get_payment_method() ) ) {
				$convertedController->set_data( $renewal_order, $subscription );

				$token_data = \FKWCSQ\Includes\Conversions\Controller::get_instance()->get_token_source();

			} else {
				// Fallback to default token retrieval
				$token_data = array(
					'token'       => $this->get_token_id_for_subscription( $renewal_order, $subscription ),
					'customer_id' => $this->get_customer_id_for_subscription( $renewal_order, $subscription ),
					'location_id' => $this->api->get_location_id(),
				);
			}

			// Get the stored payment token
			$token_id = $token_data['token'];

			if ( empty( $token_id ) ) {
				throw new Exception( 'No stored payment token found for subscription renewal' );
			}

			// Get customer ID
			$customer_id = $token_data['customer_id'];

			if ( empty( $customer_id ) ) {
				throw new Exception( 'No Square customer ID found for subscription renewal' );
			}

			// Format amount and currency
			$amount   = (int) round( $amount * 100 ); // Convert to cents
			$currency = $renewal_order->get_currency();

			// Get location ID
			$location_id = $token_data['location_id'];

			// Prepare payment data
			$payment_data = array(
				'source_id'           => $token_id,
				'customer_id'         => $customer_id, // Required when using stored card token
				'idempotency_key'     => uniqid( 'fkwcsq_renewal_' . $renewal_order->get_id() . '_', true ),
				'amount_money'        => array(
					'amount'   => $amount,
					'currency' => $currency,
				),
				'location_id'         => $location_id,
				'reference_id'        => $this->api->get_reference_id( $renewal_order->get_order_number() ),
				'buyer_email_address' => $renewal_order->get_billing_email(),
				'billing_address'     => $this->format_address( $renewal_order, 'billing' ),
				'note'                => sprintf( 'Subscription Renewal Payment for Order #%s', $renewal_order->get_id() ),
				'autocomplete'        => true,
				'is_renewal'          => true, // Indicate this is a renewal payment
			);

			// Add billing address if available
			if ( $renewal_order->get_billing_address_1() ) {
				$payment_data['billing_address'] = $this->format_address( $renewal_order, 'billing' );
			}

			// Add shipping address if needed
			if ( $renewal_order->needs_shipping_address() ) {
				$payment_data['shipping_address'] = $this->format_address( $renewal_order, 'shipping' );
			}

			$this->log_debug( 'Renewal payment data for order #' . $renewal_order->get_id() . ': ' . wp_json_encode( $payment_data, true ) );

			// Process the payment
			$response = $this->api->create_payment( $payment_data );

			if ( is_wp_error( $response ) ) {
				throw new Exception( $response->get_error_message() );
			}

			// Handle the response
			if ( isset( $response['payment'] ) && in_array( $response['payment']['status'], array( 'COMPLETED', 'APPROVED' ), true ) ) {
				$payment = $response['payment'];

				// Payment successful
				$renewal_order->payment_complete( $payment['id'] );


				// Store payment details
				$renewal_order->update_meta_data( '_square_payment_id', $payment['id'] );
				$renewal_order->update_meta_data( '_square_version_token', $payment['version_token'] );
				$renewal_order->update_meta_data( '_square_receipt_url', $payment['receipt_url'] );

				// Store card details
				if ( ! empty( $payment['card_details']['card'] ) ) {
					$card = $payment['card_details']['card'];
					$renewal_order->update_meta_data( '_square_card_brand', $card['card_brand'] );
					$renewal_order->update_meta_data( '_square_card_last4', $card['last_4'] );
					$renewal_order->update_meta_data( '_square_card_exp', sprintf( '%02d/%d', $card['exp_month'], $card['exp_year'] ) );
					$renewal_order->update_meta_data( '_square_card_type', $card['card_type'] );
				}

				// Store transaction details
				if ( ! empty( $payment['card_details']['card_payment_timeline'] ) ) {
					$renewal_order->update_meta_data( '_square_authorized_at', $payment['card_details']['card_payment_timeline']['authorized_at'] );
					$renewal_order->update_meta_data( '_square_captured_at', $payment['card_details']['card_payment_timeline']['captured_at'] );
				}

				// Store risk evaluation
				if ( ! empty( $payment['risk_evaluation'] ) ) {
					$renewal_order->update_meta_data( '_square_risk_level', $payment['risk_evaluation']['risk_level'] );
				}

				// Save all changes
				$renewal_order->save();

				$this->log_debug( sprintf( 'Subscription renewal payment processed successfully for order %d. Payment ID: %s', $renewal_order->get_id(), $payment['id'] ) );
			} else {
				throw new Exception( 'Payment was not completed' );
			}
		} catch ( Exception $e ) {
			$this->log_error( sprintf( 'Failed to process subscription renewal payment: %s', $e->getMessage() ) );

			// Mark renewal order as failed
			$renewal_order->update_status( 'failed', sprintf( 'Square subscription renewal payment failed: %s', $e->getMessage() ) );

			// Add subscription note
			if ( isset( $subscription ) ) {
				$subscription->add_order_note( sprintf( 'Square renewal payment failed: %s', $e->getMessage() ) );
			}
		}
	}

	/**
	 * Update the failing payment method for a subscription
	 *
	 * @param \WC_Subscription $subscription The subscription for which the payment method is being updated.
	 * @param WC_Order         $renewal_order The order which is used to update the payment method.
	 *
	 * @return void
	 */
	public function update_failing_payment_method( $subscription, $renewal_order ) {
		try {
			$has_changes = false;

			// Get new token ID from renewal order
			$token_id = $this->get_token_id_for_subscription( $renewal_order, null );
			if ( ! empty( $token_id ) ) {
				$current_token_id = (string) $subscription->get_meta( '_square_payment_token' );
				if ( $current_token_id !== (string) $token_id ) {
					$subscription->update_meta_data( '_square_payment_token', $token_id );
					$has_changes = true;
				}
			}

			// Get new customer ID from renewal order
			$customer_id = $this->get_customer_id_for_subscription( $renewal_order, null );
			if ( ! empty( $customer_id ) ) {
				$current_customer_id = (string) $subscription->get_meta( '_square_customer_id' );
				if ( $current_customer_id !== (string) $customer_id ) {
					$subscription->update_meta_data( '_square_customer_id', $customer_id );
					$has_changes = true;
				}
			}

			if ( $has_changes ) {
				$subscription->save();
			}

			$this->log_debug( sprintf( 'Updated failing payment method for subscription %d', $subscription->get_id() ) );
		} catch ( Exception $e ) {
			$this->log_error( sprintf( 'Failed to update failing payment method: %s', $e->getMessage() ) );
		}
	}

	/**
	 * Check if order is a subscription renewal order
	 *
	 * @param WC_Order $order The order to check.
	 *
	 * @return bool
	 */
	protected function is_subscription_renewal_order( $order ) {
		return function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order );
	}

	/**
	 * Check if order contains subscription
	 *
	 * @param WC_Order $order The order to check.
	 *
	 * @return bool
	 */
	protected function order_contains_subscription( $order ) {
		return function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order );
	}

	/**
	 * Get token ID for subscription renewal
	 *
	 * @param WC_Order $renewal_order The renewal order
	 * @param \WC_Subscription $subscription The subscription
	 *
	 * @return string|null Token ID or null if not found
	 */
	/**
	 * Get meta value from subscription-related orders
	 *
	 * @param WC_Order              $renewal_order The renewal order
	 * @param \WC_Subscription|null $subscription The subscription
	 * @param string                $meta_key The meta key to retrieve
	 *
	 * @return string|null Meta value or null if not found
	 */
	protected function get_subscription_meta_value( $renewal_order, $subscription, $meta_key ) {
		// Try renewal order first
		$value = $renewal_order->get_meta( $meta_key );
		if ( ! empty( $value ) ) {
			return $value;
		}

		// Then try subscription if available
		if ( $subscription ) {
			$value = $subscription->get_meta( $meta_key );
			if ( ! empty( $value ) ) {
				return $value;
			}

			// Try parent order if subscription exists
			$parent_order = wc_get_order( $subscription->get_parent_id() );
			if ( $parent_order ) {
				$value = $parent_order->get_meta( $meta_key );
				if ( ! empty( $value ) ) {
					return $value;
				}
			}
		}

		return null;
	}

	/**
	 * Get token ID for subscription renewal
	 *
	 * @param WC_Order         $renewal_order The renewal order
	 * @param \WC_Subscription $subscription The subscription
	 *
	 * @return string|null Token ID or null if not found
	 */
	protected function get_token_id_for_subscription( $renewal_order, $subscription ) {
		return $this->get_subscription_meta_value( $renewal_order, $subscription, '_square_payment_token' );
	}

	/**
	 * Get customer ID for subscription renewal
	 *
	 * @param WC_Order         $renewal_order The renewal order
	 * @param \WC_Subscription $subscription The subscription
	 *
	 * @return string|null Customer ID or null if not found
	 */
	protected function get_customer_id_for_subscription( $renewal_order, $subscription ) {
		return $this->get_subscription_meta_value( $renewal_order, $subscription, '_square_customer_id' );
	}

	/**
	 * Get customer ID for subscription renewal
	 *
	 * @param WC_Order         $renewal_order The renewal order
	 * @param \WC_Subscription $subscription The subscription
	 *
	 * @return string|null Customer ID or null if not found
	 */
	protected function get_location_id_for_subscription( $renewal_order, $subscription ) {
		return $this->get_subscription_meta_value( $renewal_order, $subscription, '_square_location_id' );
	}
}
