<?php
/**
 * Payment gateway for BNPL.
 */

namespace Flo_Checkout\Gateways;

use Flo_Checkout\Gateways\Helpers\Gateway_Logger_Trait;
use Flo_Checkout\Gateways\Helpers\Interface_Flo_WC_Payment_Gateway;
use Flo_Checkout\Gateways\BNPL\BNPL_AJAX;
use Flo_Checkout\Gateways\BNPL\BNPL_API;
use Flo_Checkout\Gateways\BNPL\BNPL_Cron;
use Flo_Checkout\Gateways\BNPL\BNPL_Emails;
use Flo_Checkout\Gateways\BNPL\BNPL_Settings;
use Flo_Checkout\Gateways\BNPL\BNPL_Waiting_Screen;
use Flo_Checkout\Shared;
use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Data\Payment_Methods;
use WC_Payment_Gateway;
use WP_Error;

/**
 * Class Northmill_BNPL
 */
class Northmill_BNPL extends WC_Payment_Gateway implements Interface_Flo_WC_Payment_Gateway {
	use Gateway_Logger_Trait;

	/**
	 * Minimum transaction amount.
	 * (Maximum is defined in class WC_Payment_Gateway)
	 *
	 * @var int
	 */
	public int $min_amount = 0;

	/**
	 * Get the gateway ID.
	 *
	 * @return string
	 */
	public static function get_id(): string {
		return Payment_Methods::NMC_BNPL;
	}

	/**
	 * Whether the gateway supports blocks.
	 *
	 * @return bool
	 */
	public static function has_block_support(): bool {
		return true;
	}

	/**
	 * Set up the necessary components for the public gateway.
	 *
	 * This method calls the setup method for the BNPL_Cron, BNPL_AJAX, and BNPL_Waiting_Screen classes.
	 * It initializes the required functionality for the public gateway to function properly.
	 *
	 * @return void
	 */
	public static function public_setup(): void {
		BNPL_Cron::setup();
		BNPL_AJAX::setup();
		BNPL_Waiting_Screen::setup();
		BNPL_Emails::setup();
	}

	/**
	 * Initializes the BNPL gateway.
	 */
	public function __construct() {
		$this->id                 = self::get_id();
		$this->method_title       = __( 'Northmill Checkout - Northmill Pay later', 'flo-checkout-plugin' );
		$this->method_description = __( 'Initializes Northmill Pay later on checkout.', 'flo-checkout-plugin' );
		$this->has_fields         = true;
		$this->supports           = array(
			'products',
			'refunds',
		);
		$this->max_amount         = 50000;
		$this->min_amount         = 50;

		$this->init_form_fields();

		$options_title       = $this->get_option( 'title' );
		$options_description = $this->get_option( 'description' );

		$this->title       = $options_title ? $options_title : __( 'Northmill Pay later', 'flo-checkout-plugin' );
		$this->description = $options_description ? $options_description : '';

		if ( $this->needs_setup() ) {
			$this->enabled = 'no';
		}

		// Save settings fields.
		add_action(
			'woocommerce_update_options_payment_gateways_' . $this->id,
			array(
				$this,
				'process_admin_options',
			),
			10,
			0
		);
	}

	/**
	 * Check if the payment gateway is available for use.
	 *
	 * Checks if:
	 * - The cart exists
	 * - If the order total is greater than zero
	 * - If the minimum amount required by the payment gateway is less than the order total.
	 *
	 * If any of these conditions are not met, then the payment gateway is considered unavailable.
	 *
	 * @return bool Returns true if the payment gateway is available, false otherwise.
	 */
	public function is_available(): bool {
		if (
			WC()->cart &&
			0 < $this->get_order_total() &&
			$this->min_amount > $this->get_order_total()
		) {
			return false;
		}
		return parent::is_available();
	}

