<?php
/**
 * Online solution sync payment methods.
 *
 * @package northmill-online-shared
 */

declare(strict_types=1);

namespace Northmill_Online_Shared\Modules\Admin\Pages\Page\Admin_Sync_Settings\Sync;

use InvalidArgumentException;
use Northmill_Online_Shared\Abstract_Shared_Base;
use Northmill_Online_Shared\Data\Payment_Methods;
use Northmill_Online_Shared\Data\Payment_Methods\Enums\Settings_Mode;
use Northmill_Online_Shared\Helpers\General_Helpers;
use Northmill_Online_Shared\Modules\Admin\Pages\Page\Admin_Sync_Settings\Models\Properties\Settings_Payment_Method_Model;
use Northmill_Online_Shared\Modules\Loggers\Log_Level_Enum;
use WC_Payment_Gateway;

defined( 'ABSPATH' ) || exit;

/**
 * Class Online_Solution_Sync_Payment_Methods
 */
readonly class Online_Solution_Sync_Payment_Methods {

	/**
	 * Get the payment methods.
	 *
	 * @param string[] $valid_payment_method_ids IDs of payment methods valid for this site.
	 *
	 * @used Northmill Express Order
	 */
	public function get_payment_methods( array $valid_payment_method_ids ): array {
		$payment_methods = array();

		foreach ( WC()->payment_gateways->payment_gateways() as $id => $gateway ) {
			if ( ! in_array( $id, $valid_payment_method_ids, true ) ) {
				continue;
			}

			$payment_methods[] = $this->create_from_wp( $gateway );
		}

		return $payment_methods;
	}

	/**
	 * Create a payment method model from a WooCommerce payment gateway.
	 *
	 * @param WC_Payment_Gateway $gateway The gateway to create from.
	 *
	 * @return Settings_Payment_Method_Model|null The payment method model on success, else null.
	 */
	public function create_from_wp( WC_Payment_Gateway $gateway ): ?Settings_Payment_Method_Model {
		$is_enabled           = 'yes' === $gateway->get_option( Payment_Methods::GATEWAY_SETTING_ENABLED, 'no' );
		$settings_title       = $gateway->get_option( Payment_Methods::GATEWAY_SETTING_TITLE, '' );
		$settings_description = $gateway->get_option( Payment_Methods::GATEWAY_SETTING_DESC, '' );
		$settings_username    = '';
		$settings_password    = '';
		$settings_key         = '';

		switch ( $gateway->id ) {
			case Payment_Methods::NMC_BNPL: // TODO: Add setting updates if implemented.
			case Payment_Methods::NMC_SHIFT4_CARD: // TODO: Add setting updates.
			case Payment_Methods::NMC_MF_INVOICE:
			case Payment_Methods::NMC_SWISH:
			case Payment_Methods::WC_COD:
				break;

			case Payment_Methods::NMC_APPLE_PAY:
				$settings_username = $this->get_option( $gateway, 'merchant_id', '' );
				$settings_password = $this->get_option( $gateway, 'secret', '' );
				// TODO: Add merchant sub id, billing descriptor and possibly payment networks.
				break;

			case Payment_Methods::NMC_RESURS_INVOICE:
				$settings_username = $this->get_option( $gateway, 'live_merchant_id', '' );
				$settings_password = $this->get_option( $gateway, 'live_password', '' );
				break;

			case Payment_Methods::NMC_NMI:
				$settings_username = $this->get_option( $gateway, 'live_terminal_id', '' );
				$settings_password = $this->get_option( $gateway, 'live_transaction_key', '' );
				$settings_key      = $this->get_option( $gateway, 'live_hash_key', '' );
				break;
		}

		try {
			return new Settings_Payment_Method_Model(
				$is_enabled,
				$settings_title,
				$settings_description,
				$settings_username,
				$settings_password,
				$settings_key,
				null,
				$gateway->id
			);
		} catch ( InvalidArgumentException $e ) {
			Abstract_Shared_Base::base_instance()->logger->log(
				'online-solutions',
				'Error creating payment method model',
				array(
					'gateway_id' => $gateway->id,
					'error'      => $e->getMessage(),
				),
				Log_Level_Enum::ERROR
			);
			return null;
		}
	}

	/**
	 * Get a gateway option.
	 *
	 * @param WC_Payment_Gateway $gateway     The gateway.
	 * @param string             $option_name The option name, as defined in Northmill_Online_Shared\Data\Payment_Methods class.
	 * @param mixed              $empty_value The value to return if the option is not set.
	 *
	 * @return mixed
	 */
	private function get_option( WC_Payment_Gateway $gateway, string $option_name, mixed $empty_value = null ): mixed {
		$option_key = Payment_Methods::get_option_key( $gateway->id, $option_name );
		return $gateway->get_option( $option_key, $empty_value );
	}


	/**
	 * Update payment methods.
	 * Mainly used for updating payment methods synced from Hub.
	 *
	 * @param string[]                        $valid_payment_method_ids IDs of payment methods valid for this site.
	 * @param Settings_Payment_Method_Model[] $payment_methods          Payment methods retrieved from Hub.
	 *
	 * @used Northmill Express Order
	 */
	public function update_payment_methods( array $valid_payment_method_ids, array $payment_methods ): void {

		foreach ( WC()->payment_gateways->payment_gateways() as $id => $wc_gateway ) {
			if ( ! in_array( $id, $valid_payment_method_ids, true ) ) {
				continue;
			}

			$hub_payment_method_type = Payment_Methods::get_online_solution_type( $wc_gateway->id );
			if ( is_null( $hub_payment_method_type ) ) {
				continue;
			}

			$hub_payment_method_model = $this->get_payment_method_of_type( $hub_payment_method_type, $payment_methods );
			if ( ! $hub_payment_method_model ) {
				$this->disable_payment_method( $wc_gateway );
				continue;
			}

			$this->update_payment_method( $wc_gateway, $hub_payment_method_model );
		}
	}

	/**
	 * Update a payment method with the settings from Hub.
	 *
	 * @param WC_Payment_Gateway            $gateway        The gateway to update.
	 * @param Settings_Payment_Method_Model $payment_method The payment method data from Hub.
	 */
	private function update_payment_method( WC_Payment_Gateway $gateway, Settings_Payment_Method_Model $payment_method ): void {
		$gateway->update_option( Payment_Methods::GATEWAY_SETTING_ENABLED, 'yes' );
		$gateway->update_option( Payment_Methods::GATEWAY_SETTING_TITLE, $payment_method->get_settings_title() );
		$gateway->update_option( Payment_Methods::GATEWAY_SETTING_DESC, $payment_method->get_settings_description() );

		switch ( $gateway->id ) {
			case Payment_Methods::NMC_BNPL: // TODO: Add setting updates if implemented.
			case Payment_Methods::NMC_SHIFT4_CARD: // TODO: Add setting updates.
			case Payment_Methods::NMC_MF_INVOICE:
			case Payment_Methods::WC_COD:
				// No settings yet.
				break;

			case Payment_Methods::NMC_APPLE_PAY:
				if ( ! General_Helpers::get_constant( 'NM_SHARED_PREVENT_PAYMENT_GATEWAY_ENV_SYNC', false ) ) {
					$this->update_option( $gateway, 'mode', Settings_Mode::PRODUCTION->value );
				}
				$this->update_option( $gateway, 'merchant_id', $payment_method->get_settings_username() );
				$this->update_option( $gateway, 'secret', $payment_method->get_settings_password() );
				// TODO: Add merchant sub id, billing descriptor and possibly payment networks.

				$this->update_option( $gateway, 'store_name', get_option( 'blogname', '' ) );
				$this->update_option( $gateway, 'log_enabled', 'yes' );
				break;

			case Payment_Methods::NMC_RESURS_INVOICE:
				if ( ! General_Helpers::get_constant( 'NM_SHARED_PREVENT_PAYMENT_GATEWAY_ENV_SYNC', false ) ) {
					$gateway->update_option( 'sandbox', 'no' );
				}
				$gateway->update_option( 'live_merchant_id', $payment_method->get_settings_username() );
				$gateway->update_option( 'live_password', $payment_method->get_settings_password() );
				$gateway->update_option( 'log_enabled', 'yes' );
				break;

			case Payment_Methods::NMC_NMI:
				$gateway->update_option( 'live_terminal_id', $payment_method->get_settings_username() );
				$gateway->update_option( 'live_transaction_key', $payment_method->get_settings_password() );
				$gateway->update_option( 'live_hash_key', $payment_method->get_settings_key() );
				$gateway->update_option( 'log_enabled', 'yes' );
				$gateway->update_option( 'sandbox', 'no' );
				break;

			case Payment_Methods::NMC_SWISH:
				$gateway->update_option( 'payee_number', $payment_method->get_settings_username() );
				break;
		}
	}

	/**
	 * Update a gateway setting.
	 *
	 * @param WC_Payment_Gateway $gateway     The gateway to update.
	 * @param string             $option_name The option name, as defined in Northmill_Online_Shared\Data\Payment_Methods class.
	 * @param mixed              $value       The value to set.
	 *
	 * @return void
	 */
	private function update_option( WC_Payment_Gateway $gateway, string $option_name, mixed $value ): void {
		$option_key = Payment_Methods::get_option_key( $gateway->id, $option_name );
		$gateway->update_option( $option_key, $value );
	}

	/**
	 * Disable payment method.
	 *
	 * @param WC_Payment_Gateway $gateway The gateway to disable.
	 */
	private function disable_payment_method( WC_Payment_Gateway $gateway ): void {
		$gateway->update_option( Payment_Methods::GATEWAY_SETTING_ENABLED, 'no' );
	}

	/**
	 * Get the payment method of a certain type.
	 *
	 * @param string                          $type            Type of payment method.
	 * @param Settings_Payment_Method_Model[] $payment_methods Payment methods.
	 *
	 * @return Settings_Payment_Method_Model|false Payment method on success, else false
	 */
	protected function get_payment_method_of_type( string $type, array $payment_methods ): false|Settings_Payment_Method_Model {
		foreach ( $payment_methods as $payment_method ) {
			if ( $payment_method->get_type() === $type ) {
				return $payment_method;
			}
		}

		return false;
	}
}
