<?php

namespace SugarCalendarBookings\Payments;

use Exception;
use SugarCalendarBookings\Appointments\AppointmentModel;
use SugarCalendarBookings\Customers\CustomerModel;

/**
 * Payment service.
 *
 * @since 1.0.0
 */
class PaymentService {

	/**
	 * Authorize a payment using the specified gateway.
	 *
	 * @since 1.0.0
	 *
	 * @param string     $payment_gateway_id Payment gateway ID.
	 * @param PaymentDTO $dto                Data transfer object.
	 *
	 * @return PaymentModel Payment model instance.
	 *
	 * @throws Exception If gateway cannot be created or payment fails.
	 */
	public function create( $payment_gateway_id, PaymentDTO $dto ) {

		$this->validate_payment( $dto );

		$tracking_id = ( new PaymentTracking() )->get_id( $payment_gateway_id );

		$payment = new PaymentModel(
			[
				'total_amount'    => $dto->total_amount,
				'subtotal_amount' => $dto->subtotal_amount,
				'currency'        => $dto->currency,
				'gateway'         => $payment_gateway_id,
				'tracking_id'     => $tracking_id,
			]
		);

		$payment->customer()->associate( $dto->customer_id );

		if ( ! $payment->save() ) {
			throw new Exception( esc_html__( 'Failed to process payment.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$this->attach_item( $payment, $dto );

		return $payment;
	}

	/**
	 * Validate a payment.
	 *
	 * @since 1.0.0
	 *
	 * @param PaymentDTO $dto Data transfer object.
	 *
	 * @return void
	 * @throws Exception If payment validation fails.
	 */
	private function validate_payment( $dto ) {

		$customer = isset( $dto->customer_id ) ? CustomerModel::get( $dto->customer_id ) : null;

		if ( ! $customer ) {
			throw new Exception( esc_html__( 'Customer not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		// TODO: support polymorphic relations.
		$appointment = isset( $dto->item_id ) ? AppointmentModel::get( $dto->item_id ) : null;

		if ( ! $appointment ) {
			throw new Exception( esc_html__( 'Appointment not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}
	}

	/**
	 * Attach item to payment.
	 *
	 * @since 1.0.0
	 *
	 * @param PaymentModel $payment The payment model.
	 * @param PaymentDTO   $dto     Data transfer object.
	 */
	private function attach_item( $payment, $dto ) {

		// TODO: support polymorphic relations.
		if ( isset( $dto->item_id ) ) {
			$payment->appointments()->attach(
				$dto->item_id,
				[
					'item_type'       => AppointmentModel::get_type(),
					'total_amount'    => $payment->total_amount,
					'subtotal_amount' => $payment->subtotal_amount,
				],
				[
					'item_type'       => '%s',
					'total_amount'    => '%f',
					'subtotal_amount' => '%f',
				]
			);
		}
	}

	/**
	 * Format payment data for capture responses.
	 *
	 * @since 1.0.0
	 *
	 * @param PaymentModel $payment Payment model instance.
	 *
	 * @return array
	 */
	public function format_payment_data( $payment ) {

		return [
			'id'      => $payment->id,
			'gateway' => $payment->gateway,
			'amount'  => PaymentsHelpers::format_currency( $payment->total_amount ),
			'status'  => $payment->status,
		];
	}
}
