<?php

namespace Miladmodaresi\Taamito\Admin;

use Miladmodaresi\Taamito\Base\BaseController;
use Miladmodaresi\Taamito\Model\TaamSync;

/**
 * The Singleton class defines the `GetInstance` method that serves as an
 * alternative to constructor and lets clients access the same instance of this
 * class over and over.
 */
class SyncDataController extends BaseController
{
    function createOrder($order_get_id)
    {

        $order = wc_get_order($order_get_id);
        if (!$order) {
            return;
        }

        $orderItems = $this->buildOrderItemsPayload($order);
        if (!count($orderItems)) {
            \Sentry\captureMessage("Taamito: order {$order_get_id} has no mapped items for sync.");
            return;
        }

        $taamitoUser = taamitoApp()->getOption('user') ?? [];
        $token = is_array($taamitoUser) && isset($taamitoUser['token']) ? $taamitoUser['token'] : null;
        $domain = taamitoApp()->getOption('domain') ?? null;

        if (!$token || !$domain) {
            \Sentry\captureMessage('Taamito: Missing token or domain for order sync.');
            return;
        }

        $userPayload = $this->buildUserPayload($order->get_user());

        taamitoApi()
            ->setSubDomain($domain)
            ->setAuth($token)
            ->post('wp/sync/orders', [
                'user' => $userPayload,
                'items' => $orderItems,
                'order' => $order->get_data(),
            ]);
    }

    protected function buildOrderItemsPayload($order)
    {
        $items = [];

        foreach ($order->get_items() as $item) {
            if (!$item instanceof \WC_Order_Item_Product) {
                continue;
            }

            $payload = $this->buildSingleItemPayload($item);
            if ($payload) {
                $items[] = $payload;
            }
        }

        return $items;
    }

    protected function buildSingleItemPayload(\WC_Order_Item_Product $item)
    {
        $productId = $item->get_product_id();
        if (!$productId) {
            return null;
        }

        $variationId = $item->get_variation_id();
        $localPriceId = $variationId ?: $productId;

        $productSync = (new TaamSync())->where('name', 'product')->where('local_id', $productId)->first();
        $priceSync = $this->findOrCreatePriceSyncRecord($productId, $localPriceId, $item, $productSync);

        if (!$priceSync) {
            \Sentry\captureMessage("Taamito: Missing product_price sync for local id {$localPriceId}");
            return null;
        }

        $remoteProductId = ($productSync && $productSync->exists()) ? $productSync->remote_id : null;

        return [
            'product_id' => $remoteProductId,
            'price_id' => $priceSync->remote_id,
            'count' => $item->get_quantity(),
            'is_percent' => false,
            'discount_price' => 0,
            'discount' => $this->calculateItemDiscount($item),
            'subtotal' => (float)$item->get_subtotal(),
            'total' => (float)$item->get_total(),
        ];
    }

    protected function calculateItemDiscount(\WC_Order_Item_Product $item): float
    {
        $discount = (float)$item->get_subtotal() - (float)$item->get_total();
        return $discount > 0 ? $discount : 0.0;
    }

    protected function buildUserPayload($wpUser)
    {
        if (!$wpUser instanceof \WP_User) {
            return null;
        }

        return [
            'id' => $wpUser->ID,
            'email' => $wpUser->user_email,
            'first_name' => $wpUser->first_name,
            'last_name' => $wpUser->last_name,
            'display_name' => $wpUser->display_name,
        ];
    }

    protected function findOrCreatePriceSyncRecord($productId, $localPriceId, \WC_Order_Item_Product $item, $productSync)
    {
        $priceSync = (new TaamSync())->where('name', 'product_price')->where('local_id', $localPriceId)->first();

        if ($priceSync && $priceSync->exists()) {
            if (!empty($priceSync->remote_id)) {
                return $priceSync;
            }

            \Sentry\captureMessage("Taamito: found price sync with empty remote id for local {$localPriceId}, attempting to repair");
            $payload = $this->inferPriceSyncPayload($productId, $localPriceId, $item, $productSync);
            if ($payload) {
                $priceSync->update([
                    'remote_id' => $payload['remote_id'],
                    'status' => $payload['status'],
                    'data' => $payload['data'],
                ]);
                return $priceSync;
            }

            \Sentry\captureMessage("Taamito: unable to repair price sync for local {$localPriceId}");
            return null;
        }

        $payload = $this->inferPriceSyncPayload($productId, $localPriceId, $item, $productSync);
        if (!$payload) {
            return null;
        }

        $created = (new TaamSync($payload))->create();
        if (!$created || !$created->exists()) {
            \Sentry\captureMessage("Taamito: failed to persist price sync for local {$localPriceId}");
        }

        return $created;
    }

