<?php

namespace Meliconnect\Meliconnect\Core;

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

use Meliconnect\Meliconnect\Core\Helpers\Helper;
use Meliconnect\Meliconnect\Core\Helpers\MeliconMeli;
use Meliconnect\Meliconnect\Core\Helpers\SyncHashHelper;
use Meliconnect\Meliconnect\Core\Services\SyncContextResolver;

class SyncService
{
    private $log_channel = 'sync-cron';
    private $settings;

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


    public function sync($item, $direction, $syncType = 'full')
    {
        Helper::logData("---- SYNC SERVICE START item {$item->id} | {$direction} ----", $this->log_channel);

        if (!$this->isSyncTypeEnabled($syncType)) {
            Helper::logData(
                "SYNC SKIPPED item {$item->id} | type={$syncType} disabled by config",
                $this->log_channel
            );
            return false;
        }

        // ===============================
        // CONTEXTO
        // ===============================
    
        $context = SyncContextResolver::build(
            $item,
            $this->log_channel
        );

        if (!$context) {
            Helper::logData(
                "SYNC SKIPPED item {$item->id} | unable to build context",
                $this->log_channel
            );
            throw new \Exception(
                "Unable to build sync context for item {$item->id}"
            );
        }

        $woo            = $context['woo'];
        $meli           = $context['meli'];
        $meliRaw        = $context['meli_raw'];
        $meliVariation  = $context['meli_variation'];

        // ===============================
        // VALIDACIONES
        // ===============================
        $type = $woo->get_type();

        /* Helper::logData([
            'message' => 'Woo product resolved',
            'item_id' => $item->id,
            'woo_product_id' => $item->woo_product_id,
            'woo_variation_id' => $item->woo_variation_id,
            'woo_type' => $type,
        ], $this->log_channel); */

        if (
            $woo->is_type('variable') &&
            empty($item->woo_variation_id)
        ) {

            Helper::logData([
                'message' => 'SYNC SKIPPED',
                'item_id' => $item->id,
                'reason' => 'variable product without variation_id'
            ], $this->log_channel);

            return false;
        }

        /* Helper::logData(
            "Meli item normalized: " . json_encode($meli),
            $this->log_channel
        ); */

        $GLOBALS['meliconnect_sync_running'] = true;

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

        Helper::logData(
            "SYNC TYPE={$syncType} CHANGES=" . json_encode($changes),
            $this->log_channel
        );


        try {

            // ===============================
            //  DIRECCIÓN
            // ===============================
            if ($direction === 'woo_to_meli') {

                // IMPORTANTE: evitar loop
                $GLOBALS['meliconnect_sync_source'] = 'woo';

                Helper::logData('Global source: woo', $this->log_channel);

                $before = $this->captureMeliState($meli);


                $updatedMeliResponse = $this->pushToMeli($item, $woo, $syncType, $changes);

                //Helper::logData('Updated meli item: ' . var_export((object)$updatedMeliResponse, true), $this->log_channel);

                if (!$updatedMeliResponse) {
                    throw new \Exception("Empty Meli response");
                }

                $updatedMeli = SyncHashHelper::normalizeMeliItem((object)$updatedMeliResponse);

                //Helper::logData('Normalized meli item: ' . var_export($updatedMeli, true), $this->log_channel);


                $after = $this->captureMeliState($updatedMeli);

                //$new_hash = SyncHashHelper::buildHashFromWoo($woo, 'full');
                $new_hash = SyncHashHelper::buildHashFromWoo($woo, $syncType, $changes);

                $snapshot_source = 'woo';
            } else {

                // IMPORTANTE: evitar loop
                $GLOBALS['meliconnect_sync_source'] = 'meli';
                Helper::logData('Global source: meli', $this->log_channel);

                $before = $this->captureWooState($woo);

                $this->pullFromMeli($woo, $meli, $syncType, $changes);

                $after = $this->captureWooState($woo);

                //$new_hash = SyncHashHelper::buildHashFromMeli($meli, 'full');
                $new_hash = SyncHashHelper::buildHashFromMeli($meli, $syncType, $changes);

                $snapshot_source = 'meli';
            }

            $snapshot_id = get_option('meliconnect_active_snapshot_id_sync') ?? null;


            $this->diffAndLog($item, $before, $after, $direction, $snapshot_id, $changes);


            // ===============================
            // SNAPSHOT
            // ===============================
            $this->updateSnapshot($item->id, $woo, $meli, $new_hash, $syncType, $snapshot_source, $changes);

            global $wpdb;

            $wpdb->update(
                $wpdb->prefix . 'meliconnect_sync_state',
                ['changes' => null],
                ['id' => $item->id]
            );

            Helper::logData("SYNC OK item {$item->id}", $this->log_channel);

            return true;
        } catch (\Throwable $e) {

            // log detallado
            Helper::logData(
                "SYNC ERROR item {$item->id}: {$e->getMessage()}",
                $this->log_channel
            );

            // importante: re-lanzar
            throw $e;
        } finally {

            //Helper::logData("Global source and running: null", $this->log_channel);

            // limpiar SIEMPRE
            $GLOBALS['meliconnect_sync_source'] = null;
            $GLOBALS['meliconnect_sync_running'] = false;
        }
    }



