<?php
/**
 * ConvesioPay customer token storage.
 *
 * Canonical card identity is customerId from ConvesioPay. This store keeps
 * one row per customerId regardless of WordPress user relationships.
 *
 * @package PeachPay
 */

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

/**
 * Persistence layer for ConvesioPay stored payment methods keyed by customerId.
 */
final class PeachPay_ConvesioPay_Customer_Token_Store {

	/**
	 * Guard for ensuring table at most once per request.
	 *
	 * @var bool
	 */
	private static $table_ensured = false;

	/**
	 * Register lifecycle hooks for table creation.
	 *
	 * @return void
	 */
	public static function init_hooks() {
		add_action( 'peachpay_plugin_activated', array( __CLASS__, 'ensure_table' ) );
		add_action( 'peachpay_plugin_upgraded', array( __CLASS__, 'ensure_table' ) );
	}

	/**
	 * Get fully-qualified table name.
	 *
	 * @return string
	 */
	public static function table_name() {
		global $wpdb;
		return $wpdb->prefix . 'peachpay_convesiopay_customer_stored_payment_tokens';
	}

	/**
	 * Ensure table exists.
	 *
	 * @return void
	 */
	public static function ensure_table() {
		if ( self::$table_ensured ) {
			return;
		}
		global $wpdb;
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';

		$table_name      = self::table_name();
		$charset_collate = $wpdb->get_charset_collate();
		$sql             = "CREATE TABLE {$table_name} (
			customer_id VARCHAR(191) NOT NULL,
			stored_payment_methods LONGTEXT NOT NULL,
			email VARCHAR(191) NOT NULL DEFAULT '',
			default_payment_method_id VARCHAR(191) NOT NULL DEFAULT '',
			updated_at DATETIME NOT NULL,
			PRIMARY KEY (customer_id)
		) {$charset_collate};";