    protected function inferPriceSyncPayload($productId, $localPriceId, \WC_Order_Item_Product $item, $productSync)
    {
        if (!$productSync || !$productSync->exists()) {
            \Sentry\captureMessage("Taamito: cannot infer price sync, missing product sync for local product {$productId}");
            return null;
        }

        $productData = $productSync->data;
        $remoteProduct = $productData['product'] ?? null;
        $remotePrices = $remoteProduct['prices'] ?? [];

        if (!$remoteProduct || empty($remotePrices)) {
            \Sentry\captureMessage("Taamito: remote product data missing prices for product {$productId}");
            return null;
        }

        $remotePriceId = $this->matchRemotePriceIdFromOrder($item, $remotePrices);
        if (!$remotePriceId) {
            if (count($remotePrices) === 1) {
                $remotePriceId = $remotePrices[0]['id'] ?? null;
            } else {
                $remotePriceId = $this->matchRemotePriceByAmount($item, $remotePrices);
            }
        }

        if (!$remotePriceId) {
            \Sentry\captureMessage("Taamito: unable to resolve remote price id for local price {$localPriceId}");
            return null;
        }

        $remotePriceData = $this->getRemotePriceData($remotePrices, $remotePriceId);

        $this->syncPriceMeta($localPriceId, $remotePriceId);

        return [
            'name' => 'product_price',
            'local_id' => $localPriceId,
            'remote_id' => $remotePriceId,
            'status' => 2,
            'data' => [
                'product_local_id' => $productId,
                'product_remote_id' => $productSync->remote_id,
                'price' => $remotePriceData,
            ],
        ];
    }

    protected function getRemotePriceData(array $remotePrices, $remotePriceId)
    {
        foreach ($remotePrices as $price) {
            if (isset($price['id']) && (string)$price['id'] === (string)$remotePriceId) {
                return $price;
            }
        }
        return null;
    }

    protected function syncPriceMeta($localPriceId, $remotePriceId)
    {
        $product = wc_get_product($localPriceId);
        if (!$product || !$remotePriceId) {
            return;
        }

        $product->update_meta_data('_taamito_price_id', $remotePriceId);
        $product->save();
    }

    protected function matchRemotePriceIdFromOrder(\WC_Order_Item_Product $item, array $remotePrices)
    {
        $attributes = $this->extractOrderItemAttributes($item);
        if (empty($attributes)) {
            \Sentry\captureMessage("Taamito: no attributes found on order item {$item->get_product_id()} for combination match");
            return null;
        }

        foreach ($remotePrices as $price) {
            $combinations = $price['combinations'] ?? [];
            if ($this->orderItemMatchesCombinations($attributes, $combinations)) {
                return $price['id'] ?? null;
            }
        }

        \Sentry\captureMessage("Taamito: order item attributes did not match any remote combination");
        return null;
    }

    protected function matchRemotePriceByAmount(\WC_Order_Item_Product $item, array $remotePrices)
    {
        $quantity = max(1, (int)$item->get_quantity());
        $unitTotal = (float)$item->get_total() / $quantity;

        foreach ($remotePrices as $price) {
            if (!isset($price['id'])) {
                continue;
            }
            if (isset($price['price']) && (float)$price['price'] == $unitTotal) {
                \Sentry\captureMessage("Taamito: matched remote price {$price['id']} by amount {$unitTotal}");
                return $price['id'];
            }
        }

        \Sentry\captureMessage("Taamito: amount-based price match failed for amount {$unitTotal}");
        return null;
    }

    protected function extractOrderItemAttributes(\WC_Order_Item_Product $item): array
    {
        $attributes = [];
        foreach ($item->get_meta_data() as $meta) {
            $key = $meta->key;
            $value = $meta->value;
            if ($this->isAttributeMeta($key, $value)) {
                $attributes[$key] = $value;
            }
        }

        return $attributes;
    }

    protected function isAttributeMeta($key, $value): bool
    {
        if (!$key || !$value) {
            return false;
        }

        return (strpos($key, 'pa_') === 0) || (strpos($key, 'attribute_pa_') === 0);
    }

    protected function orderItemMatchesCombinations(array $attributes, array $combinations): bool
    {
        if (empty($combinations)) {
            return false;
        }

        foreach ($combinations as $combination) {
            if (empty($combination['feature']) || empty($combination['feature_value'])) {
                return false;
            }

            $attrKey = $this->normalizeAttributeKeyFromFeature($combination['feature']);
            $expectedValue = $this->normalizeAttributeValueFromFeature($combination['feature'], $combination['feature_value']);

            if (!$attrKey || !$expectedValue) {
                return false;
            }

            if (!isset($attributes[$attrKey]) || !$this->attributeValuesEqual($attributes[$attrKey], $expectedValue)) {
                return false;
            }
        }

        return true;
    }

    protected function normalizeAttributeKeyFromFeature($feature)
    {
        if (!$feature) {
            return null;
        }

        $taxonomy = wc_attribute_taxonomy_name($feature);
        return utf8_uri_encode($taxonomy);
    }

    protected function normalizeAttributeValueFromFeature($feature, $featureValue)
    {
        if (!$featureValue) {
            return null;
        }

        $taxonomy = wc_attribute_taxonomy_name($feature);
        $term = get_term_by('name', $featureValue, $taxonomy);

        if ($term && isset($term->slug)) {
            return $term->slug;
        }

        $slug = sanitize_title($featureValue);
        if (strpos($slug, 'pa_') !== 0) {
            $slug = 'pa_' . $slug;
        }

        return $slug;
    }

    protected function attributeValuesEqual($valueA, $valueB): bool
    {
        if ($valueA === $valueB) {
            return true;
        }

        return rawurldecode($valueA) === rawurldecode($valueB);
    }
}
