<?php


namespace WooCommerce\Plugin\AddonPayments\Operations;

use ComerciaGlobalPayments\AddonPayments\SDK\Response\OperationInterface;
use Exception;

class ValidateOrder {

	public const OPERATION_DEBIT = 'DEBIT';

	public const OPERATION_CREDIT = 'CREDIT';

	public const OPERATION_VOID = 'VOID';

	public const OPERATION_REFUND = 'REFUND';

	public const OPERATION_REBATE = 'REBATE';


	public function __invoke( $transactions, \WC_Order $order ) {
 		$transaction = array_pop( $transactions );

		switch ( $transaction->getStatus() ) {
			case OperationInterface::STATUS_SUCCESS:
				try {
					$this->process_operations( $transaction, $order );

				} catch ( Exception $e ) {
					throw new Exception( $e->getMessage() );
				}

				break;
			case OperationInterface::STATUS_ERROR:
			case OperationInterface::STATUS_ERROR3DS:
			case OperationInterface::STATUS_FAIL:
			case OperationInterface::STATUS_REJECTED:
				if( $transaction->getOperationType() !== self::OPERATION_REBATE
				    && $transaction->getOperationType() !== self::OPERATION_REFUND ) {
					$order->update_status(
						'wc-failed',
						__( 'Payment error', 'addonpayments' )
					);
				}
				break;
			case OperationInterface::STATUS_CANCELLED:
				$order->update_status(
					'wc-cancelled',
					__( 'Payment canceled', 'addonpayments' )
				);

				break;
			case OperationInterface::STATUS_PENDING:
			case OperationInterface::STATUS_PENDING_CONFIRMATION:
				$message = __( 'Payment confirmed (pending capture)', 'addonpayments' );

				if( OperationInterface::STATUS_PENDING_CONFIRMATION === $transaction->getStatus() ) {
					$message = __( 'Transaction was pending confirmation.', 'addonpayments' );
				}

				$order->update_status(
					'wc-on-hold',
					$message
				);

				do_action( 'woocommerce_payment_complete', $order->get_id() );

				break;
			case OperationInterface::STATUS_AWAITING_PAYSOL:
			case OperationInterface::STATUS_REDIRECTED:
			default:

				if( $transaction->getOperationType() === self::OPERATION_REBATE
				    || $transaction->getOperationType() === self::OPERATION_REFUND ) {
					break;
				}

				$order->update_status(
					'wc-pending',
					__( 'Payment processing', 'addonpayments' )
				);
				break;
		}
	}

	public function process_operations( $transaction, \WC_Order $order ) {
		$paymentOperations = array( self::OPERATION_DEBIT, self::OPERATION_CREDIT );

		if ( in_array( $transaction->getOperationType(), $paymentOperations, true ) ) {
			$order->payment_complete( $transaction->getPayFrexTransactionId() );
			add_post_meta(
				$order->get_id(),
				'paymentSol',
				$transaction->getPaymentSolution(),
				true
			);

			// Handle VOID
		} elseif ( self::OPERATION_VOID === $transaction->getOperationType() ) {
			$order->update_status(
				'wc-cancelled',
				__( 'Payment canceled', 'addonpayments' )
			);

			// Handle REFUND (status change)
		} elseif ( self::OPERATION_REFUND === $transaction->getOperationType() ) {
			$order->update_status(
				'wc-refunded',
				__( 'Payment refunded', 'addonpayments' )
			);

		}

		elseif ( self::OPERATION_REBATE === $transaction->getOperationType() ) {
			$refunds = $order->get_refunds();

			if ( ! empty( $refunds ) ) {
				$last_refund                = reset( $refunds );
				$last_refund_transaction_id = get_post_meta(
					$last_refund->get_id(),
					'_transaction_id',
					TRUE
				);

				if ( $last_refund_transaction_id === $transaction->getPayFrexTransactionId() ) {
					return;
				}
			}

			try {
				// Rebate from EPG backoffice
				$refund = $this->create_refund($order, $transaction);

				if ( is_wp_error( $refund ) ) {
					throw new Exception( $refund->get_error_message() );
				}

				add_post_meta(
					$refund->get_id(),
					'_transaction_id',
					$transaction->getPayFrexTransactionId(),
					true
				);

				$order->add_order_note(
					sprintf( __( 'Refunded %s. Transaction ID: %d. Reason:' ),
						strip_tags( wc_price( $transaction->getAmount() ) ),
						$transaction->getPayFrexTransactionId()
					)
				);

				$remaining_amount = ( null !== $transaction->getRemainingAmount() )
					? $transaction->getRemainingAmount()
					: ( $order->get_total() - $order->get_total_refunded() );


				if ( 0 < $remaining_amount ) {
					$order->update_status( 'wc-partial-refund' );
				}

			} catch ( \Exception $e ) {
				\WC_Gateway_EasyPaymentGateway::log($e->getMessage(), 'error');
			}
		}
	}

