<?php
/**
 * ConvesioPay Webhook: Payment Method Update Events.
 *
 * Handles eventType "paymentMethods" (create, update, delete, setDefault).
 * Persists stored methods in the customer token table keyed by customerId.
 * On delete, updates parent order token for affected subscriptions when fallback is available.
 *
 * @package PeachPay
 */

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

/**
 * Handles ConvesioPay Payment Method Update webhook payloads.
 *
 * @see ConvesioPay docs: Customer Payment Method Update Event Webhook
 */
class PeachPay_ConvesioPay_Webhook_Payment_Methods {

	/**
	 * Handle ConvesioPay paymentMethods webhook.
	 *
	 * IMPORTANT:
	 * - Identity is strictly customerId from ConvesioPay.
	 * - Stored cards are persisted in the custom customer token table.
	 * - No WooCommerce user/email identity is used for card ownership decisions.
	 *
	 * @param array $webhook_data Decoded payload.
	 * @return WP_REST_Response
	 */
	public function handle( $webhook_data ) {
		$eventType               = $webhook_data['eventType'] ?? '';
		$customerId              = $webhook_data['customerId'] ?? '';
		$email                   = $webhook_data['email'] ?? '';
		$action                  = $webhook_data['action'] ?? '';
		$defaultPaymentMethodId  = $webhook_data['defaultPaymentMethodId'] ?? '';
		$affectedPaymentMethodId = $webhook_data['affectedPaymentMethodId'] ?? '';
		$storedPaymentMethods    = $webhook_data['storedPaymentMethods'] ?? array();

		peachpay_log( 'debug', wp_json_encode( array(
			'event'                    => 'convesiopay_payment_methods_webhook',
			'stage'                    => 'start',
			'customerId'               => $customerId,
			'action'                   => $action,
			'defaultPaymentMethodId'   => $defaultPaymentMethodId,
			'affectedPaymentMethodId'  => $affectedPaymentMethodId,
			'storedPaymentMethodsCount' => is_array( $storedPaymentMethods ) ? count( $storedPaymentMethods ) : 0,
		) ) );

		if ( 'paymentMethods' !== $eventType || ! is_array( $storedPaymentMethods ) ) {
			return new WP_REST_Response( array( 'error' => 'Invalid paymentMethods webhook data' ), 400 );
		}

		$allowed_actions = array( 'create', 'update', 'delete', 'setDefault' );
		if ( '' === $customerId || '' === $action || ! in_array( $action, $allowed_actions, true ) ) {
			return new WP_REST_Response( array( 'error' => 'Invalid paymentMethods webhook data' ), 400 );
		}

		$current_default = PeachPay_ConvesioPay_Customer_Token_Store::get_default_payment_method_id( $customerId );
		$row             = PeachPay_ConvesioPay_Customer_Token_Store::get_row( $customerId );
		$existing_email  = is_array( $row ) && ! empty( $row['email'] ) ? (string) $row['email'] : '';
		$email_to_save   = '' !== (string) $email ? (string) $email : $existing_email;

		// Source of truth: ConvesioPay paymentMethods webhook payload.
		// Persist the complete list exactly as provided.
		$updated_methods = array_values( $storedPaymentMethods );
		$updated_default = ( '' !== (string) $defaultPaymentMethodId ) ? (string) $defaultPaymentMethodId : (string) $current_default;

		switch ( $action ) {
			case 'delete':
				if ( ! empty( $updated_default ) && ! $this->method_exists( $updated_methods, $updated_default ) ) {
					$updated_default = '';
				}
				$this->reassign_deleted_token_for_customer_subscriptions( $customerId, $affectedPaymentMethodId, $updated_methods, $updated_default );
				break;
			case 'create':
			case 'update':
			case 'setDefault':
				// No transformation needed; canonical list already in $updated_methods.
				break;
		}

		PeachPay_ConvesioPay_Customer_Token_Store::upsert_customer_tokens(
			$customerId,
			$updated_methods,
			$email_to_save,
			$updated_default
		);

		peachpay_log( 'info', wp_json_encode( array(
			'event'                  => 'convesiopay_payment_methods_webhook',
			'stage'                  => 'end',
			'customerId'             => $customerId,
			'action'                 => $action,
			'storedPaymentMethodsCount' => count( $updated_methods ),
			'defaultPaymentMethodId' => $updated_default,
		) ) );

		return new WP_REST_Response( array( 'status' => 'success' ), 200 );
	}

