<?php

namespace Meliconnect\Meliconnect\Core;

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

use Meliconnect\Meliconnect\Core\Helpers\Helper;
use Meliconnect\Meliconnect\Core\Helpers\SyncHashHelper;

class SyncObserver
{
    private $log_channel = 'sync-observer';

    private $settings;
    private $before_state = [];


    public function __construct()
    {
        $this->settings = Helper::getMeliconnectOptions('sync');
    }

    private function isEnabled($key)
    {
        $value = $this->settings[$key] ?? false;

        return filter_var($value, FILTER_VALIDATE_BOOLEAN);
    }

    public function init()
    {
        

        add_action('woocommerce_before_product_object_save', [$this, 'captureBefore'], 10, 1);
        add_action('woocommerce_update_product', [$this, 'onProductUpdate'], 10, 1);
        add_action('woocommerce_update_product_variation', [$this, 'onVariationUpdate'], 10, 1);

        /* add_action('woocommerce_product_set_price', [$this, 'onPriceChange'], 10, 1);
        add_action('woocommerce_product_set_stock', [$this, 'onStockChange'], 10, 1);
        add_action('transition_post_status', [$this, 'onStatusChange'], 10, 3); */
    }

    private function isMeliToWoo()
    {
        return ($this->settings['meliconnect_sync_direction'] ?? '') === 'meli_to_woo';
    }

    public function captureBefore($product)
    {
        Helper::logData("Global meliconnect_sync_source: " . var_export($GLOBALS['meliconnect_sync_source'], true), $this->log_channel);
        Helper::logData("Global meliconnect_sync_running: " . var_export($GLOBALS['meliconnect_sync_running'], true), $this->log_channel);

        if (!$this->isEnabled('meliconnect_sync_enabled')) {
            Helper::logData('Sync is disabled by settings', $this->log_channel);
            return;
        }

        if ($this->isMeliToWoo()) {
            Helper::logData('Observer disabled because meliconnect_sync_direction is meli_to_woo', $this->log_channel);
            return;
        }

        if ($this->isSyncRunning() || $this->isFromMeli()) {
            Helper::logData("Global meliconnect_sync_running or Global meliconnect_sync_source is meli", $this->log_channel);
            return;
        }

        $product_id = $product->get_id();
        Helper::logData("Product WooCommerce ID:{$product_id}", $this->log_channel);


        // leer desde DB, no desde el objeto mutado
        $before_stock = get_post_meta($product_id, '_stock', true);
        $before_price = get_post_meta($product_id, '_price', true);
        $before_status = get_post_status($product_id);

        $this->before_state[$product_id] = [
            'price'  => (float) $before_price,
            'stock'  => (int) $before_stock,
            'status' => (string) $before_status,
        ];

        Helper::logData(
            "BEFORE (DB): " . var_export($this->before_state[$product_id], true),
            $this->log_channel
        );
    }

    private function resolveTypeFromChanges($changes)
    {
        $count = count($changes);

        if ($count > 1) return 'full';

        if (!empty($changes['price'])) return 'price';
        if (!empty($changes['stock'])) return 'stock';
        if (!empty($changes['status'])) return 'status';

        return 'full';
    }

    /**
     * ===============================
     * FLAGS
     * ===============================
     */
    private function isFromMeli()
    {
        return !empty($GLOBALS['meliconnect_sync_source'])
            && $GLOBALS['meliconnect_sync_source'] === 'meli';
    }

    private function isSyncRunning()
    {
        return !empty($GLOBALS['meliconnect_sync_running']);
    }

    /**
     * ===============================
     * PRODUCT UPDATE
     * ===============================
     */
    public function onProductUpdate($product_id)
    {   
        Helper::logData("Product ID:{$product_id}", $this->log_channel);
        Helper::logData("Global meliconnect_sync_source: " . var_export($GLOBALS['meliconnect_sync_source'], true), $this->log_channel);
        Helper::logData("Global meliconnect_sync_running: " . var_export($GLOBALS['meliconnect_sync_running'], true), $this->log_channel);

        if (!$this->isEnabled('meliconnect_sync_enabled')) {
            Helper::logData('Sync is disabled by settings', $this->log_channel);
            return;
        }

        if ($this->isMeliToWoo()) {
            Helper::logData('Observer disabled because meliconnect_sync_direction is meli_to_woo', $this->log_channel);
            return;
        }

        //Helper::logData("Observer: Product with ID:{$product_id} UPDATE", $this->log_channel);

        if ($this->isSyncRunning() || $this->isFromMeli()) {
            Helper::logData("Global meliconnect_sync_running or Global meliconnect_sync_source is meli", $this->log_channel);
            return;
        }

        $product = wc_get_product($product_id);
        
        if (!$product) return;

        $before = $this->before_state[$product_id] ?? null;

        if (!$before) {
            Helper::logData("No before snapshot {$product_id}", $this->log_channel);
            return;
        }

        $after = [
            'price'  => (float) $product->get_price(),
            'stock'  => (int) $product->get_stock_quantity(),
            'status' => (string) $product->get_status(),
        ];

        Helper::logData("Observer: Product with ID:{$product_id} AFTER update :" . var_export($after, true), $this->log_channel);

        $changes = [];

        if ($before['price'] !== $after['price']) {
            $changes['price'] = true;
        }

        if ($before['stock'] !== $after['stock']) {
            $changes['stock'] = true;
        }

        if ($before['status'] !== $after['status']) {
            $changes['status'] = true;
        }

        if (empty($changes)) {
            Helper::logData("Observer: Product with ID:{$product_id} has no changes", $this->log_channel);
            return;
        }

        Helper::logData("Observer: Product with ID:{$product_id} has changes: " . var_export($changes, true), $this->log_channel);

        $type = $this->resolveTypeFromChanges($changes);

        $this->markForSync($product_id, 0, $type, $changes);
    }

