<?php

namespace PostenBringCheckout\admin\api\handlers;

use Exception;
use PostenBringCheckout\admin\api\models\OrderMapper;
use PostenBringCheckout\admin\api\models\OrderRequest;
use PostenBringCheckout\admin\bring\CheckoutApi;
use PostenBringCheckout\admin\bring\models\booking\ValidationRequest;
use PostenBringCheckout\admin\bring\models\booking\WebshopBookingMapper;
use PostenBringCheckout\admin\bring\models\service\MaxEdges;
use PostenBringCheckout\admin\bring\models\service\MybringServiceDetails;
use PostenBringCheckout\admin\bring\models\service\MaxDimensions;
use PostenBringCheckout\checkout\integrations\shipmenttracking\ShipmentTrackingIntegration;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WC_Order_Query;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Handles bookings
 */
class BookingHandler {
	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 * @throws Exception
	 */
	public function get_orders( WP_REST_Request $request ) {
		$order_ids = $request->get_param( 'orderIds' );

		$args = array(
			'post__in' => $order_ids,
			'return'   => 'objects',
			'limit'    => '-1',
		);

		$query     = new WC_Order_Query( $args );
		$wc_orders = $query->get_orders();

		$wc_orders = array_filter( $wc_orders, function( $order ) {
			return $this->is_bookable_order( $order );
		} );

		$validation_response = CheckoutApi::get_instance()->validateOrders( new ValidationRequest( $wc_orders ) );

		$orders = array();
		foreach ( $wc_orders as $wc_order ) {
			$mybring_bookings = $wc_order->get_meta( 'mybring_bookings' );
			if ( empty( $mybring_bookings ) ) {
				$mybring_bookings = array();
			}
			$order_validation_errors = $validation_response->getOrder( $wc_order->get_id() );

			$order    = OrderMapper::mapToOrder( $wc_order, $mybring_bookings, $order_validation_errors );
			$orders[] = $order;
		}

		return new WP_REST_Response( $orders, 200 );
	}

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_REST_Response
	 * @throws Exception
	 */
	public function get_order( WP_REST_Request $request ) {
		$order_id = $request->get_param( 'orderId' );

		$wc_order         = wc_get_order( $order_id );
		if ( ! $wc_order || ! $this->is_bookable_order( $wc_order ) ) {
			return new WP_Error( 'invalid_order_type', 'Order not found or is a refund', array( 'status' => 400 ) );
		}

		$mybring_bookings = $wc_order->get_meta( 'mybring_bookings' );
		if ( empty( $mybring_bookings ) ) {
			$mybring_bookings = array();
		}

		$validation_response     = CheckoutApi::get_instance()->validateOrders( new ValidationRequest( array( $wc_order ) ) );
		$order_validation_errors = $validation_response->getOrder( $wc_order->get_id() );
		$order                   = OrderMapper::mapToOrder( $wc_order, $mybring_bookings, $order_validation_errors );

		return new WP_REST_Response( $order->to_array(), 200 );
	}


	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function get_labels( WP_REST_Request $request ) {
		$order_ids  = $request->get_param( 'orderIds' );
		$label_type = $request->get_param( 'labelType' );

		if ( empty( $order_ids ) || empty( $label_type ) ) {
			return new WP_Error( 'missing_parameters', 'Order IDs and Label Type are required', array( 'status' => 400 ) );
		}

		$checkout_api = CheckoutApi::get_instance();

		try {
			$labels = $checkout_api->getLabels( $order_ids, $label_type );

			// This filter prevents WP_REST_Server::serve_request() from automatically applying wp_json_encode to the returned binary data,
			// by echoing the raw binary data before serve_request handles the response
			add_filter('rest_pre_serve_request',function() use($labels){
				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				echo $labels;
				return true;
			});

			return new WP_REST_Response(
				null,
				200,
				array(
					'Content-Type'        => 'application/pdf',
					'Content-disposition' => 'inline; filename=labels.pdf'
				)
			);
		} catch ( Exception $e ) {
			return new WP_Error( 'failed_to_get_labels', esc_html($e->getMessage()), array( 'status' => 500 ) );
		}
	}

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function get_waybills( WP_REST_Request $request ) {
		$order_ids  = $request->get_param( 'orderIds' );

		if ( empty( $order_ids ) ) {
			return new WP_Error( 'missing_parameters', 'Order IDs are required', array( 'status' => 400 ) );
		}

		$checkout_api = CheckoutApi::get_instance();

		try {
			$waybills = $checkout_api->getWaybills( $order_ids );

			// This filter prevents WP_REST_Server::serve_request() from automatically applying wp_json_encode to the returned binary data,
			// by echoing the raw binary data before serve_request handles the response
			add_filter('rest_pre_serve_request',function() use($waybills){
				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				echo $waybills;
				return true;
			});

			return new WP_REST_Response(
				null,
				200,
				array(
					'Content-Type'        => 'application/pdf',
					'Content-disposition' => 'inline; filename=waybills.pdf'
				)
			);
		} catch ( Exception $e ) {
			return new WP_Error( 'failed_to_get_waybills', esc_html($e->getMessage()), array( 'status' => 500 ) );
		}
	}

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function post_order( WP_REST_Request $request ) {
		$order_id      = $request->get_param( 'orderId' );
		$wc_order      = wc_get_order( $order_id );
		$json_data     = json_decode( $request->get_body(), true );
		$order_request = new OrderRequest( $json_data );

		$checkout_api = CheckoutApi::get_instance();

		if ( ! $wc_order ) {
			return new WP_Error( 'order_not_found', 'Order not found', array( 'status' => 404 ) );
		}

		$webshop_booking = WebshopBookingMapper::mapToWebshopBooking( $wc_order, $order_request );

		try {
			$booking_response = $checkout_api->createBooking( $webshop_booking );

			$mybring_bookings = $wc_order->get_meta( 'mybring_bookings' );
			if ( ! empty( $mybring_bookings ) ) {
				$mybring_bookings = array_merge( $mybring_bookings, $booking_response );
			} else {
				$mybring_bookings = $booking_response;
			}

			$wc_order->update_meta_data( 'mybring_bookings', $mybring_bookings );
			$wc_order->save();
			ShipmentTrackingIntegration::sync_booking_to_order_tracking( $wc_order, $booking_response );
		} catch ( Exception $e ) {
			wc_get_logger()->error(
				esc_html($e->getMessage()),
				array(
					'source'    => 'posten-bring-checkout',
					'backtrace' => true,
				)
			);
			return new WP_Error( 'api_error', esc_html($e->getMessage()), array( 'status' => 500 ) );
		}

		return new WP_REST_Response( $booking_response, 200 );
	}

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function get_booking_customers_and_services( WP_REST_Request $request ) {
		$fromParam = $request->get_param( 'from' );
		$toParam   = $request->get_param( 'to' );
		$from      = ! empty( $fromParam ) ? $fromParam : 'NO';
		$to        = ! empty( $toParam ) ? $toParam : 'NO';

		$checkout_api = CheckoutApi::get_instance();

		try {
			$customers_and_services = $checkout_api->getBookingCustomersAndServices( $from, $to );
		} catch ( Exception $e ) {
			return new WP_Error( 'api_error', esc_html($e->getMessage()), array( 'status' => 500 ) );
		}

		return new WP_REST_Response( $customers_and_services, 200 );
	}

