<?php

namespace BWDK\Woo\Order;

use BWDK\Enums\BWDK_OrderStatus;
use BWDK\Integrations\Digify\Client;
use BWDK\Support\Constant;
use WC_Order;
use Exception;

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

/**
 * Service class for syncing BWDK orders from Digify API
 * Reusable logic for both manual admin refresh and automatic cancellation sync
 */
class OrderSyncService
{
    /**
     * Refresh order from BWDK API and optionally verify if paid
     * 
     * @param int $order_id WooCommerce order ID
     * @param array $opts Options:
     *   - 'skip_verify' (bool): Skip verify even if status is PAID_BY_USER
     *   - 'skip_address_sync' (bool): Skip address/customer info sync
     *   - 'source' (string): Source of sync (e.g., 'admin_button', 'auto_cancel')
     * @return array Result array:
     *   - 'ok' (bool): Success status
     *   - 'order_uuid' (string): BWDK order UUID
     *   - 'bwdk_status' (int): BWDK status code
     *   - 'bwdk_status_display' (string): BWDK status label
     *   - 'was_verified' (bool): Whether order was verified (status 9)
     *   - 'verify_called' (bool): Whether verify API was called
     *   - 'verify_ok' (bool): Whether verify succeeded
     *   - 'message' (string): Human-readable message
     *   - 'raw' (array|null): Raw API response (trimmed if large)
     */
    public static function refresh_from_bwdk(int $order_id, array $opts = []): array
    {
        $default_opts = [
            'skip_verify' => false,
            'skip_address_sync' => false,
            'source' => 'unknown',
        ];
        $opts = array_merge($default_opts, $opts);

        $order = wc_get_order($order_id);
        if (!$order instanceof WC_Order) {
            return [
                'ok' => false,
                'message' => 'سفارش یافت نشد',
                'order_uuid' => '',
                'bwdk_status' => 0,
                'bwdk_status_display' => '',
                'was_verified' => false,
                'verify_called' => false,
                'verify_ok' => false,
            ];
        }

        $order_uuid = $order->get_meta(Constant::META_BWDK_UUID);
        if (empty($order_uuid)) {
            return [
                'ok' => false,
                'message' => 'این سفارش متعلق به BWDK نیست',
                'order_uuid' => '',
                'bwdk_status' => 0,
                'bwdk_status_display' => '',
                'was_verified' => false,
                'verify_called' => false,
                'verify_ok' => false,
            ];
        }

        try {
            $client = new Client();
            
            // Step 1: Get status from Digify API (Sync)
            $status_data = $client->orderGetStatus($order_uuid);
            
            if (!$status_data['ok'] || empty($status_data['raw'])) {
                $error_msg = $status_data['error'] ?? 'خطای نامشخص';
                return [
                    'ok' => false,
                    'message' => sprintf('خطا در دریافت وضعیت از دیجیکالا: %s', $error_msg),
                    'order_uuid' => $order_uuid,
                    'bwdk_status' => 0,
                    'bwdk_status_display' => '',
                    'was_verified' => false,
                    'verify_called' => false,
                    'verify_ok' => false,
                ];
            }

            $raw = $status_data['raw'];
            
            // Save sync data to order meta
            $order->update_meta_data(Constant::META_BWDK_JSON_CB, $raw);
            
            // Update reference code if exists
            $reference_code = isset($raw['reference_code']) ? (string) $raw['reference_code'] : '';
            if ($reference_code !== '') {
                $order->update_meta_data('_bwdk_reference_code', $reference_code);
            }
            
            // Update digify order ID if exists
            $digify_id = isset($raw['id']) ? (string) $raw['id'] : '';
            if ($digify_id !== '') {
                $order->update_meta_data('_bwdk_digify_order_id', $digify_id);
            }

            // Update API status meta
            $current_status = isset($raw['status']) ? (int) $raw['status'] : 0;
            $status_display = isset($raw['status_display']) ? (string) $raw['status_display'] : BWDK_OrderStatus::label($current_status);
            $order->update_meta_data('_bwdk_api_status', $current_status);
            $order->update_meta_data('_bwdk_api_status_display', $status_display);

            // Update total_paid_amount if exists
            if (isset($raw['total_paid_amount'])) {
                $order->update_meta_data('_bwdk_total_paid_amount', (float) $raw['total_paid_amount']);
            }

            // Extract and sync order address/customer info (only fill empty fields)
            $address_sync_result = ['updated_count' => 0, 'checked' => false];
            if (!$opts['skip_address_sync']) {
                // Reuse address sync logic from AjaxHandler
                $address_sync_result = self::syncOrderAddressFromBwdk($order, $raw);
            }

            // Step 2: Check if status is PAID_BY_USER (7) or VERIFIED_BY_MERCHANT (9) and verify if needed
            $verified = false;
            $verify_called = false;
            $verify_ok = false;
            $verify_network_error = false;
            $was_verified = ($current_status === BWDK_OrderStatus::VERIFIED_BY_MERCHANT);

            if (!$opts['skip_verify'] && $current_status === BWDK_OrderStatus::PAID_BY_USER) {
                // Status is exactly 7 (PAID_BY_USER) - call verify
                $verify_result = $client->verifyOrder($order, $order_uuid);
                $verify_called = true;

                if (is_array($verify_result) && !empty($verify_result['network_error'])) {
                    // Request not sent (network/DNS timeout) - order note already added in Client
                    $verify_ok = false;
                    $verify_network_error = true;
                } elseif ($verify_result === false) {
                    // Server rejected verify (HTTP non-2xx) or missing merchant_unique_id
                    $verify_ok = false;
                } elseif (is_array($verify_result)) {
                    // Verify was attempted and got a response
                    // After verify, poll status up to 3 times with 2 second intervals
                    $poll_max_attempts = 3;
                    $poll_interval = 2; // seconds
                    $final_status_data = null;
                    
                    for ($attempt = 1; $attempt <= $poll_max_attempts; $attempt++) {
                        // Wait before polling (except first attempt)
                        if ($attempt > 1) {
                            sleep($poll_interval);
                        }
                        
                        $status_poll = $client->orderGetStatus($order_uuid);
                        
                        if ($status_poll['ok'] && !empty($status_poll['raw'])) {
                            $poll_raw = $status_poll['raw'];
                            $poll_status = isset($poll_raw['status']) ? (int) $poll_raw['status'] : 0;
                            
                            if ($poll_status === BWDK_OrderStatus::VERIFIED_BY_MERCHANT) {
                                // Status is now 9 (VERIFIED_BY_MERCHANT)
                                $final_status_data = $poll_raw;
                                $verified = true;
                                $verify_ok = true;
                                $was_verified = true;
                                break; // Exit polling loop
                            }
                            
                            // Store latest status data for final update
                            $final_status_data = $poll_raw;
                        }
                    }
                    
                    if ($verified && $final_status_data) {
                        // Status is now 9 (VERIFIED_BY_MERCHANT) - save verified meta
                        $raw = $final_status_data;
                        $raw['status'] = BWDK_OrderStatus::VERIFIED_BY_MERCHANT;
                        $raw['status_display'] = BWDK_OrderStatus::label(BWDK_OrderStatus::VERIFIED_BY_MERCHANT);
                        
                        // Merge verify response data
                        if (is_array($verify_result)) {
                            $raw = array_merge($raw, $verify_result);
                        }
                        
                        // Save verified meta
                        $order->update_meta_data('_bwdk_verified_by_merchant', 1);
                        $order->update_meta_data('_bwdk_verified_at', time());
                        $order->update_meta_data('_bwdk_api_status', BWDK_OrderStatus::VERIFIED_BY_MERCHANT);
                        $order->update_meta_data('_bwdk_api_status_display', BWDK_OrderStatus::label(BWDK_OrderStatus::VERIFIED_BY_MERCHANT));
                        
                        $current_status = BWDK_OrderStatus::VERIFIED_BY_MERCHANT;
                        $status_display = BWDK_OrderStatus::label(BWDK_OrderStatus::VERIFIED_BY_MERCHANT);
                    }
                }
            } elseif ($was_verified) {
                // Already verified (status 9)
                $verify_ok = true;
            }

            // Post-verify recovery: move to processing ONCE after verification.
            if ($verify_ok && $current_status === BWDK_OrderStatus::VERIFIED_BY_MERCHANT) {
                // Guard: only move to processing from allowed early statuses and only once.
                if (function_exists('bwdk_is_admin_manual_status_change_to_final') && bwdk_is_admin_manual_status_change_to_final($order)) {
                    $order->add_order_note('BWDK debug: skipped auto-processing because admin manually changed status.', false);
                } elseif (function_exists('bwdk_has_recent_admin_manual_status_lock') && bwdk_has_recent_admin_manual_status_lock($order, 300)) {
                    $order->add_order_note('BWDK debug: skipped auto-processing because admin manual status lock is active.', false);
                } elseif (function_exists('bwdk_can_auto_move_to_processing') && bwdk_can_auto_move_to_processing($order)) {

                    // Prevent recursion into our own order-status handlers.
                    if (class_exists(\BWDK\Core\App::class) && \BWDK\Core\App::has(\BWDK\Woo\Order\OrderCancelSync::class)) {
                        $cancel_sync = \BWDK\Core\App::get(\BWDK\Woo\Order\OrderCancelSync::class);
                        if (is_object($cancel_sync)) {
                            remove_action('woocommerce_order_status_changed', [$cancel_sync, 'handleOrderStatusChange'], 5);
                            remove_action('woocommerce_order_status_pending_to_cancelled', [$cancel_sync, 'handlePendingToCancelled'], 5);
                        }
                    }

                    $note = 'BWDK verify succeeded; WooCommerce order moved to processing.';
                    $order->update_status('processing', $note);
                    $order->update_meta_data('_bwdk_auto_processing_done', 'yes');
                    $order->save();

                    if (class_exists(\BWDK\Core\App::class) && \BWDK\Core\App::has(\BWDK\Woo\Order\OrderCancelSync::class)) {
                        $cancel_sync = \BWDK\Core\App::get(\BWDK\Woo\Order\OrderCancelSync::class);
                        if (is_object($cancel_sync)) {
                            add_action('woocommerce_order_status_changed', [$cancel_sync, 'handleOrderStatusChange'], 5, 4);
                            add_action('woocommerce_order_status_pending_to_cancelled', [$cancel_sync, 'handlePendingToCancelled'], 5, 2);
                        }
                    }
                }
            }

            // When BWDK reports refund final (13 or 17), set Woo order to refunded if currently processing/in-progress
            if (BWDK_OrderStatus::isRefundFinalStatus($current_status)) {
                $woo_status = $order->get_status();
                if (in_array($woo_status, ['processing', 'in-progress'], true)) {
                    $order->set_status('refunded');
                }
            }

            // Save updated raw data (with verify result if applicable)
            $order->update_meta_data(Constant::META_BWDK_JSON_CB, $raw);
            $order->save();

            // Build message
            $message = sprintf(
                'BWDK Sync (%s): وضعیت سفارش به‌روز شد | order_uuid=%s | bwdk_status=%d (%s)',
                $opts['source'],
                $order_uuid,
                $current_status,
                $status_display
            );
            
            if ($verify_called) {
                if ($verify_ok) {
                    $message .= ' | Verify انجام شد و تأیید شد';
                } elseif ($verify_network_error) {
                    $message .= ' | Verify ارسال نشد (خطای شبکه/DNS)';
                } else {
                    $message .= ' | Verify انجام شد اما تأیید نشد';
                }
            } elseif ($was_verified) {
                $message .= ' | قبلاً Verify شده بود';
            }

            // Trim raw response if too large (keep only essential fields)
            $raw_trimmed = null;
            if (is_array($raw)) {
                $raw_trimmed = [
                    'status' => $raw['status'] ?? null,
                    'status_display' => $raw['status_display'] ?? null,
                    'reference_code' => $raw['reference_code'] ?? null,
                    'id' => $raw['id'] ?? null,
                ];
            }

            return [
                'ok' => true,
                'order_uuid' => $order_uuid,
                'bwdk_status' => $current_status,
                'bwdk_status_display' => $status_display,
                'was_verified' => $was_verified,
                'verify_called' => $verify_called,
                'verify_ok' => $verify_ok,
                'verify_network_error' => $verify_network_error,
                'message' => $message,
                'raw' => $raw_trimmed,
            ];

        } catch (Exception $e) {
            return [
                'ok' => false,
                'message' => sprintf('خطا در به‌روزرسانی سفارش: %s', $e->getMessage()),
                'order_uuid' => $order_uuid ?? '',
                'bwdk_status' => 0,
                'bwdk_status_display' => '',
                'was_verified' => false,
                'verify_called' => false,
                'verify_ok' => false,
                'verify_network_error' => false,
            ];
        }
    }

