<?php

namespace App\Services;

use App\Models\Order;
use App\Models\WebhookSource;
use App\Helpers\PhoneHelper;
use App\Services\OrderNumberService;
use Illuminate\Support\Str;

class WebhookOrderMapper
{
    /**
     * Map webhook payload to order data
     */
    public function mapToOrder(array $payload, WebhookSource $webhookSource)
    {
        $mapping = $webhookSource->field_mapping ?? [];

        // Extract data using field mapping or common patterns
        $orderData = [
            'order_number' => $this->extractField($payload, $mapping, 'order_number', ['order_id', 'order_number', 'transaction_id', 'reference']),
            'total' => $this->extractField($payload, $mapping, 'total', ['total', 'amount', 'payment_amount', 'grand_total']),
            'currency' => $this->extractField($payload, $mapping, 'currency', ['currency']) ?? 'MYR',
            'status' => 'processing', // Default status for new webhook orders
            'payment_method' => $this->extractField($payload, $mapping, 'payment_method', ['payment_method', 'payment_type']) ?? 'online',
            'payment_method_title' => $this->extractField($payload, $mapping, 'payment_method_title', ['payment_method_title', 'payment_gateway']) ?? 'Online Payment',
        ];

        // Extract billing information
        $orderData['billing'] = $this->extractBillingData($payload, $mapping);

        // Extract shipping information (use billing if not provided)
        $orderData['shipping'] = $this->extractShippingData($payload, $mapping) ?? $orderData['billing'];

        // Extract line items
        $orderData['line_items'] = $this->extractLineItems($payload, $mapping);

        // Calculate subtotal from line items if not provided
        if (empty($orderData['subtotal'])) {
            $orderData['subtotal'] = collect($orderData['line_items'])->sum(function ($item) {
                return floatval($item['total'] ?? 0);
            });
        }

        // Extract additional fields
        $orderData['meta_data'] = $this->extractMetaData($payload, $mapping);
        $orderData['date_created'] = now();
        $orderData['date_modified'] = now();

        return $orderData;
    }

    /**
     * Extract a field from payload using mapping or common patterns
     */
    protected function extractField(array $payload, array $mapping, string $field, array $alternatives = [])
    {
        // Check if custom mapping exists
        if (isset($mapping[$field])) {
            return data_get($payload, $mapping[$field]);
        }

        // Try common field names
        foreach ($alternatives as $alt) {
            $value = data_get($payload, $alt);
            if ($value !== null) {
                return $value;
            }
        }

        return null;
    }

    /**
     * Extract billing data from payload
     */
    protected function extractBillingData(array $payload, array $mapping)
    {
        $billing = [];

        // Try to extract from nested customer/billing object or root level
        $customerPaths = ['customer', 'billing', 'buyer', 'payer'];
        $customerData = null;

        foreach ($customerPaths as $path) {
            if (isset($payload[$path])) {
                $customerData = $payload[$path];
                break;
            }
        }

        // If no nested object, use root level
        if (!$customerData) {
            $customerData = $payload;
        }

        // Extract name
        $fullName = data_get($customerData, 'name') ?? data_get($customerData, 'full_name') ?? '';
        $firstName = data_get($customerData, 'first_name') ?? data_get($customerData, 'firstname') ?? '';
        $lastName = data_get($customerData, 'last_name') ?? data_get($customerData, 'lastname') ?? '';

        if (empty($firstName) && !empty($fullName)) {
            $nameParts = explode(' ', $fullName, 2);
            $firstName = $nameParts[0] ?? '';
            $lastName = $nameParts[1] ?? '';
        }

        $billing['first_name'] = $firstName;
        $billing['last_name'] = $lastName;

        // Extract email
        $billing['email'] = data_get($customerData, 'email') ??
                           data_get($customerData, 'email_address') ??
                           '';

        // Extract phone and format
        $phone = data_get($customerData, 'phone') ??
                data_get($customerData, 'phone_number') ??
                data_get($customerData, 'mobile') ??
                '';
        $billing['phone'] = PhoneHelper::formatMalaysian($phone);

        // Extract address
        $billing['address_1'] = data_get($customerData, 'address') ??
                               data_get($customerData, 'address_line_1') ??
                               data_get($customerData, 'street_address') ??
                               '';

        $billing['address_2'] = data_get($customerData, 'address_2') ??
                               data_get($customerData, 'address_line_2') ??
                               '';

        $billing['city'] = data_get($customerData, 'city') ??
                          data_get($customerData, 'town') ??
                          '';

        $billing['state'] = data_get($customerData, 'state') ??
                           data_get($customerData, 'province') ??
                           '';

        $billing['postcode'] = data_get($customerData, 'postcode') ??
                              data_get($customerData, 'postal_code') ??
                              data_get($customerData, 'zip') ??
                              '';

        $billing['country'] = data_get($customerData, 'country') ?? 'MY';

        return $billing;
    }

