<?php

namespace BookWellAB;

defined('ABSPATH') || exit;

if (!class_exists('BookWellAB_Paypal')) {

    class BookWellAB_Paypal
    {

        private static $instance = null;

        /**
         * @return self
         */
        public static function get_instance()
        {
            if (self::$instance === null) {
                self::$instance = new self();
            }
            return self::$instance;
        }

        public function __construct()
        {
            add_action('wp_ajax_bookwellab_paypal_create_order', [$this, 'ajax_create_order']);
            add_action('wp_ajax_nopriv_bookwellab_paypal_create_order', [$this, 'ajax_create_order']);
            add_action('wp_ajax_bookwellab_paypal_confirm_order', [$this, 'ajax_confirm_order']);
            add_action('wp_ajax_nopriv_bookwellab_paypal_confirm_order', [$this, 'ajax_confirm_order']);

            add_action('wp_ajax_bookwellab_paypal_cancel_order', [$this, 'ajax_cancel_order']);
            add_action('wp_ajax_nopriv_bookwellab_paypal_cancel_order', [$this, 'ajax_cancel_order']);

            add_filter('bookwellab_payment_gateway_options', [$this, 'filter_payment_gateway_options'], 10, 2);

            add_action('init', [$this, 'maybe_schedule_stale_paypal_cleanup']);
            add_action('bookwellab_cleanup_stale_paypal_appointments', [$this, 'run_stale_paypal_cleanup']);
        }

        /**
         * Schedule hourly cleanup for abandoned PayPal checkout appointments.
         *
         * @return void
         */
        public function maybe_schedule_stale_paypal_cleanup()
        {
            if (wp_next_scheduled('bookwellab_cleanup_stale_paypal_appointments')) {
                return;
            }

            wp_schedule_event(time() + HOUR_IN_SECONDS, 'hourly', 'bookwellab_cleanup_stale_paypal_appointments');
        }

        /**
         * Cancel pending PayPal appointments older than one hour.
         *
         * @return void
         */
        public function run_stale_paypal_cleanup()
        {
            BookWellAB_Booking_Helper::cleanup_stale_pending_paypal_appointments(60);
        }

        /**
         * PayPal-supported currency codes (minimal list).
         *
         * @return string[]
         */
        public static function get_supported_currencies()
        {
            $currencies = [
                'AUD', 'BRL', 'CAD', 'CHF', 'CNY', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD',
                'HUF', 'ILS', 'INR', 'JPY', 'MXN', 'MYR', 'NOK', 'NZD', 'PHP', 'PLN',
                'RUB', 'SEK', 'SGD', 'THB', 'TWD', 'USD',
                'AED', 'ARS', 'BGN', 'COP', 'HRK', 'IDR', 'KRW', 'PEN', 'RON', 'VND',
            ];

            /**
             * Filter PayPal-supported currency codes used to show/hide the gateway.
             *
             * @param string[] $currencies ISO 4217 currency codes.
             */
            return apply_filters('bookwellab_paypal_supported_currencies', $currencies);
        }

        /**
         * Whether a currency code is supported by PayPal Checkout.
         *
         * @param string $currency ISO 4217 currency code.
         * @return bool
         */
        public static function is_currency_supported($currency)
        {
            $currency = strtoupper(sanitize_text_field((string) $currency));
            if ($currency === '') {
                return false;
            }

            return in_array($currency, self::get_supported_currencies(), true);
        }

        /**
         * Whether the site currency from general settings is supported by PayPal.
         *
         * @return bool
         */
        public static function is_site_currency_supported()
        {
            $settings = BookWellAB_Settings::get_settings();
            $currency = isset($settings['general']['currency_code']) ? (string) $settings['general']['currency_code'] : 'USD';

            return self::is_currency_supported($currency);
        }

        /**
         * @return array<string, mixed>
         */
        public static function get_payment_settings()
        {
            $settings = BookWellAB_Settings::get_settings();
            return isset($settings['payment']) && is_array($settings['payment']) ? $settings['payment'] : [];
        }

        /**
         * @return string sandbox|live
         */
        public static function get_active_mode()
        {
            $payment = self::get_payment_settings();
            $mode = isset($payment['paypal_active_mode']) ? sanitize_key($payment['paypal_active_mode']) : 'sandbox';
            return $mode === 'live' ? 'live' : 'sandbox';
        }

        /**
         * @return array{client_id: string, client_secret: string, mode: string}
         */
        public static function get_active_credentials()
        {
            $payment = self::get_payment_settings();
            $mode = self::get_active_mode();
            $prefix = $mode === 'live' ? 'paypal_live_' : 'paypal_sandbox_';

            return [
                'client_id' => isset($payment[$prefix . 'client_id']) ? trim((string) $payment[$prefix . 'client_id']) : '',
                'client_secret' => isset($payment[$prefix . 'client_secret']) ? trim((string) $payment[$prefix . 'client_secret']) : '',
                'mode' => $mode,
            ];
        }

        /**
         * Whether PayPal is enabled in settings with credentials for the active mode.
         *
         * @return bool
         */
        public static function is_configured()
        {
            $payment = self::get_payment_settings();
            if (!isset($payment['enable_paypal']) || $payment['enable_paypal'] !== 'yes') {
                return false;
            }

            $credentials = self::get_active_credentials();
            if ($credentials['client_id'] === '' || $credentials['client_secret'] === '') {
                return false;
            }

            $settings = BookWellAB_Settings::get_settings();
            if (!self::is_site_currency_supported()) {
                return false;
            }

            return true;
        }

        /**
         * Whether PayPal should appear on the frontend booking form.
         *
         * @return bool
         */
        public static function is_frontend_enabled()
        {
            return self::is_configured();
        }

        /**
         * @return string
         */
        public static function get_frontend_client_id()
        {
            if (!self::is_frontend_enabled()) {
                return '';
            }
            return self::get_active_credentials()['client_id'];
        }

        /**
         * @return string
         */
        public static function get_frontend_currency()
        {
            $settings = BookWellAB_Settings::get_settings();
            $currency = isset($settings['general']['currency_code']) ? strtoupper((string) $settings['general']['currency_code']) : 'USD';
            return $currency !== '' ? $currency : 'USD';
        }

        /**
         * @return string
         */
        public static function get_gateway_label()
        {
            $payment = self::get_payment_settings();
            if (!empty($payment['paypal_label'])) {
                return sanitize_text_field($payment['paypal_label']);
            }
            return esc_html__('PayPal', 'bookwellnow-appointment-booking');
        }

        /**
         * @param array<int, array<string, string>> $options
         * @param array<string, mixed>              $payment_settings
         * @return array<int, array<string, string>>
         */
        public function filter_payment_gateway_options($options, $payment_settings)
        {
            if (self::is_configured()) {
                $options[] = [
                    'value' => 'paypal',
                    'label' => self::get_gateway_label(),
                ];
            }
            return $options;
        }

        /**
         * @return string
         */
        private function get_api_host()
        {
            return self::get_active_mode() === 'live'
                ? 'https://api-m.paypal.com'
                : 'https://api-m.sandbox.paypal.com';
        }

        /**
         * @return string|\WP_Error
         */
        private function get_access_token()
        {
            $credentials = self::get_active_credentials();
            if ($credentials['client_id'] === '' || $credentials['client_secret'] === '') {
                return new \WP_Error('paypal_config', esc_html__('PayPal is not configured.', 'bookwellnow-appointment-booking'));
            }

            $response = wp_remote_post(
                $this->get_api_host() . '/v1/oauth2/token',
                [
                    'headers' => [
                        'Authorization' => 'Basic ' . base64_encode($credentials['client_id'] . ':' . $credentials['client_secret']),
                    ],
                    'body' => [
                        'grant_type' => 'client_credentials',
                    ],
                    'timeout' => 30,
                ]
            );

            if (is_wp_error($response)) {
                return $response;
            }

            $body = json_decode(wp_remote_retrieve_body($response), true);
            if (!is_array($body) || empty($body['access_token'])) {
                $message = esc_html__('Unable to authenticate with PayPal.', 'bookwellnow-appointment-booking');
                if (is_array($body) && !empty($body['error_description'])) {
                    $message = sanitize_text_field((string) $body['error_description']);
                }
                return new \WP_Error('paypal_auth', $message);
            }

            return (string) $body['access_token'];
        }

        /**
         * Write a PayPal gateway debug log entry when enabled in settings.
         *
         * @param string $event  Event slug.
         * @param mixed  $data   Payload.
         * @param int    $ref_id Appointment ID.
         * @return void
         */
        private function debug_log($event, $data, $ref_id = 0)
        {
            BookWellAB_Payment_Debug_Log::log(
                'paypal',
                $event,
                'BookWellAB_Paypal',
                $data,
                $ref_id
            );
        }

        /**
         * @param int    $appointment_id Appointment ID.
         * @param float  $amount         Payable amount.
         * @param string $currency       Currency code.
         * @param string $description    Order description.
         * @return string|\WP_Error PayPal order ID.
         */
        public function create_checkout_order($appointment_id, $amount, $currency, $description)
        {
            $token = $this->get_access_token();
            if (is_wp_error($token)) {
                $this->debug_log('paypal_api_error', [
                    'step' => 'get_access_token',
                    'message' => $token->get_error_message(),
                    'code' => $token->get_error_code(),
                ]);
                return $token;
            }

            $currency = strtoupper(sanitize_text_field($currency));
            $value = self::format_paypal_amount($amount, $currency);

            $body = [
                'intent' => 'CAPTURE',
                'purchase_units' => [
                    [
                        'reference_id' => (string) absint($appointment_id),
                        'description' => sanitize_text_field($description),
                        'amount' => [
                            'currency_code' => $currency,
                            'value' => $value,
                        ],
                    ],
                ],
            ];

            $response = wp_remote_post(
                $this->get_api_host() . '/v2/checkout/orders',
                [
                    'headers' => [
                        'Content-Type' => 'application/json',
                        'Authorization' => 'Bearer ' . $token,
                    ],
                    'body' => wp_json_encode($body),
                    'timeout' => 30,
                ]
            );

            if (is_wp_error($response)) {
                $this->debug_log('paypal_api_error', [
                    'step' => 'create_checkout_order',
                    'message' => $response->get_error_message(),
                ]);
                return $response;
            }

            $order_data = json_decode(wp_remote_retrieve_body($response), true);
            $status_code = (int) wp_remote_retrieve_response_code($response);
            if ($status_code >= 400 || !is_array($order_data) || empty($order_data['id'])) {
                $this->debug_log('paypal_api_error', [
                    'step' => 'create_checkout_order',
                    'status_code' => $status_code,
                    'currency' => $currency,
                    'amount' => $value,
                    'response' => $order_data,
                ]);
                return new \WP_Error(
                    'paypal_order',
                    self::parse_paypal_order_error_message($order_data, $currency),
                    ['status_code' => $status_code]
                );
            }

            return sanitize_text_field((string) $order_data['id']);
        }

        /**
         * Build a user-facing message from a PayPal Orders API error payload.
         *
         * @param mixed  $order_data Decoded PayPal error response.
         * @param string $currency   Currency code used in the request.
         * @return string
         */
        public static function parse_paypal_order_error_message($order_data, $currency = '')
        {
            $currency = strtoupper(sanitize_text_field((string) $currency));
            $details = (is_array($order_data) && isset($order_data['details']) && is_array($order_data['details']))
                ? $order_data['details']
                : [];

            foreach ($details as $detail) {
                if (!is_array($detail)) {
                    continue;
                }
                $issue = isset($detail['issue']) ? sanitize_text_field((string) $detail['issue']) : '';
                if (in_array($issue, ['CURRENCY_NOT_ALLOWED', 'INVALID_CURRENCY_CODE', 'CURRENCY_NOT_SUPPORTED_FOR_RECEIVER'], true)) {
                    if ($currency === 'INR') {
                        return esc_html__(
                            'PayPal cannot process INR with this merchant account. Use a PayPal India business account and Live credentials, or test with USD currency in sandbox.',
                            'bookwellnow-appointment-booking'
                        );
                    }
                    return esc_html__(
                        'PayPal does not support the selected currency for this merchant account.',
                        'bookwellnow-appointment-booking'
                    );
                }
                if (!empty($detail['description'])) {
                    return sanitize_text_field((string) $detail['description']);
                }
            }

            if (is_array($order_data) && !empty($order_data['message'])) {
                $message = sanitize_text_field((string) $order_data['message']);
                if ($currency === 'INR' && stripos($message, 'business validation') !== false) {
                    return esc_html__(
                        'PayPal rejected this INR payment. Confirm your PayPal business account supports INR, or switch the site currency to USD for sandbox testing.',
                        'bookwellnow-appointment-booking'
                    );
                }
                return $message;
            }

            return esc_html__('Unable to create PayPal order.', 'bookwellnow-appointment-booking');
        }

        /**
         * @param string $order_id PayPal order ID.
         * @return array<string, mixed>|\WP_Error
         */
        public function get_order($order_id)
        {
            $order_id = sanitize_text_field((string) $order_id);
            if ($order_id === '') {
                return new \WP_Error('invalid_order', esc_html__('Invalid PayPal order.', 'bookwellnow-appointment-booking'));
            }

            $token = $this->get_access_token();
            if (is_wp_error($token)) {
                $this->debug_log('paypal_api_error', [
                    'step' => 'get_access_token',
                    'message' => $token->get_error_message(),
                    'code' => $token->get_error_code(),
                ]);
                return $token;
            }

            $response = wp_remote_get(
                $this->get_api_host() . '/v2/checkout/orders/' . rawurlencode($order_id),
                [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $token,
                    ],
                    'timeout' => 30,
                ]
            );

            if (is_wp_error($response)) {
                $this->debug_log('paypal_api_error', [
                    'step' => 'get_order',
                    'order_id' => $order_id,
                    'message' => $response->get_error_message(),
                ]);
                return $response;
            }

            $order_data = json_decode(wp_remote_retrieve_body($response), true);
            if (!is_array($order_data) || empty($order_data['id'])) {
                $this->debug_log('paypal_api_error', [
                    'step' => 'get_order',
                    'order_id' => $order_id,
                    'response' => $order_data,
                ]);
                return new \WP_Error('paypal_order', esc_html__('Unable to verify PayPal order.', 'bookwellnow-appointment-booking'));
            }

            return $order_data;
        }

        /**
         * @param float  $amount   Amount.
         * @param string $currency Currency code.
         * @return string
         */
        public static function format_paypal_amount($amount, $currency)
        {
            $amount = (float) $amount;
            $currency = strtoupper((string) $currency);
            $decimals = $currency === 'JPY' ? 0 : 2;
            return number_format($amount, $decimals, '.', '');
        }

        /**
         * @return void
         */
        public function ajax_create_order()
        {
            if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'bw_front_nonce')) {
                wp_send_json_error(['msg' => esc_html__('Invalid request token', 'bookwellnow-appointment-booking')]);
            }

            if (!self::is_configured()) {
                wp_send_json_error(['msg' => esc_html__('PayPal is not available.', 'bookwellnow-appointment-booking')]);
            }

            $booking_data = BookWellAB_Input::get_post_json('data');
            if (empty($booking_data) || !is_array($booking_data)) {
                wp_send_json_error(['msg' => esc_html__('No data received', 'bookwellnow-appointment-booking')]);
            }

            $booking_data['payment_gateway'] = 'paypal';

            $this->debug_log('create_order_request', [
                'booking' => BookWellAB_Payment_Debug_Log::summarize_booking_data($booking_data),
            ]);

            $frontend = BookWellAB_Frontend::get_instance();
            $prepared = $frontend->prepare_booking_submission($booking_data);
            if (is_wp_error($prepared)) {
                $this->debug_log('create_order_error', [
                    'message' => $prepared->get_error_message(),
                    'code' => $prepared->get_error_code(),
                ]);
                wp_send_json_error(['msg' => $prepared->get_error_message()]);
            }

            $validated = $prepared['validated'];
            $payable_amount = (float) $prepared['payable_amount'];
            $currency = self::get_frontend_currency();

            if ($payable_amount <= 0) {
                wp_send_json_error(['msg' => esc_html__('PayPal is not available for free bookings.', 'bookwellnow-appointment-booking')]);
            }

            if ($payable_amount < 0.10 && $currency !== 'JPY') {
                wp_send_json_error(['msg' => esc_html__('The payable amount is below the PayPal minimum.', 'bookwellnow-appointment-booking')]);
            }

            $result = BookWellAB_Booking_Helper::create_appointment_from_booking_data(
                $validated,
                [
                    'payment_gateway' => 'paypal',
                    'payment_status' => 'pending',
                    'bw_status' => 'pending',
                    'send_notification' => false,
                ]
            );

            if (empty($result['success'])) {
                $msg = isset($result['msg']) ? $result['msg'] : esc_html__('Failed to create appointment', 'bookwellnow-appointment-booking');
                $this->debug_log('create_order_error', ['message' => $msg]);
                wp_send_json_error(['msg' => $msg]);
            }

            $appointment_id = (int) $result['appointment_id'];
            $appointment_hash = isset($result['appointment_hash']) ? sanitize_text_field((string) $result['appointment_hash']) : '';
            $service_name = isset($validated['service_name']) ? $validated['service_name'] : esc_html__('Appointment Booking', 'bookwellnow-appointment-booking');

            $order_id = $this->create_checkout_order($appointment_id, $payable_amount, $currency, $service_name);
            if (is_wp_error($order_id)) {
                BookWellAB_Booking_Helper::delete_pending_appointment($appointment_id);
                $this->debug_log('create_order_error', [
                    'message' => $order_id->get_error_message(),
                    'code' => $order_id->get_error_code(),
                    'currency' => $currency,
                    'amount' => self::format_paypal_amount($payable_amount, $currency),
                ], $appointment_id);
                wp_send_json_error(['msg' => $order_id->get_error_message()]);
            }

            $this->debug_log('create_order_success', [
                'order_id' => $order_id,
                'currency' => $currency,
                'amount' => self::format_paypal_amount($payable_amount, $currency),
            ], $appointment_id);

            wp_send_json_success([
                'order_id' => $order_id,
                'appointment_id' => $appointment_id,
                'appointment_hash' => $appointment_hash,
                'currency' => $currency,
                'amount' => self::format_paypal_amount($payable_amount, $currency),
            ]);
        }

        /**
         * @return void
         */
        public function ajax_confirm_order()
        {
            if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'bw_front_nonce')) {
                wp_send_json_error(['msg' => esc_html__('Invalid request token', 'bookwellnow-appointment-booking')]);
            }

            if (!self::is_configured()) {
                wp_send_json_error(['msg' => esc_html__('PayPal is not available.', 'bookwellnow-appointment-booking')]);
            }

            $order_id = BookWellAB_Input::get_post_string('order_id');
            if ($order_id === '') {
                wp_send_json_error(['msg' => esc_html__('Invalid PayPal order.', 'bookwellnow-appointment-booking')]);
            }

            $this->debug_log('confirm_order_request', ['order_id' => $order_id]);

            $order = $this->get_order($order_id);
            if (is_wp_error($order)) {
                $this->debug_log('confirm_order_error', [
                    'order_id' => $order_id,
                    'message' => $order->get_error_message(),
                    'code' => $order->get_error_code(),
                ]);
                wp_send_json_error(['msg' => $order->get_error_message()]);
            }

            $reference_id = isset($order['purchase_units'][0]['reference_id']) ? absint($order['purchase_units'][0]['reference_id']) : 0;
            $order_status = isset($order['status']) ? sanitize_text_field((string) $order['status']) : '';
            $transaction_id = isset($order['purchase_units'][0]['payments']['captures'][0]['id'])
                ? sanitize_text_field((string) $order['purchase_units'][0]['payments']['captures'][0]['id'])
                : '';
            $capture_status = isset($order['purchase_units'][0]['payments']['captures'][0]['status'])
                ? sanitize_text_field((string) $order['purchase_units'][0]['payments']['captures'][0]['status'])
                : '';
            $capture = isset($order['purchase_units'][0]['payments']['captures'][0])
                && is_array($order['purchase_units'][0]['payments']['captures'][0])
                ? $order['purchase_units'][0]['payments']['captures'][0]
                : [];

            if (!empty($capture['amount']['value'])) {
                $amount = (float) $capture['amount']['value'];
                $currency = isset($capture['amount']['currency_code'])
                    ? strtoupper(sanitize_text_field((string) $capture['amount']['currency_code']))
                    : '';
            } else {
                $amount = isset($order['purchase_units'][0]['amount']['value'])
                    ? (float) $order['purchase_units'][0]['amount']['value']
                    : 0.0;
                $currency = isset($order['purchase_units'][0]['amount']['currency_code'])
                    ? strtoupper(sanitize_text_field((string) $order['purchase_units'][0]['amount']['currency_code']))
                    : '';
            }

            if ($reference_id <= 0 || $order_status !== 'COMPLETED' || $transaction_id === '') {
                $this->debug_log('confirm_order_error', [
                    'order_id' => $order_id,
                    'reference_id' => $reference_id,
                    'order_status' => $order_status,
                    'transaction_id' => $transaction_id,
                    'capture_status' => $capture_status,
                ], $reference_id);
                wp_send_json_error(['msg' => esc_html__('PayPal payment was not completed.', 'bookwellnow-appointment-booking')]);
            }

            if (!in_array($capture_status, ['COMPLETED', 'PENDING'], true)) {
                $this->debug_log('confirm_order_error', [
                    'order_id' => $order_id,
                    'capture_status' => $capture_status,
                ], $reference_id);
                wp_send_json_error(['msg' => esc_html__('PayPal payment was not completed.', 'bookwellnow-appointment-booking')]);
            }

            $confirm = BookWellAB_Booking_Helper::confirm_paypal_payment(
                $reference_id,
                $transaction_id,
                $amount,
                $currency
            );

            if (is_wp_error($confirm)) {
                $this->debug_log('confirm_order_error', [
                    'order_id' => $order_id,
                    'message' => $confirm->get_error_message(),
                    'code' => $confirm->get_error_code(),
                    'amount' => $amount,
                    'currency' => $currency,
                ], $reference_id);
                wp_send_json_error(['msg' => $confirm->get_error_message()]);
            }

            $this->debug_log('confirm_order_success', [
                'order_id' => $order_id,
                'transaction_id' => $transaction_id,
                'amount' => $amount,
                'currency' => $currency,
            ], $reference_id);

            wp_send_json_success([
                'appointment_id' => $reference_id,
                'msg' => esc_html__('Appointment booked successfully!', 'bookwellnow-appointment-booking'),
            ]);
        }

        /**
         * Mark a pending PayPal appointment as cancelled when payment is abandoned.
         *
         * @return void
         */
        public function ajax_cancel_order()
        {
            if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'bw_front_nonce')) {
                wp_send_json_error(['msg' => esc_html__('Invalid request token', 'bookwellnow-appointment-booking')]);
            }

            $appointment_id = absint(BookWellAB_Input::get_post_string('appointment_id'));
            $appointment_hash = sanitize_text_field(BookWellAB_Input::get_post_string('appointment_hash'));
            if ($appointment_id <= 0 || $appointment_hash === '') {
                wp_send_json_error(['msg' => esc_html__('Invalid appointment.', 'bookwellnow-appointment-booking')]);
            }

            $result = BookWellAB_Booking_Helper::cancel_paypal_payment($appointment_id, $appointment_hash);
            if (is_wp_error($result)) {
                wp_send_json_error(['msg' => $result->get_error_message()]);
            }

            $this->debug_log('cancel_order', ['appointment_id' => $appointment_id], $appointment_id);

            wp_send_json_success([
                'appointment_id' => $appointment_id,
                'msg' => esc_html__('PayPal payment was cancelled.', 'bookwellnow-appointment-booking'),
            ]);
        }
    }
}