    public function captureMeliState($meli)
    {
        return [
            'price'  => (float) ($meli['price'] ?? 0),
            'stock'  => (int) ($meli['available_quantity'] ?? 0),
            'status' => $meli['status'] ?? 'undefined',
        ];
    }

    private function captureWooState($woo)
    {
        return [
            'price'  => (float) ($woo->get_price() ?? 0),
            'stock'  => (int) ($woo->get_stock_quantity() ?? 0),
            'status' => $woo->get_status() ?? 'undefined',
        ];
    }

    private function diffAndLog($item, $before, $after, $direction, $snapshot_id, $changes = [])
    {

        foreach ($changes as $field => $_) {

            $oldValue = $before[$field] ?? null;
            $newValue = $after[$field] ?? null;
            $source = $direction === 'woo_to_meli' ? 'woo changes' : 'meli event';

            if ((string)$oldValue !== (string)$newValue) {

                self::logChange([
                    'sync_state_id'  => $item->id,
                    'woo_product_id' => $item->woo_variation_id ?: $item->woo_product_id,
                    'meli_item_id'   => $item->meli_item_id,
                    'action'         => $this->mapActionByField($field),
                    'source'         => $source,
                    'field'          => $field,
                    'old_value'      => $oldValue,
                    'new_value'      => $newValue,
                    'direction'      => $direction,
                    'trigger_type'   => 'system',
                    'settings_snapshot_id' => $snapshot_id,
                    'message'        => "Sync {$direction} | {$field}",
                ]);
                Helper::logData("Log change. Direction:{$direction} | {$field}... Old: {$oldValue} , New: {$newValue}.", $this->log_channel);
            } else {
                //Helper::logData("Skipping log change. Direction:{$direction} | {$field}... Old: {$oldValue} , New: {$newValue}.", $this->log_channel);
            }
        }
    }

    public static function logChange($data)
    {
        global $wpdb;

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

        $wpdb->insert($table, [
            'sync_state_id'      => $data['sync_state_id'] ?? null,
            'woo_product_id'     => $data['woo_product_id'] ?? null,
            'meli_item_id'       => $data['meli_item_id'] ?? null,
            'action'             => $data['action'],
            'source'             => $data['source'],
            'field'              => $data['field'],
            'old_value'          => maybe_serialize($data['old_value']),
            'new_value'          => maybe_serialize($data['new_value']),
            'direction'          => $data['direction'] ?? null,
            'trigger_type'       => $data['trigger_type'] ?? 'system',
            'status'             => 'success',
            'message'            => $data['message'] ?? null,
            'settings_snapshot_id' => $data['settings_snapshot_id'] ?? null,
            'plugin_version'     => MELICONNECT_VERSION,
            'created_at'         => current_time('mysql'),
        ]);
    }

    private function mapActionByField($field)
    {
        return match ($field) {
            'price'  => 'price_updated',
            'stock'  => 'stock_updated',
            'status' => 'status_updated',
            default  => 'sync_update',
        };
    }





    private function isSyncTypeEnabled($type)
    {
        return match ($type) {
            'price'   => ($this->settings['meliconnect_sync_price'] ?? 'true') === 'true',
            'stock'   => ($this->settings['meliconnect_sync_stock'] ?? 'true') === 'true',
            'status' => ($this->settings['meliconnect_sync_status'] ?? 'true') === 'true',
            'full'    => true,
            default   => true
        };
    }