    /**
     * Extract shipping data from payload
     */
    protected function extractShippingData(array $payload, array $mapping)
    {
        $shippingPaths = ['shipping', 'delivery', 'shipping_address'];

        foreach ($shippingPaths as $path) {
            if (isset($payload[$path]) && is_array($payload[$path])) {
                $shippingData = $payload[$path];

                // Extract similar to billing
                $fullName = data_get($shippingData, 'name') ?? '';
                $nameParts = explode(' ', $fullName, 2);

                return [
                    'first_name' => data_get($shippingData, 'first_name') ?? $nameParts[0] ?? '',
                    'last_name' => data_get($shippingData, 'last_name') ?? $nameParts[1] ?? '',
                    'address_1' => data_get($shippingData, 'address') ?? data_get($shippingData, 'address_1') ?? '',
                    'address_2' => data_get($shippingData, 'address_2') ?? '',
                    'city' => data_get($shippingData, 'city') ?? '',
                    'state' => data_get($shippingData, 'state') ?? '',
                    'postcode' => data_get($shippingData, 'postcode') ?? data_get($shippingData, 'postal_code') ?? '',
                    'country' => data_get($shippingData, 'country') ?? 'MY',
                ];
            }
        }

        return null;
    }

    /**
     * Extract line items from payload
     */
    protected function extractLineItems(array $payload, array $mapping)
    {
        $items = [];

        // Try to find items in common paths
        $itemsPaths = ['items', 'line_items', 'products', 'cart', 'order_items'];

        foreach ($itemsPaths as $path) {
            if (isset($payload[$path]) && is_array($payload[$path])) {
                foreach ($payload[$path] as $item) {
                    $items[] = [
                        'name' => data_get($item, 'name') ?? data_get($item, 'product_name') ?? data_get($item, 'title') ?? 'Product',
                        'product_id' => data_get($item, 'product_id') ?? data_get($item, 'id') ?? null,
                        'quantity' => data_get($item, 'quantity') ?? data_get($item, 'qty') ?? 1,
                        'price' => floatval(data_get($item, 'price') ?? data_get($item, 'unit_price') ?? 0),
                        'total' => floatval(data_get($item, 'total') ?? data_get($item, 'subtotal') ?? data_get($item, 'amount') ?? 0),
                        'sku' => data_get($item, 'sku') ?? data_get($item, 'product_code') ?? '',
                    ];
                }
                return $items;
            }
        }

        // If no items found, create single item from order total
        return [[
            'name' => 'Order from ' . ($payload['source'] ?? 'Webhook'),
            'quantity' => 1,
            'price' => floatval($payload['total'] ?? $payload['amount'] ?? 0),
            'total' => floatval($payload['total'] ?? $payload['amount'] ?? 0),
        ]];
    }

    /**
     * Extract meta data from payload
     */
    protected function extractMetaData(array $payload, array $mapping)
    {
        $meta = [];

        // Store original webhook payload for reference
        $meta[] = [
            'key' => '_webhook_source',
            'value' => 'generic_webhook'
        ];

        $meta[] = [
            'key' => '_webhook_payload',
            'value' => json_encode($payload)
        ];

        // Extract any custom fields
        if (isset($payload['custom_fields']) && is_array($payload['custom_fields'])) {
            foreach ($payload['custom_fields'] as $key => $value) {
                $meta[] = [
                    'key' => $key,
                    'value' => $value
                ];
            }
        }

        return $meta;
    }

    /**
     * Create order from webhook payload
     */
    public function createOrder(array $payload, WebhookSource $webhookSource)
    {
        $orderData = $this->mapToOrder($payload, $webhookSource);

        // Generate clean sequential webhook order number if not provided
        if (empty($orderData['order_number'])) {
            $orderData['order_number'] = $this->generateWebhookOrderNumber();
        }

        // Associate with webhook source
        $orderData['webhook_source_id'] = $webhookSource->id;

        // Create the order
        $order = Order::create($orderData);

        return $order;
    }

    /**
     * Generate sequential webhook order number using atomic sequence
     */
    protected function generateWebhookOrderNumber()
    {
        $orderNumberService = app(OrderNumberService::class);
        return $orderNumberService->generateWebhookOrderNumber();
    }
}