    /**
     * ===============================
     * VARIATION UPDATE
     * ===============================
     */
    public function onVariationUpdate($variation_id)
    {
        Helper::logData("Product ID:{$product_id}", $this->log_channel);
        Helper::logData("Global meliconnect_sync_source: " . var_export($GLOBALS['meliconnect_sync_source'], true), $this->log_channel);
        Helper::logData("Global meliconnect_sync_running: " . var_export($GLOBALS['meliconnect_sync_running'], true), $this->log_channel);

        if (!$this->isEnabled('meliconnect_sync_enabled')) {
            Helper::logData('Sync is disabled by settings', $this->log_channel);
            return;
        }

        if ($this->isMeliToWoo()) {
            Helper::logData('Observer disabled because meliconnect_sync_direction is meli_to_woo', $this->log_channel);
            return;
        }

        if ($this->isSyncRunning() || $this->isFromMeli()) {
            Helper::logData("Global meliconnect_sync_running or Global meliconnect_sync_source is meli", $this->log_channel);
            return;
        }

        $variation = wc_get_product($variation_id);
        
        if (!$variation) return;

        if ($this->isSyncRunning() || $this->isFromMeli()) return;

        $before = $this->before_state[$variation_id] ?? null;

        Helper::logData("Observer: Variation with ID:{$variation_id} BEFORE update" . var_export($before, true), $this->log_channel);

        if (!$before) return;

        $after = [
            'price'  => $variation->get_price(),
            'stock'  => $variation->get_stock_quantity(),
            'status' => $variation->get_status(),
        ];

        Helper::logData("Observer: Variation with ID:{$variation_id} AFTER update" . var_export($after, true), $this->log_channel);

        $changes = [];

        if ((string)$before['price'] !== (string)$after['price']) {
            $changes['price'] = true;
        }

        if ((string)$before['stock'] !== (string)$after['stock']) {
            $changes['stock'] = true;
        }

        if ((string)$before['status'] !== (string)$after['status']) {
            $changes['status'] = true;
        }

        if (empty($changes)) return;

        $type = $this->resolveTypeFromChanges($changes);

        $this->markForSync(
            $variation->get_parent_id(),
            $variation_id,
            $type,
            $changes
        );
    }

    public function onPriceChange($product)
    {
        if (!$product) return;

        $product_id   = $product->get_id();
        $variation_id = $product->is_type('variation') ? $product_id : 0;

        Helper::logData("Observer: Price change {$product_id}", $this->log_channel);

        if (!$this->isEnabled('meliconnect_sync_price')) {
            Helper::logData('Sync Price is disabled', $this->log_channel);
            return;
        }

        if ($this->isSyncRunning() || $this->isFromMeli()) {
            Helper::logData("Global meliconnect_sync_running or Global meliconnect_sync_source is meli", $this->log_channel);
            return;
        }

        $this->markForSync(
            $variation_id ? $product->get_parent_id() : $product_id,
            $variation_id,
            'price'
        );
    }

    /**
     * ===============================
     * STOCK CHANGE
     * ===============================
     */
    public function onStockChange($product)
    {
        if (!$product) return;

        $product_id   = $product->get_id();
        $variation_id = $product->is_type('variation') ? $product_id : 0;

        Helper::logData("Observer: Stock change {$product_id}", $this->log_channel);

        if (!$this->isEnabled('meliconnect_sync_stock')) {
            Helper::logData('Sync STOCK is disabled', $this->log_channel);
            return;
        }

        if ($this->isSyncRunning() || $this->isFromMeli()) {
            Helper::logData("Global meliconnect_sync_running or Global meliconnect_sync_source is meli", $this->log_channel);
            return;
        }

        $this->markForSync(
            $variation_id ? $product->get_parent_id() : $product_id,
            $variation_id,
            'stock'
        );
    }

