<?php

namespace Simplyin\Simplyin_W_Plugin\Payments\Gateway;

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

use Exception;
use Simplyin\Simplyin_W_Plugin\Payments\Admin\Admin_Options;
use Simplyin\Simplyin_W_Plugin\Payments\Ajax\Payment_Methods\Payment_Methods_Ajax_Handler;
use Simplyin\Simplyin_W_Plugin\Payments\Gateway\Model\Order_Data;
use Simplyin\Simplyin_W_Plugin\Payments\Gateway\Model\Payment_Type_Interface;
use Simplyin\Simplyin_W_Plugin\Payments\Gateway\Model\Session_Data;
use Simplyin\Simplyin_W_Plugin\Payments\Gateway\Payment_Fields\Payment_Fields;
use Simplyin\Simplyin_W_Plugin\Payments\Gateway\Status\Payment_Status;
use WC_Order;
use WC_Payment_Gateway;


class Simplyin_Gateway extends WC_Payment_Gateway {

	private static string $payment_fields_cache = '';

	private Admin_Options $admin_options;
	protected Order_Data $order_data;

	/**
	 *
	 * @throws Exception
	 */
	public function __construct() {
		$this->id           = 'simplyin';
		$this->icon
		                    = simplyin()->get_plugin_url() . 'public/img/logo_smalln.svg';
		$this->has_fields
		                    = true;
		$this->method_title = __( 'Simply.IN',
			'simplyin' );
		$this->method_description
		                    = __( 'Boosts conversion with on-page payments — cards, BLIK, and a seamless 1-click experience without leaving the store.',
			'simplyin' );
		$this->supports     = [
			'products',
		];


		$this->title       = __( 'Simple and Safe payments',
			'simplyin' );
		$this->description = '';
		$this->enabled     = $this->get_option( 'enabled' );

		$this->admin_options = new Admin_Options();
		$this->init_form_fields();
		$this->init_settings();


		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id,
			[ $this, 'process_admin_options' ] );

		add_action( 'woocommerce_before_thankyou',
			[ $this, 'before_thankyou_actions' ]
			,
			1 );


		$this->webhook();