	/**
	 * @param WP_REST_Request $request
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function get_shipping_options( WP_REST_Request $request ) {
		$from         = $request->get_param( 'from' );
		$to           = $request->get_param( 'to' );
		$weight       = $this->to_float_or_default( $request->get_param( 'weight' ), 0 );
		$height       = $this->to_int_or_default( $request->get_param( 'height' ), 0 );
		$width        = $this->to_int_or_default( $request->get_param( 'width' ), 0 );
		$length       = $this->to_int_or_default( $request->get_param( 'length' ), 0 );
		$checkout_api = CheckoutApi::get_instance();

		try {
			$shipping_options           = $checkout_api->get_shipping_options( $from, $to );

			$available_shipping_options = array_filter(
				$shipping_options,
				function ( MybringServiceDetails $shipping_option ) use ( $weight, $height, $width, $length ) {
					return ($shipping_option->max_weight_in_kgs * 1000) >= $weight &&
					MaxDimensions::fits_within( $shipping_option->max_dimensions, $height, $width, $length ) &&
					MaxEdges::fits_within_edges( $shipping_option->max_edges, $height, $width, $length );
				}
			);

			return new WP_REST_Response( $available_shipping_options, 200 );

		} catch ( Exception $e ) {
			wc_get_logger()->warning( esc_html($e->getMessage()), array( 'source' => 'posten-bring-checkout' ) );
			return new WP_Error( 'api_error', esc_html($e->getMessage()), array( 'status' => 500 ) );
		}
	}

	/**
	 * @param string|null $value
	 * @param int         $default
	 *
	 * @return int
	 */
	private function to_int_or_default( ?string $value, int $default ): int {
		return is_numeric( $value ) ? intval( $value ) : $default;
	}

	/**
	 * @param string|null $value
	 * @param int         $default
	 *
	 * @return float
	 */
	private function to_float_or_default( ?string $value, int $default ): float {
		return is_numeric( $value ) ? floatval( $value ) : $default;
	}

	private function is_bookable_order( $order ): bool {
		return $order && ! ( $order instanceof WC_Order_Refund );
	}
}