    /**
     * Sync order address and customer info from BWDK response
     * Only fills empty fields - does not overwrite existing data
     * 
     * @param WC_Order $order
     * @param array $raw BWDK API response data
     * @return array ['updated_count' => int, 'updated_fields' => array, 'checked' => bool]
     */
    private static function syncOrderAddressFromBwdk(WC_Order $order, array $raw): array
    {
        $updated_fields = [];
        $checked = false;

        // Helper function to check if field is empty
        $isEmpty = static function($value): bool {
            return empty(trim((string)$value));
        };

        // Helper function to clean value
        $clean = static function($value): string {
            return wc_clean(trim((string)$value));
        };

        // Extract destination_address (priority source for shipping address)
        $destination = $raw['destination_address'] ?? null;
        
        // Extract user info (for billing and name fields)
        $user = $raw['user'] ?? null;

        // ---------- Shipping Address from destination_address ----------
        if (!empty($destination) && is_array($destination)) {
            $checked = true;
            
            // Shipping Address 1
            $addr1 = $clean($destination['address'] ?? '');
            if (!empty($addr1) && $isEmpty($order->get_shipping_address_1())) {
                $order->set_shipping_address_1($addr1);
                $updated_fields[] = 'shipping_address_1';
            }

            // Shipping Address 2 (building, unit, district)
            $building = $clean($destination['building_number'] ?? '');
            $unit = $clean($destination['unit'] ?? '');
            $district = $clean($destination['district_name'] ?? '');
            
            $addr2Parts = array_filter([
                $district ?: null,
                $building ? "پلاک {$building}" : null,
                $unit ? "واحد {$unit}" : null,
            ]);
            $addr2 = $clean(implode('، ', $addr2Parts));
            
            if (!empty($addr2) && $isEmpty($order->get_shipping_address_2())) {
                $order->set_shipping_address_2($addr2);
                $updated_fields[] = 'shipping_address_2';
            }

            // Shipping City
            $city = $clean($destination['city_name'] ?? '');
            if (!empty($city) && $isEmpty($order->get_shipping_city())) {
                $order->set_shipping_city($city);
                $updated_fields[] = 'shipping_city';
            }

            // Shipping State
            $stateName = $clean($destination['state_name'] ?? '');
            if (!empty($stateName)) {
                $stateCode = $stateName;
                // Try to find state code from WooCommerce states
                if (function_exists('WC') && WC()->countries) {
                    $states = WC()->countries->get_states('IR') ?: [];
                    $found = array_search($stateName, $states, true);
                    if ($found !== false) {
                        $stateCode = $found;
                    }
                }
                
                if ($isEmpty($order->get_shipping_state())) {
                    $order->set_shipping_state($stateCode);
                    $updated_fields[] = 'shipping_state';
                }
            }

            // Shipping Postcode
            $postal = preg_replace('/\D+/', '', (string)($destination['postal_code'] ?? ''));
            if (!empty($postal) && $isEmpty($order->get_shipping_postcode())) {
                $order->set_shipping_postcode($postal);
                $updated_fields[] = 'shipping_postcode';
            }

            // Shipping Country (default to IR if not set)
            if ($isEmpty($order->get_shipping_country())) {
                $order->set_shipping_country('IR');
                $updated_fields[] = 'shipping_country';
            }

            // Shipping Phone from receiver_phone
            $receiver_phone_raw = $clean($destination['receiver_phone'] ?? '');
            if (!empty($receiver_phone_raw)) {
                $order->update_meta_data('_bwdk_receiver_phone', sanitize_text_field($receiver_phone_raw));
                // Normalize phone to local format (0...)
                $normalize_e164 = static function($p) {
                    $d = preg_replace('/\D+/', '', (string)$p);
                    if ($d && $d[0] === '0') $d = '98' . substr($d, 1);
                    if ($d && strpos($d, '98') !== 0) $d = '98' . $d;
                    return $d;
                };
                $to_local_0 = static function($e164) {
                    if (strpos($e164, '98') === 0) return '0' . substr($e164, 2);
                    return $e164;
                };
                
                $e164 = $normalize_e164($receiver_phone_raw);
                $local = $to_local_0($e164);
                
                $current_shipping_phone = '';
                if (method_exists($order, 'get_shipping_phone')) {
                    $current_shipping_phone = $order->get_shipping_phone();
                } else {
                    $current_shipping_phone = get_post_meta($order->get_id(), '_shipping_phone', true);
                }
                
                if ($isEmpty($current_shipping_phone)) {
                    if (method_exists($order, 'set_shipping_phone')) {
                        $order->set_shipping_phone($local);
                    } else {
                        update_post_meta($order->get_id(), '_shipping_phone', $local);
                    }
                    $order->update_meta_data('_bwdk_receiver_phone_e164', $e164);
                    $updated_fields[] = 'shipping_phone';
                }
            }

            // Receiver name (split to first/last if available)
            $receiver_name = $clean($destination['receiver_name'] ?? '');
            if (!empty($receiver_name)) {
                $order->update_meta_data('_bwdk_receiver_name', sanitize_text_field($receiver_name));
                // Try to split by first space
                $nameParts = explode(' ', $receiver_name, 2);
                $receiver_first = $nameParts[0] ?? '';
                $receiver_last = isset($nameParts[1]) ? trim($nameParts[1]) : '';
                
                if (!empty($receiver_first) && $isEmpty($order->get_shipping_first_name())) {
                    $order->set_shipping_first_name($receiver_first);
                    $updated_fields[] = 'shipping_first_name';
                }
                
                if (!empty($receiver_last) && $isEmpty($order->get_shipping_last_name())) {
                    $order->set_shipping_last_name($receiver_last);
                    $updated_fields[] = 'shipping_last_name';
                }
            }

            // Save extra meta fields (always, not just if empty)
            $dkId = $clean($destination['digikala_id'] ?? '');
            if (!empty($dkId)) {
                $order->update_meta_data('_dest_digikala_id', $dkId);
            }
            
            $district = $clean($destination['district_name'] ?? '');
            if (!empty($district)) {
                $order->update_meta_data('_dest_district_name', $district);
            }
            
            $lat = $clean($destination['latitude'] ?? '');
            if (!empty($lat)) {
                $order->update_meta_data('_dest_latitude', $lat);
            }
            
            $lng = $clean($destination['longitude'] ?? '');
            if (!empty($lng)) {
                $order->update_meta_data('_dest_longitude', $lng);
            }
        }

        // ---------- User info (for billing and name fallback) ----------
        if (!empty($user) && is_array($user)) {
            $checked = true;
            
            // Billing First Name
            $user_first_name = $clean($user['first_name'] ?? '');
            if (!empty($user_first_name) && $isEmpty($order->get_billing_first_name())) {
                $order->set_billing_first_name($user_first_name);
                $updated_fields[] = 'billing_first_name';
            }

            // Billing Last Name
            $user_last_name = $clean($user['last_name'] ?? '');
            if (!empty($user_last_name) && $isEmpty($order->get_billing_last_name())) {
                $order->set_billing_last_name($user_last_name);
                $updated_fields[] = 'billing_last_name';
            }

            // Billing Phone
            $user_phone = $clean($user['phone_number'] ?? '');
            if (!empty($user_phone) && $isEmpty($order->get_billing_phone())) {
                $order->set_billing_phone($user_phone);
                $updated_fields[] = 'billing_phone';
            }

            // Shipping First/Last Name (fallback if not set from receiver_name)
            if (!empty($user_first_name) && $isEmpty($order->get_shipping_first_name())) {
                $order->set_shipping_first_name($user_first_name);
                $updated_fields[] = 'shipping_first_name';
            }
            
            if (!empty($user_last_name) && $isEmpty($order->get_shipping_last_name())) {
                $order->set_shipping_last_name($user_last_name);
                $updated_fields[] = 'shipping_last_name';
            }

            // Shipping Phone (fallback if not set from receiver_phone)
            if (!empty($user_phone)) {
                $current_shipping_phone = '';
                if (method_exists($order, 'get_shipping_phone')) {
                    $current_shipping_phone = $order->get_shipping_phone();
                } else {
                    $current_shipping_phone = get_post_meta($order->get_id(), '_shipping_phone', true);
                }
                
                if ($isEmpty($current_shipping_phone)) {
                    if (method_exists($order, 'set_shipping_phone')) {
                        $order->set_shipping_phone($user_phone);
                    } else {
                        update_post_meta($order->get_id(), '_shipping_phone', $user_phone);
                    }
                    $updated_fields[] = 'shipping_phone';
                }
            }

            // Billing Address (copy from shipping if billing is empty)
            if ($isEmpty($order->get_billing_address_1()) && !$isEmpty($order->get_shipping_address_1())) {
                $order->set_billing_address_1($order->get_shipping_address_1());
                $updated_fields[] = 'billing_address_1';
            }
            
            if ($isEmpty($order->get_billing_address_2()) && !$isEmpty($order->get_shipping_address_2())) {
                $order->set_billing_address_2($order->get_shipping_address_2());
                $updated_fields[] = 'billing_address_2';
            }
            
            if ($isEmpty($order->get_billing_city()) && !$isEmpty($order->get_shipping_city())) {
                $order->set_billing_city($order->get_shipping_city());
                $updated_fields[] = 'billing_city';
            }
            
            if ($isEmpty($order->get_billing_state()) && !$isEmpty($order->get_shipping_state())) {
                $order->set_billing_state($order->get_shipping_state());
                $updated_fields[] = 'billing_state';
            }
            
            if ($isEmpty($order->get_billing_postcode()) && !$isEmpty($order->get_shipping_postcode())) {
                $order->set_billing_postcode($order->get_shipping_postcode());
                $updated_fields[] = 'billing_postcode';
            }
            
            if ($isEmpty($order->get_billing_country())) {
                $order->set_billing_country('IR');
                $updated_fields[] = 'billing_country';
            }
        }

        // Save order if any fields were updated
        if (!empty($updated_fields)) {
            $order->save();
        }

        return [
            'updated_count' => count($updated_fields),
            'updated_fields' => array_values(array_unique($updated_fields)),
            'checked' => $checked,
        ];
    }
}
