<?php
/**
 * Payment REST API Controller
 *
 * @package NemtlyBooking
 */

namespace NemtlyBooking\API;

use NemtlyBooking\Interfaces\Payment_Repository_Interface;
use WP_REST_Controller;
use WP_REST_Server;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Class Payment_Controller
 */
class Payment_Controller extends WP_REST_Controller {
	/**
	 * Payment repository
	 *
	 * @var Payment_Repository_Interface
	 */
	private $payment_repository;

	/**
	 * Booking repository
	 *
	 * @var \NemtlyBooking\Interfaces\Booking_Repository_Interface
	 */
	private $booking_repository;

	/**
	 * Constructor
	 *
	 * @param Payment_Repository_Interface                           $payment_repository Payment repository.
	 * @param \NemtlyBooking\Interfaces\Booking_Repository_Interface $booking_repository Booking repository.
	 */
	public function __construct( $payment_repository, $booking_repository ) {
		$this->namespace          = 'nemtly-booking/v1';
		$this->rest_base          = 'payments';
		$this->payment_repository = $payment_repository;
		$this->booking_repository = $booking_repository;
	}

	/**
	 * Register routes
	 */
	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_items' ),
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
					'args'                => $this->get_collection_params(),
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_item' ),
					'permission_callback' => array( $this, 'get_item_permissions_check' ),
					'args'                => array(
						'id' => array(
							'description' => __( 'Unique identifier for the payment.', 'nemtly-booking' ),
							'type'        => 'integer',
						),
					),
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/booking/(?P<booking_id>[\d]+)',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_by_booking' ),
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
					'args'                => array(
						'booking_id' => array(
							'description' => __( 'Unique identifier for the booking.', 'nemtly-booking' ),
							'type'        => 'integer',
						),
					),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)/mark-paid',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'mark_payment_paid' ),
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
					'args'                => array(
						'id' => array(
							'description' => __( 'Unique identifier for the payment.', 'nemtly-booking' ),
							'type'        => 'integer',
							'required'    => true,
						),
					),
				),
			)
		);
	}

	/**
	 * Get payments
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_items( $request ) {
		$params = $request->get_params();

		// Get all payments.
		$all_payments = $this->payment_repository->find_all();

		// Apply filters.
		$filtered_payments = $this->filter_payments( $all_payments, $params );

		// Apply sorting.
		$sorted_payments = $this->sort_payments( $filtered_payments, $params );

		// Get total count before pagination.
		$total = count( $sorted_payments );

		// Apply pagination.
		$per_page           = isset( $params['per_page'] ) ? (int) $params['per_page'] : 20;
		$page               = isset( $params['page'] ) ? (int) $params['page'] : 1;
		$offset             = ( $page - 1 ) * $per_page;
		$paginated_payments = array_slice( $sorted_payments, $offset, $per_page );

		// Prepare response.
		$data = array();
		foreach ( $paginated_payments as $payment ) {
			$data[] = $this->prepare_item_for_response( $payment, $request );
		}

		$response = rest_ensure_response( $data );

		// Add pagination headers.
		$response->header( 'X-WP-Total', $total );
		$response->header( 'X-WP-TotalPages', ceil( $total / $per_page ) );

		return $response;
	}

	/**
	 * Get single payment
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_item( $request ) {
		$payment = $this->payment_repository->find( $request['id'] );

		if ( ! $payment ) {
			return new WP_Error(
				'nemtly_booking_payment_not_found',
				__( 'Payment not found.', 'nemtly-booking' ),
				array( 'status' => 404 )
			);
		}

		return rest_ensure_response( $this->prepare_item_for_response( $payment, $request ) );
	}

	/**
	 * Get payments by booking
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_by_booking( $request ) {
		$payments = $this->payment_repository->find_by_booking_id( $request['booking_id'] );

		$data = array();
		foreach ( $payments as $payment ) {
			$data[] = $this->prepare_item_for_response( $payment, $request );
		}

		return rest_ensure_response( $data );
	}

	/**
	 * Mark a payment as paid (admin). Updates payment status to succeeded and confirms the related booking.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response|WP_Error
	 */
	public function mark_payment_paid( $request ) {
		$payment_id = (int) $request['id'];
		$payment    = $this->payment_repository->find( $payment_id );

		if ( ! $payment ) {
			return new WP_Error(
				'rest_not_found',
				__( 'Payment not found.', 'nemtly-booking' ),
				array( 'status' => 404 )
			);
		}

		if ( 'succeeded' === $payment->get_status() || 'completed' === $payment->get_status() ) {
			return new WP_REST_Response(
				array(
					'success' => true,
					'message' => __( 'Payment was already marked as paid.', 'nemtly-booking' ),
					'data'    => $this->prepare_item_for_response( $payment, $request ),
				),
				200
			);
		}

		$updated = $this->payment_repository->update( $payment_id, array_merge( $payment->to_array(), array( 'status' => 'succeeded' ) ) );
		if ( ! $updated ) {
			return new WP_Error(
				'update_failed',
				__( 'Failed to update payment.', 'nemtly-booking' ),
				array( 'status' => 500 )
			);
		}

		$booking_id = $payment->get_booking_id();
		if ( $booking_id ) {
			$booking = $this->booking_repository->find( $booking_id );
			if ( $booking ) {
				$booking->set_booking_status( 'confirmed' );
				$this->booking_repository->update( $booking );
			}
		}

		$payment = $this->payment_repository->find( $payment_id );
		return new WP_REST_Response(
			array(
				'success' => true,
				'message' => __( 'Payment marked as paid and booking confirmed.', 'nemtly-booking' ),
				'data'    => $this->prepare_item_for_response( $payment, $request ),
			),
			200
		);
	}

	/**
	 * Filter payments based on query parameters
	 *
	 * @param array $payments Payments array.
	 * @param array $params   Query parameters.
	 * @return array Filtered payments.
	 */
	private function filter_payments( $payments, $params ) {
		$filtered = $payments;

		// Filter by status.
		if ( ! empty( $params['status'] ) ) {
			$statuses = is_array( $params['status'] ) ? $params['status'] : array( $params['status'] );
			$filtered = array_filter(
				$filtered,
				function ( $payment ) use ( $statuses ) {
					return in_array( $payment->get_status(), $statuses, true );
				}
			);
		}

		// Filter by gateway.
		if ( ! empty( $params['gateway'] ) ) {
			$filtered = array_filter(
				$filtered,
				function ( $payment ) use ( $params ) {
					return $payment->get_gateway() === $params['gateway'];
				}
			);
		}

		// Filter by search term.
		if ( ! empty( $params['search'] ) ) {
			$search_term = strtolower( $params['search'] );
			$filtered    = array_filter(
				$filtered,
				function ( $payment ) use ( $search_term ) {
					// Search in transaction ID.
					if ( stripos( $payment->get_transaction_id(), $search_term ) !== false ) {
						return true;
					}

					// Search in booking data.
					$booking = $this->booking_repository->find( $payment->get_booking_id() );
					if ( $booking ) {
						if ( stripos( $booking->get_booking_uuid(), $search_term ) !== false ) {
							return true;
						}
						if ( stripos( $booking->get_attendee_name(), $search_term ) !== false ) {
							return true;
						}
						if ( stripos( $booking->get_attendee_email(), $search_term ) !== false ) {
							return true;
						}
					}

					return false;
				}
			);
		}

		// Filter by date range.
		if ( ! empty( $params['date_from'] ) ) {
			$date_from = strtotime( $params['date_from'] );
			$filtered  = array_filter(
				$filtered,
				function ( $payment ) use ( $date_from ) {
					return strtotime( $payment->get_created_at() ) >= $date_from;
				}
			);
		}

		if ( ! empty( $params['date_to'] ) ) {
			$date_to  = strtotime( $params['date_to'] . ' 23:59:59' );
			$filtered = array_filter(
				$filtered,
				function ( $payment ) use ( $date_to ) {
					return strtotime( $payment->get_created_at() ) <= $date_to;
				}
			);
		}

		return array_values( $filtered );
	}

	/**
	 * Sort payments based on query parameters
	 *
	 * @param array $payments Payments array.
	 * @param array $params   Query parameters.
	 * @return array Sorted payments.
	 */
	private function sort_payments( $payments, $params ) {
		$orderby = isset( $params['orderby'] ) ? $params['orderby'] : 'created_at';
		$order   = isset( $params['order'] ) ? strtoupper( $params['order'] ) : 'DESC';

		usort(
			$payments,
			function ( $a, $b ) use ( $orderby, $order ) {
				$value_a = null;
				$value_b = null;

				switch ( $orderby ) {
					case 'id':
						$value_a = $a->get_id();
						$value_b = $b->get_id();
						break;
					case 'amount':
						$value_a = $a->get_amount();
						$value_b = $b->get_amount();
						break;
					case 'status':
						$value_a = $a->get_status();
						$value_b = $b->get_status();
						break;
					case 'gateway':
						$value_a = $a->get_gateway();
						$value_b = $b->get_gateway();
						break;
					case 'created_at':
					default:
						$value_a = strtotime( $a->get_created_at() );
						$value_b = strtotime( $b->get_created_at() );
						break;
				}

				if ( $value_a === $value_b ) {
					return 0;
				}

				if ( 'ASC' === $order ) {
					return $value_a < $value_b ? -1 : 1;
				} else {
					return $value_a > $value_b ? -1 : 1;
				}
			}
		);

		return $payments;
	}

	/**
	 * Prepare payment for response
	 *
	 * @param \NemtlyBooking\Models\Payment $payment Payment object.
	 * @param WP_REST_Request               $request Request object.
	 * @return array
	 */
	public function prepare_item_for_response( $payment, $request ) {
		$data = $payment->to_array();

		// Add booking information.
		$booking = $this->booking_repository->find( $payment->get_booking_id() );
		if ( $booking ) {
			$data['booking'] = array(
				'id'             => $booking->get_id(),
				'uuid'           => $booking->get_booking_uuid(),
				'attendee_name'  => $booking->get_attendee_name(),
				'attendee_email' => $booking->get_attendee_email(),
				'attendee_phone' => $booking->get_attendee_phone(),
				'booking_status' => $booking->get_booking_status(),
				'booking_date'   => $booking->get_booking_date(),
			);

			// Add event information if available.
			$event = get_post( $booking->get_event_id() );
			if ( $event ) {
				$data['booking']['event'] = array(
					'id'    => $event->ID,
					'title' => $event->post_title,
				);
			}
		} else {
			$data['booking'] = null;
		}

		// Add formatted amount.
		$data['formatted_amount'] = $this->format_currency( $payment->get_amount(), $payment->get_currency() );

		return $data;
	}

	/**
	 * Format currency
	 *
	 * @param float  $amount   Amount.
	 * @param string $currency Currency code.
	 * @return string
	 */
	private function format_currency( $amount, $currency ) {
		$symbols = array(
			'USD' => '$',
			'EUR' => '€',
			'GBP' => '£',
		);

		$symbol = isset( $symbols[ $currency ] ) ? $symbols[ $currency ] : $currency . ' ';

		return $symbol . number_format( $amount, 2 );
	}

	/**
	 * Check permissions for getting items
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return bool|WP_Error
	 */
	public function get_items_permissions_check( $request ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return new WP_Error(
				'rest_forbidden',
				__( 'You do not have permission to view payments.', 'nemtly-booking' ),
				array( 'status' => rest_authorization_required_code() )
			);
		}

		return true;
	}

	/**
	 * Check permissions for getting a single item
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return bool|WP_Error
	 */
	public function get_item_permissions_check( $request ) {
		return $this->get_items_permissions_check( $request );
	}

	/**
	 * Get collection parameters
	 *
	 * @return array
	 */
	public function get_collection_params() {
		return array(
			'page'      => array(
				'description'       => __( 'Current page of the collection.', 'nemtly-booking' ),
				'type'              => 'integer',
				'default'           => 1,
				'sanitize_callback' => 'absint',
				'validate_callback' => 'rest_validate_request_arg',
				'minimum'           => 1,
			),
			'per_page'  => array(
				'description'       => __( 'Maximum number of items to be returned in result set.', 'nemtly-booking' ),
				'type'              => 'integer',
				'default'           => 20,
				'minimum'           => 1,
				'maximum'           => 100,
				'sanitize_callback' => 'absint',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'search'    => array(
				'description'       => __( 'Limit results to those matching a string.', 'nemtly-booking' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'orderby'   => array(
				'description'       => __( 'Sort collection by object attribute.', 'nemtly-booking' ),
				'type'              => 'string',
				'default'           => 'created_at',
				'enum'              => array( 'id', 'amount', 'status', 'gateway', 'created_at' ),
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'order'     => array(
				'description'       => __( 'Order sort attribute ascending or descending.', 'nemtly-booking' ),
				'type'              => 'string',
				'default'           => 'desc',
				'enum'              => array( 'asc', 'desc' ),
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'status'    => array(
				'description'       => __( 'Limit result set to payments with specific status.', 'nemtly-booking' ),
				'type'              => 'array',
				'items'             => array(
					'type' => 'string',
					'enum' => array( 'pending', 'succeeded', 'completed', 'failed', 'refunded', 'processing' ),
				),
				'sanitize_callback' => function ( $value ) {
					return is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : array( sanitize_text_field( $value ) );
				},
				'validate_callback' => 'rest_validate_request_arg',
			),
			'gateway'   => array(
				'description'       => __( 'Limit result set to payments from specific gateway.', 'nemtly-booking' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'date_from' => array(
				'description'       => __( 'Limit result set to payments created after this date.', 'nemtly-booking' ),
				'type'              => 'string',
				'format'            => 'date',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'date_to'   => array(
				'description'       => __( 'Limit result set to payments created before this date.', 'nemtly-booking' ),
				'type'              => 'string',
				'format'            => 'date',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
		);
	}

	/**
	 * Get public item schema
	 *
	 * @return array
	 */
	public function get_public_item_schema() {
		return array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'payment',
			'type'       => 'object',
			'properties' => array(
				'id'               => array(
					'description' => __( 'Unique identifier for the payment.', 'nemtly-booking' ),
					'type'        => 'integer',
					'readonly'    => true,
				),
				'booking_id'       => array(
					'description' => __( 'The booking ID associated with this payment.', 'nemtly-booking' ),
					'type'        => 'integer',
				),
				'transaction_id'   => array(
					'description' => __( 'The transaction ID from the payment gateway.', 'nemtly-booking' ),
					'type'        => 'string',
				),
				'amount'           => array(
					'description' => __( 'The payment amount.', 'nemtly-booking' ),
					'type'        => 'number',
				),
				'currency'         => array(
					'description' => __( 'The currency code.', 'nemtly-booking' ),
					'type'        => 'string',
				),
				'status'           => array(
					'description' => __( 'The payment status.', 'nemtly-booking' ),
					'type'        => 'string',
					'enum'        => array( 'pending', 'succeeded', 'completed', 'failed', 'refunded', 'processing' ),
				),
				'gateway'          => array(
					'description' => __( 'The payment gateway used.', 'nemtly-booking' ),
					'type'        => 'string',
				),
				'gateway_response' => array(
					'description' => __( 'The response from the payment gateway.', 'nemtly-booking' ),
					'type'        => 'string',
				),
				'created_at'       => array(
					'description' => __( 'The date the payment was created.', 'nemtly-booking' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'readonly'    => true,
				),
				'updated_at'       => array(
					'description' => __( 'The date the payment was last updated.', 'nemtly-booking' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'readonly'    => true,
				),
			),
		);
	}
}