	/**
	 * Check if a token exists in a method list.
	 *
	 * @param array  $methods List of methods.
	 * @param string $id      Token id.
	 * @return bool
	 */
	private function method_exists( $methods, $id ) {
		if ( '' === (string) $id ) {
			return false;
		}
		foreach ( (array) $methods as $method ) {
			if ( is_array( $method ) && isset( $method['id'] ) && (string) $method['id'] === (string) $id ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * On delete action, reassign subscriptions that used the deleted token.
	 *
	 * Selection is stored on parent order meta:
	 * _convesiopay_stored_payment_method_id
	 *
	 * @param string $customerId              ConvesioPay customerId.
	 * @param string $affectedPaymentMethodId Deleted token id.
	 * @param array  $storedPaymentMethods    Remaining methods.
	 * @param string $defaultPaymentMethodId  New default token id.
	 * @return void
	 */
	private function reassign_deleted_token_for_customer_subscriptions( $customerId, $affectedPaymentMethodId, $storedPaymentMethods, $defaultPaymentMethodId ) {
		if ( '' === (string) $affectedPaymentMethodId ) {
			return;
		}
		$replacement_token = '';
		if ( '' !== (string) $defaultPaymentMethodId && $this->method_exists( $storedPaymentMethods, $defaultPaymentMethodId ) ) {
			$replacement_token = (string) $defaultPaymentMethodId;
		} elseif ( ! empty( $storedPaymentMethods ) && is_array( $storedPaymentMethods[0] ) && ! empty( $storedPaymentMethods[0]['id'] ) ) {
			$replacement_token = (string) $storedPaymentMethods[0]['id'];
		}
		if ( '' === $replacement_token ) {
			return;
		}

		$subscriptions = $this->get_subscriptions_by_customer_id( $customerId );
		foreach ( $subscriptions as $subscription ) {
			if ( ! $subscription || ! is_callable( array( $subscription, 'get_parent_id' ) ) ) {
				continue;
			}
			$parent_order_id = (int) $subscription->get_parent_id();
			if ( $parent_order_id <= 0 ) {
				continue;
			}
			$parent_order = wc_get_order( $parent_order_id );
			if ( ! $parent_order ) {
				continue;
			}
			$current_token = (string) $parent_order->get_meta( '_convesiopay_stored_payment_method_id' );
			if ( $current_token !== (string) $affectedPaymentMethodId || $current_token === $replacement_token ) {
				continue;
			}

			$parent_order->update_meta_data( '_convesiopay_stored_payment_method_id', $replacement_token );
			// Webhook replaced the deleted token — flag as user-changed so renewal takes the native path.
			$parent_order->update_meta_data( '_pp_cpay_user_renewal_card', '1' );
			$parent_order->save();
			peachpay_log( 'info', wp_json_encode( array(
				'event'                  => 'subscription_parent_order_token_updated',
				'subscription_id'        => $subscription->get_id(),
				'parent_order_id'        => $parent_order_id,
				'old_token'              => $current_token,
				'new_token'              => $replacement_token,
				'reason'                 => 'deleted_token_fallback',
			) ) );
		}
	}

	/**
	 * Get subscriptions mapped to customerId via subscription meta.
	 *
	 * @param string $customerId ConvesioPay customerId.
	 * @return array
	 */
	private function get_subscriptions_by_customer_id( $customerId ) {
		if ( ! function_exists( 'wc_get_orders' ) ) {
			return array();
		}
		$subscriptions = array();
		$page          = 1;
		$batch_size    = 100;

		do {
			$batch = wc_get_orders( array(
				'type'       => 'shop_subscription',
				'limit'      => $batch_size,
				'paged'      => $page,
				'status'     => 'any',
				'meta_query' => array(
					array(
						'key'     => '_convesiopay_customer_id',
						'value'   => $customerId,
						'compare' => '=',
					),
				),
				'return'     => 'objects',
			) );
			$batch = is_array( $batch ) ? $batch : array();
			$subscriptions = array_merge( $subscriptions, $batch );
			$page++; 
		} while ( count( $batch ) === $batch_size );

		return $subscriptions;
	}
}