    /**
     * ===============================
     * STATUS CHANGE
     * ===============================
     */
    public function onStatusChange($new_status, $old_status, $post)
    {
        if (!in_array($post->post_type, ['product', 'product_variation'])) return;
        if ($new_status === $old_status) return;

        Helper::logData(
            "Observer: Status change {$post->ID} {$old_status} → {$new_status}",
            $this->log_channel
        );

        if (!$this->isEnabled('meliconnect_sync_status')) {
            Helper::logData('Sync STATUS is disabled', $this->log_channel);
            return;
        }

        if ($this->isSyncRunning() || $this->isFromMeli()) {
            Helper::logData("Global meliconnect_sync_running or Global meliconnect_sync_source is meli", $this->log_channel);
            return;
        }

        if ($post->post_type === 'product_variation') {

            $variation = wc_get_product($post->ID);
            if (!$variation) return;

            $this->markForSync($variation->get_parent_id(), $post->ID, 'status');
        } else {
            $this->markForSync($post->ID, 0, 'status');
        }
    }

    /**
     * ===============================
     * CORE
     * ===============================
     */
    private function markForSync($product_id, $variation_id = 0, $syncType = 'full', $newChanges = [])
    {
        global $wpdb;

        $table = $wpdb->prefix . 'meliconnect_sync_state';

        $items = $this->getSyncItems($product_id, $variation_id);

        if (empty($items)) {

            Helper::logData("No sync_state → creating for product {$product_id}", $this->log_channel);

            $this->upsertSyncState($product_id, $variation_id);

            // volver a buscar
            $items = $this->getSyncItems($product_id, $variation_id);

            if (empty($items)) {
                Helper::logData("Failed to create sync_state for {$product_id}", $this->log_channel);
                return;
            }
        }

        $woo = wc_get_product($variation_id ?: $product_id);

        if (!$woo) {
            Helper::logData(
                'Woo product with id ' . ($variation_id ?: $product_id) . ' not found.',
                $this->log_channel
            );
            return;
        };

        foreach ($items as $item) {

            $existingChanges = json_decode($item->changes, true);
            $existingChanges = is_array($existingChanges) ? $existingChanges : [];

            // merge inteligente
            $mergedChanges = array_merge($existingChanges, $newChanges);

            $finalType = $this->resolveTypeFromChanges($mergedChanges);

            $hash = SyncHashHelper::buildHashFromWoo($woo, $finalType);

            $isNew = empty($item->last_hash); // o created_at == updated_at


            if (!empty($item->last_hash) && $item->last_hash === $hash) {
                Helper::logData("Skip item {$item->id} - hash identical", $this->log_channel);
                continue;
            };

            if (!$isNew && !empty($item->updated_at) && strtotime($item->updated_at) > time() - 5) {
                Helper::logData("Skip item {$item->id} - debounce", $this->log_channel);
                continue;
            }

            if ($item->source === 'meli') {
                Helper::logData("Skip item {$item->id} - source=meli", $this->log_channel);
                continue;
            }

            if (!empty($item->last_sync_at) && strtotime($item->last_sync_at) > time() - 3) continue;

            $priority = SyncHashHelper::resolvePriority($finalType);

            $wpdb->update(
                $table,
                [
                    'sync_status' => 'pending',
                    'needs_sync'  => 1,
                    'sync_type'   => $finalType,
                    'changes'     => wp_json_encode($mergedChanges),
                    'priority'    => $priority,
                    'source'      => 'woo',
                    'updated_at'  => current_time('mysql')
                ],
                ['id' => $item->id]
            );

            Helper::logData("Observer: Enqueued {$finalType} with changes " . json_encode($mergedChanges), $this->log_channel);
        }
    }

    private function getSyncItems($product_id, $variation_id = 0)
    {
        global $wpdb;

        $table = $wpdb->prefix . 'meliconnect_sync_state';

        if ($variation_id) {
            return $wpdb->get_results($wpdb->prepare(
                "SELECT * FROM {$table}
             WHERE woo_product_id = %d
             AND woo_variation_id = %d",
                $product_id,
                $variation_id
            ));
        }

        return $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$table}
         WHERE woo_product_id = %d
         AND woo_variation_id IS NULL",
            $product_id
        ));
    }

    private function upsertSyncState($product_id, $variation_id = 0)
    {
        global $wpdb;

        $table = $wpdb->prefix . 'meliconnect_sync_state';

        $woo = wc_get_product($variation_id ?: $product_id);
        if (!$woo) {
            Helper::logData(
                'Woo product with id ' . ($variation_id ?: $product_id) . ' not found.',
                $this->log_channel
            );
            return;
        }

        $meli_id   = get_post_meta($variation_id ?: $product_id, 'meliconnect_meli_listing_id', true);
        $seller_id = get_post_meta($variation_id ?: $product_id, 'meliconnect_meli_seller_id', true);

        if (!$meli_id) {
            Helper::logData("Skip upsert {$product_id} - no meli_id", $this->log_channel);
            return;
        }

        $wpdb->insert($table, [
            'woo_product_id'     => $product_id,
            'woo_variation_id'   => $variation_id ?: null,
            'meli_item_id'       => $meli_id,
            'meli_seller_id'     => $seller_id,

            'sync_status' => 'pending',
            'needs_sync'  => 1,
            'sync_type'   => 'full',
            'priority'    => 10,

            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ]);

        Helper::logData("Created sync_state {$meli_id}", $this->log_channel);
    }
}