    /**
     * 🚀 PUSH Woo → Meli
     */
    private function pushToMeli($item, $woo, $syncType, $changes = [])
    {
        $seller = $this->getSeller($item->meli_seller_id);

        $meli = new MeliconMeli(
            $seller->app_id,
            $seller->secret_key,
            $seller->access_token
        );

        $body = [];

        // 🔥 PRICE
        if (
            !empty($changes['price']) &&
            ($this->settings['meliconnect_sync_price'] ?? 'true') === 'true'
        ) {
            $body['price'] = (float) $woo->get_price();
        }

        // 🔥 STOCK
        if (
            !empty($changes['stock']) &&
            ($this->settings['meliconnect_sync_stock'] ?? 'true') === 'true'
        ) {
            $body['available_quantity'] = (int) $woo->get_stock_quantity();
        }

        // 🔥 STATUS
        if (
            !empty($changes['status']) &&
            ($this->settings['meliconnect_sync_status'] ?? 'true') === 'true'
        ) {
            $body['status'] = SyncHashHelper::mapStatus($woo->get_status());
        }

        // ⚠️ fallback FULL si no hay changes
        if (empty($changes)) {

            Helper::logData(
                "Empty changes → FULL fallback push",
                $this->log_channel
            );

            if (($this->settings['meliconnect_sync_price'] ?? 'true') === 'true') {
                $body['price'] = (float) $woo->get_price();
            }

            if (($this->settings['meliconnect_sync_stock'] ?? 'true') === 'true') {
                $body['available_quantity'] = (int) $woo->get_stock_quantity();
            }

            if (($this->settings['meliconnect_sync_status'] ?? 'true') === 'true') {
                $body['status'] = SyncHashHelper::mapStatus($woo->get_status());
            }
        }

        if (empty($body)) {
            Helper::logData(
                "SYNC SKIPPED item {$item->id} | empty payload after filtering",
                $this->log_channel
            );
            return;
        }

        Helper::logData(
            "Push payload: " . json_encode($body),
            $this->log_channel
        );

        if (!empty($item->woo_variation_id)) {

            $meliVariationId = get_post_meta(
                $item->woo_variation_id,
                'meliconnect_meli_asoc_variation_id',
                true
            );

            if ($meliVariationId) {

                $endpoint = "/items/{$item->meli_item_id}/variations/{$meliVariationId}";
            } else {

                $endpoint = "/items/{$item->meli_item_id}";
            }
        } else {

            $endpoint = "/items/{$item->meli_item_id}";
        }

        $res = $meli->put_v2(
            $endpoint,
            $body,
            ['access_token' => $seller->access_token]
        );

        $this->validateResponse($res);

        return $res['body'] ?? null;
    }

