<?php

namespace Elementor_Pay_Addons\API;

if ( !defined( 'ABSPATH' ) ) {
    exit;
}
// Exit if accessed directly
use Elementor_Pay_Addons\Stripe\Stripe_Helper;
use Elementor_Pay_Addons\Stripe\Stripe_API;
use Elementor_Pay_Addons\Stripe\Stripe_Settings;
use Elementor_Pay_Addons\Stripe\Stripe_Webhook_State;
use Elementor_Pay_Addons\Shared\Utils;
use Elementor_Pay_Addons\Core\Mailer;
use Elementor_Pay_Addons\Shared\Logger;
class Rest_API_Stripe_Webhooks_Controller extends \WP_REST_Controller {
    public function __construct() {
        $this->namespace = EPA_ADDONS_REST_API . 'stripe';
        $this->rest_base = 'webhook';
        Stripe_Webhook_State::get_monitoring_began_at();
    }

    public function register_routes() {
        register_rest_route( $this->namespace, $this->rest_base, array(array(
            'methods'             => \WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'handle_webhooks'),
            'permission_callback' => array($this, 'verify_access'),
        )) );
    }

    public function verify_access( \WP_REST_Request $request ) {
        return true;
    }

    public function handle_webhooks( \WP_REST_Request $request ) {
        try {
            // Parse the message body (and check the signature if possible)
            $webhookSecret = Stripe_Settings::get_webhook_secret();
            if ( !empty( $webhookSecret ) ) {
                try {
                    $event = \Stripe\Webhook::constructEvent( $request->get_body(), $request->get_header( 'stripe-signature' ), $webhookSecret );
                } catch ( \Exception $e ) {
                    Logger::error( 'Exception on StripeWebhook crypto:' . $e->getMessage() );
                    Stripe_Webhook_State::set_last_webhook_failure_at( time() );
                    Stripe_Webhook_State::set_last_error_reason( Stripe_Webhook_State::VALIDATION_FAILED_SIGNATURE_INVALID );
                    return http_response_code( 403 );
                }
            } else {
                $event = $request->get_json_params();
            }
            if ( !$this->is_event_from_current_site( $event ) ) {
                http_response_code( 200 );
                Logger::info( 'Skipped event from another site, id:' . $event['id'] );
                return;
            }
            if ( ($event['type'] ?? '') === 'payment_intent.succeeded' && empty( $event['data']['object']['metadata'] ) ) {
                $subscription = Stripe_Helper::get_subscription_from_payment_intent( $event['data']['object'] );
                if ( $subscription && !empty( $subscription['metadata'] ) ) {
                    $event['data']['object']['metadata'] = $subscription['metadata'];
                }
            }
            $event = apply_filters( 'elementor_pay_addons/webhook/event', $event );
            if ( empty( $event ) || empty( $event['type'] ) || !isset( $event['data']['object'] ) ) {
                Logger::info( 'Webhook event was empty or invalid after filter, skipping processing.' );
                Stripe_Webhook_State::set_last_webhook_success_at( time() );
                http_response_code( 200 );
                return;
            }
            $type = $event['type'];
            $object = $event['data']['object'];
            switch ( $type ) {
                case 'checkout.session.completed':
                    $payment_intent = Stripe_Helper::get_payment_intent_from_session( $object['id'] );
                    $txn_query = new \Elementor_Pay_Addons\Database\Transaction_Query();
                    $txn_query->update_by_transaction_id( $object['id'], [
                        'payment_status' => 'success',
                        'amount'         => $object['amount_total'],
                        'payment_method' => $payment_intent['payment_method']['type'] ?? '',
                        'customer_name'  => $object['customer_details']['name'] ?? '',
                        'customer_email' => $object['customer_details']['email'] ?? '',
                    ] );
                    break;
                case 'checkout.session.async_payment_succeeded':
                    $intent = Stripe_Helper::get_payment_intent_from_session( $object['id'] );
                    $this->send_payment_success_email( $intent, null );
                    break;
                case 'checkout.session.async_payment_failed':
                    $intent = Stripe_Helper::get_payment_intent_from_session( $object['id'] );
                    $this->send_payment_failed_email( $intent );
                    break;
                case 'charge.succeeded':
                    break;
                case 'invoice.upcoming':
                    break;
                case 'invoice.payment_succeeded':
                    break;
                case 'invoice.payment_failed':
                    break;
                case 'payment_intent.payment_failed':
                    $intent = $object;
                    $error_message = ( $intent->last_payment_error ? $intent->last_payment_error->message : "" );
                    Logger::info( 'Webhook received! PaymentIntent event ' . $intent['id'] . ' failed:' . $error_message );
                    $this->send_payment_failed_email( $intent );
                    break;
                case 'payment_intent.succeeded':
                    $intent = $object;
                    $this->send_payment_success_email( $intent, null );
                    break;
            }
            Stripe_Webhook_State::set_last_webhook_success_at( $event['created'] );
            http_response_code( 200 );
        } catch ( \Exception $e ) {
            Logger::error( 'Exception detail:', json_encode( $e ) );
            return new \WP_Error('stripe_error', $e->getMessage(), array(
                'status' => 400,
            ));
        }
    }

    public function is_redirect_based_payment_method( $charge ) {
        $payment_method = $charge['payment_method_details']['type'];
        $redirection_prone = [
            'klarna',
            'paypal',
            'ideal',
            'giropay',
            'bancontact',
            'sofort',
            'p24',
            'eps',
            'fpx',
            'bacs_debit',
            'au_becs_debit',
            'grabpay',
            'oxxo',
            'alipay',
            'wechat_pay'
        ];
        return in_array( $payment_method, $redirection_prone );
    }

    protected function get_invoice_subscription_id( $invoice ) {
        if ( !empty( $invoice['subscription'] ) ) {
            return $invoice['subscription'];
        }
        if ( !empty( $invoice['parent']['subscription_details']['subscription'] ) ) {
            return $invoice['parent']['subscription_details']['subscription'];
        }
        if ( !empty( $invoice['lines']['data'][0]['parent']['subscription_item_details']['subscription'] ) ) {
            return $invoice['lines']['data'][0]['parent']['subscription_item_details']['subscription'];
        }
        return null;
    }

    protected function get_subscription_price_id( $subscription ) {
        if ( !empty( $subscription['items']['data'][0]['price']['id'] ) ) {
            return $subscription['items']['data'][0]['price']['id'];
        }
        if ( !empty( $subscription['items']['data'][0]['price'] ) && is_string( $subscription['items']['data'][0]['price'] ) ) {
            return $subscription['items']['data'][0]['price'];
        }
        if ( !empty( $subscription->plan->id ) ) {
            return $subscription->plan->id;
        }
        return null;
    }

    function send_payment_success_email( $intent, $extra = null ) {
        $placeholders = Stripe_Helper::get_receipt_placeholders_by_payment_intent( $intent['id'] );
        $subscription = Stripe_Helper::get_subscription_from_payment_intent( $intent );
        if ( $subscription && !empty( $subscription['metadata'] ) ) {
            Stripe_Helper::append_metadata_placeholder( $subscription, $placeholders );
        }
        /**
         * Fires after a payment intent succeeds and all related data (including
         * subscription metadata for renewal payments) has been resolved.
         *
         * @param array       $intent       The Stripe PaymentIntent object.
         * @param object|null $subscription The Stripe Subscription object, or null for one-time payments.
         * @param array       $placeholders Resolved placeholders including subscription metadata.
         */
        do_action(
            'elementor_pay_addons/webhook/payment_intent_succeeded',
            $intent,
            $subscription,
            $placeholders
        );
        $email = Mailer::get_emails()['epa_email_pay_success'];
        $email->set_placeholders( $placeholders )->trigger();
        $invoice_email = Mailer::get_emails()['epa_email_invoice'];
        $invoice_email->set_placeholders( $placeholders )->trigger();
        $txn_query = new \Elementor_Pay_Addons\Database\Transaction_Query();
        $txn_query->update_by_transaction_id( $intent['id'], [
            'payment_status' => 'success',
            'customer_name'  => $placeholders['customer.name'] ?? '',
            'customer_email' => $placeholders['customer.email'] ?? '',
            'payment_method' => $placeholders['payment_method.type'] ?? '',
        ] );
    }

    function send_payment_failed_email( $intent ) {
        // Send an email to the customer asking them to retry their order
        $placeholders = Stripe_Helper::get_receipt_placeholders_by_payment_intent( $intent['id'], $intent['last_payment_error'] );
        $email = Mailer::get_emails()['epa_email_pay_fail'];
        $email->set_placeholders( $placeholders )->trigger();
        $txn_query = new \Elementor_Pay_Addons\Database\Transaction_Query();
        $txn_query->update_by_transaction_id( $intent['id'], [
            'payment_status' => 'failed',
            'comment'        => $placeholders['error.message'] ?? '',
            'customer_name'  => $placeholders['customer.name'] ?? '',
            'customer_email' => $placeholders['customer.email'] ?? '',
            'payment_method' => $placeholders['payment_method.type'] ?? '',
        ] );
    }

    function is_event_from_current_site( $event ) {
        $referer = $event['data']['object']['metadata']['referer'] ?? null;
        if ( $referer === null ) {
            return true;
        }
        return $referer === $this->get_site_domain();
    }

    protected function get_site_domain() {
        return wp_parse_url( get_site_url(), PHP_URL_HOST );
    }

}