	/**
	 * Initializes the form fields for the BNPL payment method settings.
	 *
	 * @return void
	 */
	public function init_form_fields(): void {
		$this->form_fields = BNPL_Settings::get_settings_fields();
	}

	/**
	 * Renders additional info and fields for payment method in checkout.
	 */
	public function payment_fields(): void {
		require Shared::instance()->package_info->paths->get_path() . 'views/bnpl/checkout_fields.php';
	}

	/**
	 * Return whether this gateway still requires setup to function.
	 *
	 * When this gateway is toggled on via AJAX, if this returns true, a
	 * redirect will occur to the settings page instead.
	 *
	 * @return bool
	 */
	public function needs_setup(): bool {
		$api_url  = $this->get_option( 'api_url', '' );
		$api_user = $this->get_option( 'api_user', '' );
		$api_pass = $this->get_option( 'api_pass', '' );

		return empty( $api_url ) || empty( $api_user ) || empty( $api_pass );
	}

	/**
	 * Processes the payment for a given order.
	 *
	 * @param int $order_id The ID of the order to process the payment for.
	 *
	 * @return array The result of the payment processing.
	 */
	public function process_payment( $order_id ): array {
		$order = wc_get_order( $order_id );

		// Filters transaction note. Should return a string.
		$transaction_note = apply_filters(
			'northmill_checkout_bnpl_transaction_note',
			'',
			$order_id,
		);

		$transaction_note = is_string( $transaction_note ) ? $transaction_note : '';

		// Create transaction.
		$transaction = BNPL_API::instance()->create_transaction( $order->get_total(), $transaction_note );

		if ( ! $transaction ) {
			$order->add_order_note(
				__( 'Failed to create transaction with Northmill', 'flo-checkout-plugin' )
			);

			// Abort checkout.
			wc_add_notice( __( 'Failed to create transaction with Northmill', 'flo-checkout-plugin' ), 'error' );
			return array(
				'result' => 'failure',
			);
		}

		$order->update_status( 'pending', __( 'Order awaiting BNPL payment', 'flo-checkout-plugin' ) );
		$order->add_order_note( __( 'Northmill BNPL payment initiated', 'flo-checkout-plugin' ) );

		// Add BNPL transaction meta to order.
		$order->update_meta_data( 'northmill_bnpl_transaction_id', $transaction->get_transaction_id() );
		$order->update_meta_data( 'northmill_bnpl_transaction_url', $transaction->get_application_url() );
		$order->update_meta_data( 'northmill_bnpl_transaction_qr', $transaction->get_qr_code() );
		$order->save();

		// Redirect user to BNPL payment waiting page.
		return array(
			'result'   => 'success',
			'redirect' => add_query_arg(
				array(
					'order_id'  => $order_id,
					'order_key' => $order->get_order_key( 'edit' ),
				),
				WC()->api_request_url( BNPL_Waiting_Screen::WAITING_PAGE_SLUG )
			),
		);
	}

	/**
	 * Process a refund if supported.
	 *
	 * @param int        $order_id Order ID.
	 * @param float|null $amount Refund amount.
	 * @param string     $reason Refund reason.
	 *
	 * @return bool|WP_Error True or false based on success, or a WP_Error object.
	 */
	public function process_refund( $order_id, $amount = null, $reason = '' ): bool|WP_Error {
		$order = wc_get_order( $order_id );

		// Check if the refund amount equals the order total.
		if ( $amount < $order->get_total() ) {
			return new WP_Error( 'error', __( 'Only full refunds are allowed for BNPL transactions.', 'flo-checkout-plugin' ) );
		}

		$transaction_id = $order->get_meta( 'northmill_bnpl_transaction_id' );
		$refunded       = BNPL_API::instance()->refund_transaction( $transaction_id );

		if ( ! $refunded ) {
			return new WP_Error( 'error', __( 'Failed to refund BNPL transaction', 'flo-checkout-plugin' ) );
		}

		return true;
	}
}