		dbDelta( $sql );
		self::$table_ensured = true;
	}

	/**
	 * Get full row by customerId.
	 *
	 * @param string $customerId ConvesioPay customerId.
	 * @return array|null
	 */
	public static function get_row( $customerId ) {
		self::ensure_table();
		if ( '' === (string) $customerId ) {
			return null;
		}
		global $wpdb;
		$table_name = self::table_name();
		$row        = $wpdb->get_row(
			$wpdb->prepare( "SELECT customer_id, stored_payment_methods, email, default_payment_method_id, updated_at FROM {$table_name} WHERE customer_id = %s", (string) $customerId ),
			ARRAY_A
		);
		return is_array( $row ) ? $row : null;
	}

	/**
	 * Get decoded stored_payment_methods for a customerId.
	 *
	 * @param string $customerId ConvesioPay customerId.
	 * @return array
	 */
	public static function get_stored_payment_methods( $customerId ) {
		$row = self::get_row( $customerId );
		if ( empty( $row ) || ! isset( $row['stored_payment_methods'] ) ) {
			return array();
		}
		$decoded = json_decode( (string) $row['stored_payment_methods'], true );
		return is_array( $decoded ) ? $decoded : array();
	}

	/**
	 * Get default payment method id by customerId.
	 *
	 * @param string $customerId ConvesioPay customerId.
	 * @return string
	 */
	public static function get_default_payment_method_id( $customerId ) {
		$row = self::get_row( $customerId );
		if ( empty( $row['default_payment_method_id'] ) ) {
			return '';
		}
		return (string) $row['default_payment_method_id'];
	}

	/**
	 * Upsert full customer token state.
	 *
	 * @param string $customerId             ConvesioPay customerId.
	 * @param array  $storedPaymentMethods   Full storedPaymentMethods array.
	 * @param string $email                  Email from payload.
	 * @param string $defaultPaymentMethodId defaultPaymentMethodId from payload.
	 * @return bool
	 */
	public static function upsert_customer_tokens( $customerId, $storedPaymentMethods, $email = '', $defaultPaymentMethodId = '' ) {
		self::ensure_table();
		if ( '' === (string) $customerId ) {
			return false;
		}
		global $wpdb;
		$table_name = self::table_name();
		$result     = $wpdb->replace(
			$table_name,
			array(
				'customer_id'               => (string) $customerId,
				'stored_payment_methods'    => wp_json_encode( array_values( is_array( $storedPaymentMethods ) ? $storedPaymentMethods : array() ) ),
				'email'                     => (string) $email,
				'default_payment_method_id' => (string) $defaultPaymentMethodId,
				'updated_at'                => current_time( 'mysql', 1 ),
			),
			array( '%s', '%s', '%s', '%s', '%s' )
		);
		return false !== $result;
	}

	/**
	 * Extract brand and last-four from payment webhook paymentMethodDetails.
	 * ConvesioPay payment webhooks use lastFour; some payloads may use last4.
	 *
	 * @param array $paymentMethodDetails payload.paymentMethodDetails.
	 * @return array{brand: string, lastFour: string}
	 */
	private static function extract_payment_method_display_fields( $paymentMethodDetails ) {
		$brand = '';
		if ( is_array( $paymentMethodDetails ) && isset( $paymentMethodDetails['brand'] ) ) {
			$brand = (string) $paymentMethodDetails['brand'];
		}

		$last_four = '';
		if ( is_array( $paymentMethodDetails ) ) {
			if ( isset( $paymentMethodDetails['lastFour'] ) && '' !== (string) $paymentMethodDetails['lastFour'] ) {
				$last_four = (string) $paymentMethodDetails['lastFour'];
			} elseif ( isset( $paymentMethodDetails['last4'] ) && '' !== (string) $paymentMethodDetails['last4'] ) {
				$last_four = (string) $paymentMethodDetails['last4'];
			}
		}

		return array(
			'brand'    => $brand,
			'lastFour' => $last_four,
		);
	}

	/**
	 * Merge display fields onto an existing stored method entry when webhook provides them.
	 *
	 * @param array $existing Existing method row.
	 * @param array $incoming brand/lastFour from extract_payment_method_display_fields().
	 * @return array
	 */
	private static function merge_token_display_fields( array $existing, array $incoming ) {
		if ( '' !== $incoming['brand'] ) {
			$existing['brand'] = $incoming['brand'];
		} elseif ( ! isset( $existing['brand'] ) ) {
			$existing['brand'] = '';
		}

		if ( '' !== $incoming['lastFour'] ) {
			$existing['lastFour'] = $incoming['lastFour'];
		} elseif ( ! isset( $existing['lastFour'] ) ) {
			$existing['lastFour'] = '';
		}

		return $existing;
	}

	/**
	 * Upsert token list from payment webhook payload fields.
	 *
	 * @param string $customerId           payload.customerId
	 * @param string $email                payload.email
	 * @param array  $paymentMethodDetails payload.paymentMethodDetails
	 * @return bool
	 */
	public static function upsert_from_payment_webhook( $customerId, $email, $paymentMethodDetails ) {
		$storedPaymentMethodId = $paymentMethodDetails['storedPaymentMethodId'] ?? '';
		if ( '' === (string) $customerId || '' === (string) $storedPaymentMethodId ) {
			return false;
		}

		$display_fields         = self::extract_payment_method_display_fields( $paymentMethodDetails );
		$stored_payment_methods = self::get_stored_payment_methods( $customerId );
		$token_exists           = false;

		foreach ( $stored_payment_methods as $index => $method ) {
			if ( ! is_array( $method ) || ! isset( $method['id'] ) || (string) $method['id'] !== (string) $storedPaymentMethodId ) {
				continue;
			}

			$token_exists = true;
			$stored_payment_methods[ $index ] = self::merge_token_display_fields(
				array(
					'id'       => (string) $storedPaymentMethodId,
					'brand'    => isset( $method['brand'] ) ? (string) $method['brand'] : '',
					'lastFour' => isset( $method['lastFour'] ) ? (string) $method['lastFour'] : '',
				),
				$display_fields
			);
			break;
		}

		if ( ! $token_exists ) {
			$stored_payment_methods[] = array(
				'id'       => (string) $storedPaymentMethodId,
				'brand'    => $display_fields['brand'],
				'lastFour' => $display_fields['lastFour'],
			);
		}

		$current_default = self::get_default_payment_method_id( $customerId );
		return self::upsert_customer_tokens(
			$customerId,
			$stored_payment_methods,
			(string) $email,
			(string) $current_default
		);
	}
}

PeachPay_ConvesioPay_Customer_Token_Store::init_hooks();
