<?php
/**
 * DESCRIPTION: Payment service handling Stripe payment initiation, webhook processing,
 * and payment record creation. Bridges booking confirmation with payment gateway.
 *
 * @package NemtlyBooking\Services
 */

namespace NemtlyBooking\Services;

use NemtlyBooking\Core\Status;
use NemtlyBooking\Interfaces\Payment_Gateway_Interface;
use NemtlyBooking\Repositories\Payment_Repository;
use NemtlyBooking\Models\Payment;

/**
 * Class Payment_Service
 */
class Payment_Service {
    /**
     * Payment repository
     *
     * @var Payment_Repository
     */
    private $payment_repository;

    /**
     * Booking service
     *
     * @var Booking_Service
     */
    private $booking_service;

    /**
     * Payment gateway
     *
     * @var Payment_Gateway_Interface
     */
    private $gateway;

    /**
     * Settings Service
     *
     * @var Settings_Service
     */
    private $settings_service;

    /**
     * Constructor
     *
     * @param Payment_Repository        $payment_repository Payment repository.
     * @param Payment_Gateway_Interface $gateway            Payment gateway.
     * @param Settings_Service          $settings_service   Settings service.
     * @param Booking_Service           $booking_service    Booking service.
     */
    public function __construct(
        Payment_Repository $payment_repository,
        Payment_Gateway_Interface $gateway,
        Settings_Service $settings_service,
        Booking_Service $booking_service
    ) {
        $this->payment_repository = $payment_repository;
        $this->gateway            = $gateway;
        $this->settings_service   = $settings_service;
        $this->booking_service    = $booking_service;
    }

    /**
     * Initiate payment for payable entity
     *
     * @param \NemtlyBooking\Interfaces\Payable $payable Payable entity.
     *
     * @return array Result with client_secret.
     * @throws \Exception If payment fails.
     */
    public function initiate_payment( \NemtlyBooking\Interfaces\Payable $payable ): array {
        $currency = $this->settings_service->get_setting( 'currency', 'USD' );
        $amount   = $payable->get_payment_amount();

        // Create intent via gateway
        $result = $this->gateway->process_payment(
            $amount,
            $currency,
            [
				'payable_id'   => $payable->get_id(),
				'payable_type' => $payable->get_type(),
				'booking_id'   => $payable->get_type() === 'booking' ? $payable->get_id() : 0, // Fallback for legacy
			]
        );

        if ( ! $result['success'] ) {
            throw new \Exception( $result['message'] ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
        }
        return $result;
    }

    /**
     * Handle Stripe Webhook
     *
     * @param string $payload    Raw payload.
     * @param string $sig_header Signature header.
     *
     * @return void
     */
    public function handle_webhook( string $payload, string $sig_header ): void {
        try {
            $event = $this->gateway->verify_webhook( $payload, $sig_header );
        } catch ( \Exception $e ) {
            http_response_code( 400 );
            exit();
        }

        // Handle the event
        switch ( $event->type ) {
            case 'payment_intent.succeeded':
                $payment_intent = $event->data->object;
                $this->handle_payment_success( $payment_intent );
                break;
            case 'payment_intent.payment_failed':
                $payment_intent = $event->data->object;
                $this->handle_payment_failure( $payment_intent );
                break;
            default:
                break;
        }

        http_response_code( 200 );
    }

    /**
     * Handle successful payment
     *
     * @param object $payment_intent Stripe PaymentIntent object.
     */
    private function handle_payment_success( object $payment_intent ): void {
        $payable_id   = $payment_intent->metadata->payable_id ?? 0;
        $payable_type = $payment_intent->metadata->payable_type ?? 'booking';

        // Backward Compatibility for Intetnts created before this change
        if ( empty( $payable_id ) && ! empty( $payment_intent->metadata->booking_id ) ) {
            $payable_id   = $payment_intent->metadata->booking_id;
            $payable_type = 'booking';
        }

        if ( ! $payable_id ) {
            return;
        }

        // Check if payment already recorded
        $existing = $this->payment_repository->find_by_transaction_id( $payment_intent->id );

        if ( $existing ) {
            return;
        }

        // Create payment record
        $payment_data = [
            'payable_id'       => $payable_id,
            'payable_type'     => $payable_type,
            'booking_id'       => 'booking' === $payable_type ? $payable_id : 0,
            'transaction_id'   => $payment_intent->id,
            'amount'           => $payment_intent->amount / 100, // Convert cents to logic unit
            'currency'         => strtoupper( $payment_intent->currency ),
            'status'           => Status::PAYMENT_SUCCEEDED,
            'gateway'          => $this->gateway->get_id(),
            'gateway_response' => json_encode( $payment_intent ),
        ];

        $payment = new Payment( $payment_data );

        $saved_payment = $this->payment_repository->create( $payment );

        // Trigger generic payment method success hook
        do_action( 'nemtly_booking_payment_succeeded', $saved_payment, $payable_id, $payable_type );

        // Trigger specific payable type hook
        do_action( "nemtly_booking_payment_succeeded_{$payable_type}", $saved_payment, $payable_id );

        // Update booking status if it's a booking
        if ( 'booking' === $payable_type ) {
            try {
                $this->booking_service->update_booking( $payable_id, [ 'booking_status' => Status::BOOKING_CONFIRMED ] );
            } catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
                // Don't fail the webhook; booking can be confirmed manually if needed.
            }
        }
    }

    /**
     * Handle failed payment
     *
     * @param object $payment_intent Stripe PaymentIntent object.
     */
    private function handle_payment_failure( object $payment_intent ): void {
        $payable_id   = $payment_intent->metadata->payable_id ?? 0;
        $payable_type = $payment_intent->metadata->payable_type ?? 'booking';

        // Trigger failure hook
        do_action( 'nemtly_booking_payment_failed', $payment_intent, $payable_id, $payable_type );
    }
}