	/**
	 * @param \WC_Order $order
	 * @param $transaction
	 *
	 * @return bool|\WC_Order_Refund|\WP_Error
	 * @throws \Exception
	 */
	private function create_refund( $order, $transaction ) {
		$refund_args = array(
			'amount'         => wc_format_decimal( $transaction->getAmount(), wc_get_price_decimals() ),
			'order_id'       => $order->get_id(),
		);

		if(!empty($transaction->getDetails()) ) {
			$details = json_decode($transaction->getDetails());
			$refunded_items = property_exists($details, 'refunded_items') ? $details->refunded_items : array();

			if ( ! empty( $refunded_items ) ) {
				$line_items = array();
				$reference_items = array();
				$items = $order->get_items( array( 'line_item', 'fee', 'shipping' ) );

				foreach ( $items as $item_id => $item ) {
					$line_items[ $item_id ] = array(
						'qty'          => 0,
						'refund_total' => 0,
						'refund_tax'   => array(),
					);

					if($item instanceof \WC_Order_Item_Shipping) {
						$reference_items['shipping'] = $item->get_id();

					}else {
						//TODO: Remove sku and id refs
						// Reference can be item_id (i_{item_id}), sku or product id
						$reference_items[ 'sku_' . $item->get_product()->get_sku() ] = $item_id;
						$reference_items[ 'id_' . $item->get_product_id() ]          = $item_id;
						$reference_items[ 'i_' . $item_id ]                          = $item_id;
					}

				}

				$refunded_items = array_reduce( $details->refunded_items,
					function ( $result, $item ) use ( $reference_items ) {
						//TODO: Remove sku and id refs
						// Reference can be item_id (i_{item_id}), sku or product id
						$reference = $item->article->reference;
						$key = '';

						if( 'shipping_fee' === $item->article->category ) {
							$key = $reference_items['shipping'];
						}else {
							if ( isset( $reference_items[ $reference ] ) ) {
								$key = $reference_items[ $reference ];
							} elseif ( isset( $reference_items["sku_{$reference}"] ) ) {
								$key = $reference_items["sku_{$reference}"];
							} elseif ( isset( $reference_items["id_{$reference}"] ) ) {
								$key = $reference_items["id_{$reference}"];
							}
						}

						$result[ $key ] = $item;
						return $result;

					}, array() );


				foreach ( $refunded_items as $key => $value ) {
					$tax          = $items[ $key ]->get_total_tax();
					$refund_total = $value->total_price_with_tax->amount;
					$refund_tax   = 0;

					if ( ! empty( $tax ) ) {
						$refund_tax   = $tax;
						$refund_total -= $refund_tax;
					}

					$line_items[ $key ] = array(
						'qty'          => $value->units,
						'refund_total' => $refund_total,
						'refund_tax'   => array(
							1 => $refund_tax
						),
					);
				}

				// TODO: Revisar shipping cost

				if(!empty($line_items)) {
					$refund_args['line_items'] = $line_items;
				}
			}
		}

		return wc_create_refund( $refund_args );
	}

}
