<?php

namespace SugarCalendarBookings\Payments\Admin;

use Exception;
use SugarCalendarBookings\Admin\Area;
use SugarCalendarBookings\Appointments\Admin\AppointmentDetailsPage;
use SugarCalendarBookings\Notifications\PaymentNotifier;
use SugarCalendarBookings\Payments\Gateways\Onsite\OnSitePaymentGateway;
use SugarCalendarBookings\Payments\Gateways\PaymentGatewayRegistry;
use SugarCalendarBookings\Payments\PaymentModel;
use SugarCalendarBookings\Payments\PaymentsHelpers;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Support\Admin\AbstractPage;
use SugarCalendarBookings\Support\Admin\Notices;
use SugarCalendarBookings\Support\Database\AbstractModel;
use SugarCalendarBookings\Support\Database\ModelQueryBuilder;
use SugarCalendarBookings\Support\DataProcessing\InputProcessor;
use SugarCalendarBookings\Support\Helpers\Date;
use SugarCalendarBookings\Support\Helpers\UI;

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

/**
 * Class PaymentDetailsPage.
 *
 * @since 1.0.0
 */
class PaymentsDetailsPage extends AbstractPage {

	/**
	 * Page title.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public static function get_title() {

		return esc_html__( 'Payment Details', 'sugar-calendar-bookings-scheduling-appointments-lite' );
	}

	/**
	 * Page slug.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public static function get_slug() {

		return 'payments';
	}

	/**
	 * View slug.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public static function get_view_slug() {

		return 'details';
	}

	/**
	 * Hooks.
	 *
	 * @since 1.0.0
	 */
	public function hooks() {

		add_action( 'admin_init', [ $this, 'validate_current_payment' ] );
		add_filter( 'screen_options_show_screen', '__return_false' );
		add_action( 'sugar_calendar_bookings_admin_page_subheader_actions_left', [ $this, 'subheader_actions_left' ] );
		add_action( 'sugar_calendar_bookings_admin_page_subheader_actions_right', [ $this, 'subheader_actions_right' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
	}

	/**
	 * Page URL.
	 *
	 * @since 1.0.0
	 *
	 * @param null|int $payment_id Payment ID.
	 *
	 * @return string
	 */
	public static function get_url( $payment_id = null ) {

		$url = parent::get_url();

		if ( ! is_null( $payment_id ) ) {
			$url = add_query_arg( 'payment_id', $payment_id, $url );
		}

		return $url;
	}

	/**
	 * Subheader left controls.
	 *
	 * @since 1.0.0
	 *
	 * @param AbstractPage $page The page instance.
	 */
	public function subheader_actions_left( $page ) {

		UI::button(
			[
				'link'  => PaymentsListPage::get_url(),
				'size'  => 'sm',
				'class' => 'sugar-calendar-bookings-back-to',
				'text'  => esc_html__( 'Back to Payments', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			]
		);
	}

	/**
	 * Get base query for payments.
	 *
	 * @since 1.0.0
	 *
	 * @return ModelQueryBuilder
	 */
	private function get_payments_query() {

		return PaymentModel::query()
			->select( [ 'id' ] )
			->where( 'trashed_at', 'IS NULL' );
	}

	/**
	 * Get payment position.
	 *
	 * @since 1.0.0
	 *
	 * @param int $payment_id Current payment ID.
	 *
	 * @return int
	 */
	private function get_payment_position( $payment_id ) {

		$count = $this->get_payments_query()
			->where( 'id', '<', $payment_id, '%d' )
			->count();

		return $count + 1;
	}

	/**
	 * Get the previous payment.
	 *
	 * @since 1.0.0
	 *
	 * @param int $payment_id Current payment ID.
	 *
	 * @return AbstractModel|null
	 */
	private function get_previous_payment( $payment_id ) {

		return $this->get_payments_query()
			->where( 'id', '<', $payment_id, '%d' )
			->order_by( 'id', 'DESC' )
			->limit( 1 )
			->first();
	}

	/**
	 * Get the next payment.
	 *
	 * @since 1.0.0
	 *
	 * @param int $payment_id Current payment ID.
	 *
	 * @return AbstractModel|null
	 */
	private function get_next_payment( $payment_id ) {

		return $this->get_payments_query()
			->where( 'id', '>', $payment_id, '%d' )
			->order_by( 'id' )
			->limit( 1 )
			->first();
	}

	/**
	 * Subheader right controls.
	 *
	 * @since 1.0.0
	 *
	 * @param AbstractPage $page The page instance.
	 */
	public function subheader_actions_right( $page ) {

		$total = $this->get_payments_query()->count();

		if ( $total === 0 ) {
			return;
		}

		$current_payment          = $this->get_current_payment();
		$current_payment_position = $this->get_payment_position( $current_payment->id );
		$previous_payment         = $this->get_previous_payment( $current_payment->id );
		$next_payment             = $this->get_next_payment( $current_payment->id );
		?>
		<div class="sugar-calendar-bookings-pagination">
			<span>
				<?php
				echo esc_html(
					sprintf(
					/* Translators: %1$s - current payment index; %2$s - total payments. */
						__( 'Payment %1$s of %2$s', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
						$current_payment_position,
						$total
					)
				);
				?>
			</span>
			<div class="sugar-calendar-bookings-pagination__controls">
				<?php // phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace ?>
				<a <?php if ( ! is_null( $previous_payment ) ) : ?> href="<?php echo esc_url( self::get_url( $previous_payment->id ) ); ?>"<?php endif; ?>
					class="sugar-calendar-bookings-pagination__control sugar-calendar-bookings-pagination__control--prev"
					<?php disabled( is_null( $previous_payment ) ); ?>></a>
				<span class="sugar-calendar-bookings-pagination__control sugar-calendar-bookings-pagination__control--current"><?php echo esc_html( $current_payment_position ); ?></span>
				<a <?php if ( ! is_null( $next_payment ) ) : ?> href="<?php echo esc_url( self::get_url( $next_payment->id ) ); ?>"<?php endif; ?>
					class="sugar-calendar-bookings-pagination__control sugar-calendar-bookings-pagination__control--next"
					<?php disabled( is_null( $next_payment ) ); ?>></a>
				<?php // phpcs:enable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace ?>
			</div>
		</div>
		<?php
	}

	/**
	 * Validate current payment.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function validate_current_payment() {

		$payment = $this->get_current_payment();

		if ( is_null( $payment ) ) {
			wp_die( esc_html__( 'Payment not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}
	}

	/**
	 * Get the current payment.
	 *
	 * @since 1.0.0
	 *
	 * @return PaymentModel
	 */
	public function get_current_payment() {

		$payment_id = isset( $_GET['payment_id'] ) ? absint( $_GET['payment_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		return PaymentModel::get( $payment_id );
	}

	/**
	 * Register metaboxes.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	private function register_metaboxes() {

		$current_screen  = get_current_screen();
		$current_payment = $this->get_current_payment();

		add_meta_box(
			'payment-overview',
			sprintf(
				'#%1$s - %2$s',
				$current_payment->id,
				$current_payment->customer->name
			),
			[ $this, 'display_metabox_overview' ],
			$current_screen->id,
			'normal',
			'high'
		);

		add_meta_box(
			'payment-details',
			__( 'Details', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			[ $this, 'display_metabox_details' ],
			$current_screen->id,
			'side',
			'high'
		);
	}

	/**
	 * Get payment statuses.
	 *
	 * @since 1.0.0
	 *
	 * @return array Payment statuses.
	 */
	private function get_payment_statuses() {

		return [
			PaymentModel::STATUS_COMPLETED => esc_html__( 'Completed', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			PaymentModel::STATUS_PENDING   => esc_html__( 'Pending', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			PaymentModel::STATUS_FAILED    => esc_html__( 'Failed', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			PaymentModel::STATUS_REFUNDED  => esc_html__( 'Refunded', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
		];
	}

	/**
	 * Display the overview metabox.
	 *
	 * @since 1.0.0
	 *
	 * @param PaymentModel $payment Current payment.
	 *
	 * @return void
	 */
	public function display_metabox_overview( $payment ) {

		$is_refunded    = $payment->status === PaymentModel::STATUS_REFUNDED;
		$should_disable = $is_refunded && $payment->gateway !== OnSitePaymentGateway::ID;
		?>
		<form action="<?php echo esc_url( self::get_url( $payment->id ) ); ?>" method="post">
			<?php wp_nonce_field( Area::SLUG ); ?>
			<input type="hidden" name="<?php echo esc_attr( Area::SLUG . '-submit' ); ?>">
			<input type="hidden" name="payment_id" value="<?php echo esc_attr( $payment->id ); ?>">
			<table class="form-table striped">
				<tr>
					<th scope="row"><?php esc_html_e( 'Tracking ID', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></th>
					<td><?php echo esc_html( $payment->tracking_id ); ?></td>
				</tr>
				<tr class="payment-overview__status">
					<th scope="row"><?php esc_html_e( 'Payment Status', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></th>
					<td>
						<?php
						UI::select_input(
							[
								'id'       => 'status',
								'name'     => [ 'payment', 'status' ],
								'options'  => $this->get_payment_statuses(),
								'value'    => $payment->status,
								'disabled' => $should_disable,
							]
						);
						?>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Customer Details', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></th>
					<td>
						<?php echo esc_html( $payment->customer->name ); ?>
						<?php if ( ! empty( $payment->customer->email ) ) : ?>
							<?php
							echo wp_kses(
								sprintf(
									'(<a href="mailto:%1$s">%1$s</a>)',
									$payment->customer->email
								),
								[
									'a' => [
										'href' => [],
									],
								]
							);
							?>
						<?php endif; ?>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Service', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></th>
					<td>
						<?php
						$appointments = array_map(
							fn( $appointment ) => sprintf( '<a href="%1$s">%2$s</a>', esc_url( AppointmentDetailsPage::get_url( $appointment->id ) ), esc_html( $appointment->service->name ) ),
							$payment->appointments
						);

						echo wp_kses(
							implode( '<br>', $appointments ),
							[
								'a'  => [
									'href'            => [],
									'data-service-id' => [],
								],
								'br' => [],
							]
						);
						?>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Appointment Date', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></th>
					<td>
						<?php
						$appointments = array_map(
							fn( $appointment ) => Date::format_display_date( $appointment->start_at ),
							$payment->appointments
						);

						echo wp_kses(
							implode( '<br>', $appointments ),
							[
								'br' => [],
							]
						);
						?>
					</td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Total', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></th>
					<td><?php echo esc_html( PaymentsHelpers::format_currency( $payment->total_amount ) ); ?></td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Purchase Date', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></th>
					<td><?php echo esc_html( Date::format_display_date( $payment->created_at ) ); ?></td>
				</tr>
			</table>
			<div class="payment-overview__actions">
				<button type="submit" class="sugar-calendar-bookings-btn sugar-calendar-bookings-btn-sm sugar-calendar-bookings-btn-secondary"><?php esc_html_e( 'Update Payment', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></button>
				<?php if ( ! $is_refunded ) : ?>
					<button type="submit" class="sugar-calendar-bookings-btn sugar-calendar-bookings-btn-sm sugar-calendar-bookings-btn-tertiary" data-set-status="refunded"><?php esc_html_e( 'Refund', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></button>
				<?php endif; ?>
			</div>
		</form>
		<?php
	}

	/**
	 * Get a payment trash url.
	 *
	 * @since 1.0.0
	 *
	 * @param int $payment_id Payment ID.
	 *
	 * @return string
	 */
	private function get_payment_trash_url( $payment_id ) {

		return esc_url(
			wp_nonce_url(
				add_query_arg(
					[
						'action'     => 'trash',
						'payment_id' => $payment_id,
					],
					PaymentsListPage::get_url()
				),
				'bulk-payments'
			)
		);
	}

	/**
	 * Display the details metabox.
	 *
	 * @since 1.0.0
	 *
	 * @param PaymentModel $payment Current payment.
	 *
	 * @return void
	 */
	public function display_metabox_details( $payment ) {

		$appointment     = ! empty( $payment->appointments ) ? array_values( $payment->appointments )[0] : null;
		$appointment_url = $appointment ? AppointmentDetailsPage::get_url( $appointment->id ) : '#';
		?>
		<ul>
			<li class="payment-details__id">
				<?php
				echo esc_html(
					sprintf(
						'%1$s: #%2$s',
						__( 'Payment ID', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
						$payment->id
					)
				);
				?>
			</li>
			<li class="payment-details__submitted_at">
				<span>
					<?php
					echo wp_kses(
						sprintf(
							'%1$s: <strong>%2$s</strong>',
							__( 'Submitted', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
							Date::format_display_date( $payment->created_at )
						),
						[
							'span'   => [],
							'strong' => [],
						]
					);
					?>
				</span>
			</li>
			<li class="payment-details__processor">
				<span>
					<?php
					echo wp_kses(
						sprintf(
							'%1$s: <strong>%2$s</strong>',
							__( 'Processor', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
							PaymentsHelpers::get_payment_gateway_display_name( $payment )
						),
						[
							'strong' => [],
						]
					);
					?>
				</span>
			</li>
			<li class="payment-details__appointment">
				<span>
					<?php
					$appointments = array_map(
						fn( $appointment ) => sprintf( '<a href="%1$s"><strong>#%2$s</strong></a>', esc_url( AppointmentDetailsPage::get_url( $appointment->id ) ), intval( $appointment->id ) ),
						$payment->appointments
					);

					echo wp_kses(
						sprintf(
							'%1$s: %2$s',
							__( 'Appointment ID', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
							implode( ', ', $appointments )
						),
						[
							'strong' => [],
							'a'      => [
								'href' => [],
							],
						]
					);
					?>
				</span>
			</li>
		</ul>
		<div class="payment-details__actions">
			<a class="payment-details__actions__delete" href="<?php echo esc_url( $this->get_payment_trash_url( $payment->id ) ); ?>"><?php esc_html_e( 'Move to Trash', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></a>
			<a class="button button-secondary button-large" href="<?php echo esc_url( $appointment_url ); ?>"><?php esc_html_e( 'View Appointment', 'sugar-calendar-bookings-scheduling-appointments-lite' ); ?></a>
		</div>
		<?php
	}

	/**
	 * Display page content.
	 *
	 * @since 1.0.0
	 */
	public function display_content() {

		$this->register_metaboxes();

		$current_screen  = get_current_screen();
		$current_payment = $this->get_current_payment();
		?>
		<div class="payments-details-page wrap">
			<div id="poststuff">
				<div id="post-body" class="metabox-holder columns-2">
					<!-- Main content area -->
					<div id="post-body-content">
						<div id="postbox-container-2" class="postbox-container">
							<div id="normal-sortables" class="meta-box-sortables ui-sortable">
								<?php do_meta_boxes( $current_screen->id, 'normal', $current_payment ); ?>
							</div>
						</div>
						<div id="postbox-container-3" class="postbox-container">
							<div id="advanced-sortables" class="meta-box-sortables ui-sortable">
								<?php do_meta_boxes( $current_screen->id, 'advanced', $current_payment ); ?>
							</div>
						</div>
					</div>

					<!-- Sidebar -->
					<div id="postbox-container-1" class="postbox-container">
						<div id="side-sortables" class="meta-box-sortables ui-sortable">
							<?php do_meta_boxes( $current_screen->id, 'side', $current_payment ); ?>
						</div>
					</div>
				</div>
			</div>
		</div>
		<?php
		wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
		wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
	}

	/**
	 * Get post data processing rules.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_post_data_processing_rules() {

		$payment_statuses = implode( ',', array_keys( $this->get_payment_statuses() ) );

		return [
			'payment.status' => "required|string|in:$payment_statuses",
		];
	}

	/**
	 * Handle POST requests.
	 *
	 * @since 1.0.0
	 *
	 * @param array $post_data Post data.
	 */
	public function handle_post( $post_data = [] ) {

		$payment         = $this->get_current_payment();
		$previous_status = $payment->status;
		$new_status      = $post_data['payment']['status'];

		if ( $previous_status === PaymentModel::STATUS_REFUNDED && $new_status !== PaymentModel::STATUS_REFUNDED && $payment->gateway !== OnSitePaymentGateway::ID ) {
			Plugin::get( Notices::class )->error( __( 'Refunded payments cannot be changed to another status.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );

			return;
		}

		if ( $new_status === PaymentModel::STATUS_REFUNDED && $previous_status !== PaymentModel::STATUS_REFUNDED && $previous_status === PaymentModel::STATUS_COMPLETED ) {
			try {
				$processor = Plugin::get( PaymentGatewayRegistry::class )
					->get( $payment->gateway )
					->get_processor();

				$processor->refund_payment( $payment );
			} catch ( Exception $e ) {
				Plugin::get( Notices::class )->error( $e->getMessage() );

				return;
			}
		}

		$payment->status = $new_status;

		if ( $payment->save() ) {
			Plugin::get( PaymentNotifier::class )->notify( $payment, $previous_status );

			Plugin::get( Notices::class )->success( __( 'Payment updated.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		} else {
			Plugin::get( Notices::class )->error( __( 'Failed to update payment.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}
	}

	/**
	 * Enqueue scripts.
	 *
	 * @since 1.0.0
	 */
	public function enqueue_scripts() {

		wp_enqueue_script( 'postbox' );
	}

	/**
	 * Get page script data.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_script_data() {

		return [
			'screen_id' => get_current_screen()->id ?? null,
		];
	}
}