    /**
     * PULL Meli → Woo
     */
    private function pullFromMeli($woo, $meli, $syncType, $changes = [])
    {
        // ===============================
        // NORMALIZAR SETTINGS
        // ===============================
        $priceEnabled  = filter_var($this->settings['meliconnect_sync_price'] ?? true, FILTER_VALIDATE_BOOLEAN);
        $stockEnabled  = filter_var($this->settings['meliconnect_sync_stock'] ?? true, FILTER_VALIDATE_BOOLEAN);
        $statusEnabled = filter_var($this->settings['meliconnect_sync_status'] ?? true, FILTER_VALIDATE_BOOLEAN);
        $updated = false;

        // ===============================
        // VALIDACIÓN DE CAMBIOS
        // ===============================
        if ($syncType !== 'full' && empty($changes)) {
            Helper::logData("Skip pull → no changes provided for partial sync", $this->log_channel);
            return;
        }

        // ===============================
        // DEFINIR QUÉ APLICAR
        // ===============================
        $applyPrice  = $syncType === 'full' || !empty($changes['price']);
        $applyStock  = $syncType === 'full' || !empty($changes['stock']);
        $applyStatus = $syncType === 'full' || !empty($changes['status']);

        // ===============================
        // PRICE
        // ===============================
        if (!$applyPrice) {
            Helper::logData("Price skipped (not in changes)", $this->log_channel);
        } elseif (!$priceEnabled) {
            Helper::logData("Price skipped (disabled in settings)", $this->log_channel);
        } else {
            $oldPrice = (float) $woo->get_price();
            $newPrice = (float) ($meli['price'] ?? 0);

            if ($oldPrice !== $newPrice) {
                $woo->set_regular_price($newPrice);
                $woo->set_price($newPrice);
                $updated = true;

                Helper::logData("Price changed {$oldPrice} → {$newPrice}", $this->log_channel);
            } else {
                //Helper::logData("Price unchanged ({$oldPrice})", $this->log_channel);
            }
        }

        // ===============================
        // STOCK
        // ===============================
        if (!$applyStock) {
            Helper::logData("Stock skipped (not in changes)", $this->log_channel);
        } elseif (!$stockEnabled) {
            Helper::logData("Stock skipped (disabled in settings)", $this->log_channel);
        } else {
            $oldStock = (int) $woo->get_stock_quantity();
            $newStock = (int) ($meli['available_quantity'] ?? 0);

            if ($oldStock !== $newStock) {
                $woo->set_stock_quantity($newStock);
                $updated = true;

                Helper::logData("Stock changed {$oldStock} → {$newStock}", $this->log_channel);
            } else {
                //Helper::logData("Stock unchanged ({$oldStock})", $this->log_channel);
            }
        }

        // ===============================
        // STATUS
        // ===============================
        if (!$applyStatus) {
            //Helper::logData("Status skipped (not in changes)", $this->log_channel);
        } elseif (!$statusEnabled) {
            Helper::logData("Status skipped (disabled in settings)", $this->log_channel);
        } else {
            $oldStatus = $woo->get_status();
            $newStatus = SyncHashHelper::mapMeliStatus($meli['status'] ?? '');

            if ($oldStatus !== $newStatus) {
                $woo->set_status($newStatus);
                $updated = true;

                Helper::logData(
                    "Status changed {$oldStatus} → {$newStatus} (meli: {$meli['status']})",
                    $this->log_channel
                );
            } else {
                //Helper::logData("Status unchanged ({$oldStatus})", $this->log_channel);
            }
        }

        // ===============================
        // SAVE
        // ===============================
        if ($updated) {

            $woo->save();

            Helper::logData("Product saved (changes applied)", $this->log_channel);
        } else {
            Helper::logData("No changes detected - Product not saved", $this->log_channel);
        }

        Helper::logData(
            "Pull completed → type={$syncType} changes=" . json_encode($changes),
            $this->log_channel
        );
    }



    /**
     * 👤 SELLER
     */
    private function getSeller($seller_id)
    {
        $seller = \Meliconnect\Meliconnect\Core\Models\UserConnection::getUser($seller_id);

        if (!$seller || empty($seller->access_token)) {
            Helper::logData("Invalid seller {$seller_id}", $this->log_channel);
            throw new \Exception("Invalid seller {$seller_id}");
        }

        return $seller;
    }

    /**
     * 💾 SNAPSHOT UPDATE
     */
    private function updateSnapshot($id, $woo, $meli, $hash, $syncType, $source, $changes = [])
    {
        global $wpdb;

        $data = [
            'last_hash'    => $hash,
            'last_sync_at' => current_time('mysql')
        ];

        // Fuente
        if ($source === 'woo') {
            $price  = (float) $woo->get_price();
            $stock  = (int) $woo->get_stock_quantity();
            $status = SyncHashHelper::mapStatus($woo->get_status());
        } else {
            $price  = (float) ($meli['price'] ?? 0);
            $stock  = (int) ($meli['available_quantity'] ?? 0);
            $status = $meli['status'] ?? 'undefined';
        }

        // 🔥 SOLO basado en changes
        if (!empty($changes['price'])) {
            $data['last_synced_price'] = $price;
        }

        if (!empty($changes['stock'])) {
            $data['last_synced_stock'] = $stock;
        }

        if (!empty($changes['status'])) {
            $data['last_synced_status'] = $status;
        }

        // Compatibilidad con full
        if (empty($changes)) {
            $data['last_synced_price']  = $price;
            $data['last_synced_stock']  = $stock;
            $data['last_synced_status'] = $status;
        }

        $wpdb->update(
            $wpdb->prefix . 'meliconnect_sync_state',
            $data,
            ['id' => $id]
        );

        Helper::logData(
            "SNAPSHOT UPDATED item {$id}",
            $this->log_channel
        );
    }


    private function validateResponse($response)
    {
        if (!isset($response['httpCode']) || $response['httpCode'] >= 300) {

            $error = $response['body']['message'] ?? 'Unknown error';

            throw new \Exception("Meli error: {$error}");
        }
    }
}