		$this->register_ajax_hooks();
	}

	public function before_thankyou_actions( $order_id ): void {
		global $wp;

		if ( empty( $order_id ) ) {
			$this->log_error(
				'thankyou_guard',
				'Empty $order_id on woocommerce_before_thankyou',
				null,
				[
					'hook'        => 'woocommerce_before_thankyou',
					'request_uri' => Gateway_Helper::get_current_url(),
					'get'         => $wp->query_vars,
				]
			);

			return;
		}

		$order = wc_get_order( $order_id );
		if ( ! $order instanceof \WC_Order ) {
			$this->log_error(
				'thankyou_guard',
				'wc_get_order() did not return WC_Order',
				$order_id,
				[
					'hook'        => 'woocommerce_before_thankyou',
					'request_uri' => Gateway_Helper::get_current_url(),
				]
			);

			return;
		}

		$order_pm = $order->get_payment_method();
		if ( $order->get_payment_method() !== $this->id ) {
			$this->log(
				'thankyou_guard',
				$order_id,
				'Skip: payment method does not match gateway',
				[
					'order_payment_method' => $order_pm,
					'gateway_id'           => $this->id,
				]
			);

			return;
		}

		try {
			$this->order_received_page_action( $order );
		} catch ( Exception $error ) {
			$this->log_error(
				'thankyou_guard',
				'order_received_page_action exception: ' . $error->getMessage(),
				$order_id,
				[
					'trace'       => $error->getTraceAsString(),
					'request_uri' => Gateway_Helper::get_current_url(),
					'get'         => $wp->query_vars,
				]
			);
		}
	}

	public function get_icon() {
		$payment_fields = new Payment_Fields();
		$payment_fields->reload();
		$session_data = new Session_Data();
		$provider     = $session_data->get_payment_provider();
		if ( null === $provider ) {
			return parent::get_icon();
		}

		if ( ! $provider->get_logo_url() ) {
			return $this->icon
				? '<img class="simplyin-payment-provider-logo" src="' . esc_url( $this->icon ) . '" alt="Simply.IN">'
				: '';
		}

		ob_start();
		simplyin()->locate_template( 'gateway_icon.php',
			[
				'main_icon' => $this->icon,
				'provider'  => $provider,
			],
			'src/Payments/views/classic_checkout' );

		$output = ob_get_contents();

		ob_end_clean();

		return $output;
	}


	private function register_ajax_hooks() {
		$payment_methods_ajax_handler = new Payment_Methods_Ajax_Handler();
		$payment_methods_ajax_handler->register_ajax_hooks();
	}

	public function init_form_fields() {

		$this->form_fields = $this->admin_options->get_form_config();

	}

	public function admin_options() {
		$this->admin_options->render(
			$this->generate_settings_html( $this->get_form_fields(), false )
		);
	}


	public function is_available() {
		if ( ! is_admin() && empty( self::$payment_fields_cache ) ) {

			ob_start();
			$this->payment_fields();

			self::$payment_fields_cache = ob_get_contents();
			ob_end_clean();

			if ( empty( self::$payment_fields_cache ) ) {
				return false;

			}
		}

		return parent::is_available();
	}

	/**
	 * @return void
	 * @throws Exception
	 */
	public function payment_fields() {
		if ( empty( self::$payment_fields_cache ) ) {
			try {
				$service = ( new Payment_Fields() );
				$service->render_payment_fields_checkout();
			} catch ( Exception $exception ) {

				$this->log_error( 'payment_fields',
					$exception->getMessage(),
					null );

				return;
			}


		}
		// phpcs:disable
		// Output payment fields template
		echo self::$payment_fields_cache;
		// phpcs:enable
	}

	private function perform_post_process_cleaning() {
		if ( ! WC()->session || ! WC()->cart ) {
			return;
		}
		wc()->session->set( 'store_api_draft_order', 0 );
		WC()->cart->empty_cart();
		WC()->session->save_data();
	}

	/**
	 * @param $order_id
	 *
	 * @return array
	 * @throws Exception
	 */
	public function process_payment( $order_id ): array {
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return [ 'result' => 'failure' ];
		}
		$this->order_data = new Order_Data( $order );

		if ( isset( $_POST['simplyin_checkout_token'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['simplyin_checkout_token'] ) ),
				'simplyin_checkout_nonce' ) ) {
			$this->log_error( 'process_payment',
				'wp_verify_nonce classic checkout failed ',
				$order_id );

			return [ 'status' => 'failure' ];

		} elseif ( isset( $_POST['simplyin_blocks_token'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['simplyin_blocks_token'] ) ),
				'simplyin_blocks_nonce' ) ) {
			$this->log_error( 'process_payment',
				'wp_verify_nonce block checkout failed ',
				$order_id );

			return [ 'status' => 'failure' ];

		} elseif ( ! isset( $_POST['simplyin_checkout_token'] ) && ! isset( $_POST['simplyin_blocks_token'] ) ) {
			$this->log_error( 'process_payment',
				'Nonce not found',
				$order_id );

			return [ 'status' => 'failure' ];
		}


		if ( isset( $_POST['simplyin-payment-option'] ) ) {
			$payment_channel_id = sanitize_text_field( wp_unslash( $_POST['simplyin-payment-option'] ) );
		} elseif ( isset( $_POST['simplyin_payment_option_id'] ) ) {//blocks
			$payment_channel_id = sanitize_text_field( wp_unslash( $_POST['simplyin_payment_option_id'] ) );
		} else {

			$this->log_error( 'process_payment',
				'Missing payment_channel_id',
				$order_id );

			return [ 'status' => 'failure' ];

		}


		$session_data = new Session_Data();
		try {
			$payment_method_model = $session_data->get_payment_method_info_by_id( $payment_channel_id );
		} catch ( \Throwable $e ) {
			$this->log_error( 'process_payment',
				'Payment method not found or cache missing: ' . $e->getMessage(),
				$order_id );

			return [ 'status' => 'failure' ];
		}


		if ( $payment_method_model instanceof Payment_Type_Interface ) {
			try {
				$payment_method_model->configure_on_checkout();
			} catch ( Exception $exception ) {
				$this->log_error( 'configure_on_checkout',
					$order_id,
					$exception->getMessage() );

				return [
					'status' => 'failure',
				];
			}
		}

		$this->order_data->update_selected_payment_method( $payment_method_model );

		$url       = $order->get_checkout_order_received_url();
		$to_return = [
			'result'   => 'success',
			'redirect' => $url,
		];


		$this->log( 'process_payment',
			$order_id,
			'',
			[
				'payment_method_model' => $payment_method_model,
			]
		);

		// If this is a retry on a previously failed order, reset to pending before attempting payment.
		if ( 'failed' === $order->get_status() ) {
			$order->update_status( 'pending' );
			$order->save();
		}

		try {
			$payment_method_model->process_payment( $this->order_data );
			$this->perform_post_process_cleaning();
		} catch ( Exception $exception ) {
			$this->log_error( 'process_payment failed',
				$exception->getMessage(),
				$order_id );

			$this->order_data->update_order_status( 'failed', sprintf( 'Simply.IN payment failed with method: %s', $payment_method_model->get_name() ) );
			$order->save();

			return [ 'result' => 'failure' ];
		}

		return $to_return;
	}

	public function order_received_page_action( WC_Order $order ): void {
		$order_id         = $order->get_id();
		$this->order_data = new Order_Data( $order );

		if ( ! $this->order_data->has_order_received_flag() ) {
			$this->order_data->add_order_received_flag();
		}


		$serialized_method = (string) $this->order_data->get_from_order_meta( '_simplyin_selected_payment_method' );
		if ( empty( $serialized_method ) ) {
			$this->log_error( 'order_received_page_action',
				'Missing _simplyin_selected_payment_method meta',
				$order_id );

			return;
		}

		try {
			$selected_payment_method = $this->order_data->get_selected_payment_method();
		} catch ( Exception $e ) {
			$this->log_error( 'order_received_page_action',
				'Failed to deserialize selected payment method: ' . $e->getMessage(),
				$order_id );

			return;
		}

		if ( $selected_payment_method instanceof Payment_Type_Interface ) {
			$selected_payment_method->order_received_actions( $this->order_data );
		}

		$this->log( 'order_received_page_action',
			$order_id,
			'',
			[
				'selected_payment_method' => $selected_payment_method,
				'payment_uuid'            => $selected_payment_method->get_payment_uuid(),
				'payment_status'          => $selected_payment_method->get_payment_status(),
				'provider_payment_id'     => $selected_payment_method->get_provider_payment_id(),
				'registered_at'           => $selected_payment_method->get_registered_at(),
			]
		);

	}


	private function log_error(
		string $action,
		string $message,
		$order_id,
		array $args = []
	) {

		$this->log( $action, $order_id, $message, $args, true );

	}

	private function log(
		string $action,
		$order_id,
		string $message = '',
		array $args = [],
		bool $error = false
	) {

		if ( $order_id ) {
			$args['order_id'] = $order_id;
		}

		$content = sprintf( "[%s] [%s] [%s]",
			$action,
			$message,
			wp_json_encode( $args ),
		);

		$woo_logger = simplyin()
			->get_woocommerce_logger( 'payments' );

		if ( $error ) {
			$woo_logger->log_error(
				$content );
		} else {
			$woo_logger->log_debug(
				$content );
		}
	}

	private function parse_webhook_params(): ?array {
		$order_key = null;

		/**
		 * Note for Plugin Reviewer: The `order_key` serves as the security token for this webhook
		 * request, as a standard WordPress nonce is not applicable for server-to-server communication.
		 * It is validated later using `wc_get_order_id_by_order_key()`.
		 */
		if ( isset( $_GET['order_key'] ) ) {// phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended
			$order_key = sanitize_text_field( wp_unslash( $_GET['order_key'] ) );// phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended
		}

		if ( $order_key ) {
			return [
				'order_key' => $order_key,
			];
		}

		return null;
	}

	public function webhook(): void {
		add_action( 'woocommerce_api_wc_gateway_simplyin', function () {
			$params = $this->parse_webhook_params();
			if ( $params ) {
				$order_id = null;
				try {
					$order_key = $params['order_key'];
					$order_id  = wc_get_order_id_by_order_key( wc_clean( wp_unslash( $order_key ) ) );
					$order     = wc_get_order( $order_id );
					if ( $order ) {
						$this->order_data = new Order_Data( $order );
					} else {
						throw new Exception( "Order: $order_id not found" );
					}

					$body = file_get_contents( 'php://input' );
					$data = json_decode( $body, true );

					if ( json_last_error() !== JSON_ERROR_NONE ) {
						$this->log_error( 'webhook',
							'Invalid JSON payload',
							null );
						status_header( 400 );
						exit;
					}

					if ( ! is_array( $data ) ) {
						$this->log_error( 'webhook', 'Payload is not a JSON object', $order_id );
						status_header( 400 );
						exit;
					}

					array_walk_recursive( $data, static function ( &$value ) {
						if ( is_string( $value ) ) {
							$value = sanitize_text_field( $value );
						}
					} );

					$payment_status_from_webhook = isset( $data['status'] ) ? (string) $data['status'] : '';
					if ( '' === $payment_status_from_webhook ) {
						$this->log_error( 'webhook', 'Missing or empty status in webhook payload', $order_id );
						status_header( 400 );
						exit;
					}

					$event_id = $data['event_id'] ?? $data['eventId'] ?? null;
					if ( null === $event_id ) {
						$event_id = hash( 'sha256', $order_key . '|' . ( $data['status'] ?? '' ) );
						$this->log( 'webhook', $order_id, 'event_id missing in payload — using deterministic fallback', [
							'fallback_event_id' => $event_id,
						] );
					}
					$processed_events = $order->get_meta( '_simplyin_processed_webhook_events' );

					if ( ! is_array( $processed_events ) ) {
						$processed_events = [];
					}

					if ( in_array( $event_id, $processed_events, true ) ) {
						$this->log( 'webhook',
							0,
							'',
							[
								'order_id'  => $order_id,
								'event_id'  => $event_id,
								'status'    => 'already_processed',
							]
						);
						wp_send_json_success( [ 'status' => 'already_processed', 'event_id' => $event_id ] );
					}

					$payment_status_service = new Payment_Status( $this->order_data );
					$payment_status_service->update_payment_status( $payment_status_from_webhook );
					$mapped_wc_order_status = $payment_status_service->map_wc_status( $payment_status_from_webhook );
					$note                   = "Simply.IN webhook status received: $payment_status_from_webhook. Mapped to Woocommerce status: $mapped_wc_order_status";

					Webhook_Context::set_processing( $order_id );
					$this->order_data->update_order_status( $mapped_wc_order_status,
						$note );
					Webhook_Context::clear( $order_id );

					$processed_events[] = $event_id;
					$processed_events   = array_slice( array_unique( $processed_events ), -20 );
					$order->update_meta_data( '_simplyin_processed_webhook_events', $processed_events );
					$order->save_meta_data();

					$this->log( 'webhook',
						0,
						'',
						[
							'order_id'  => $order_id,
							'order_key' => $order_key,
							'data'      => $data,
							'status from webhook'    => $payment_status_from_webhook,
						]
					);
				} catch ( Exception $exception ) {
					$this->log_error( 'webhook',
						$exception->getMessage(),
						$order_id
					);
				}

			} else {
				$this->log_error( 'webhook',
					'Order not found in request',
					null,
					[
						'url' => Gateway_Helper::get_current_url(),
					],
				);
			}
		} );


	}


}
