<?php
// sincronizacionml.php

require __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/config/db.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

/** 1) Obtener credenciales más recientes */
function getCredentials(mysqli $conn): array
{
    $stmt = $conn->prepare("
        SELECT user_id, access_token, refresh_token, expires_in, updated_at
          FROM tb_marketplace_tokens
         WHERE idMP = 1
         ORDER BY updated_at DESC
         LIMIT 1
    ");
    $stmt->execute();
    $stmt->bind_result($user_id, $access_token, $refresh_token, $expires_in, $updated_at);
    $stmt->fetch();
    $stmt->close();

    return [
        'user_id'       => $user_id,
        'access_token'  => $access_token,
        'refresh_token' => $refresh_token,
        'expires_in'    => $expires_in,
        'updated_at'    => $updated_at,
    ];
}

/** 2) Refrescar token si expira (1 min antes) */
function refreshTokenIfNeeded(Client $http, array &$creds, mysqli $conn)
{
    $expireTs = strtotime($creds['updated_at']) + $creds['expires_in'];
    if (time() < $expireTs - 60) return;

    $resp = $http->post('/oauth/token', [
        'form_params' => [
            'grant_type'    => 'refresh_token',
            'client_id'     => '3658695067033012',
            'client_secret' => 'GZBg63xJNM9Eeb2cOhF9JwsOEv6vTxo7',
            'refresh_token' => $creds['refresh_token'],
        ],
        'headers' => ['Accept' => 'application/json'],
    ]);
    $data = json_decode((string)$resp->getBody(), true);

    // Actualizar en memoria y en BD
    $creds['access_token']  = $data['access_token'];
    $creds['refresh_token'] = $data['refresh_token'];
    $creds['expires_in']    = $data['expires_in'];

    $u = $conn->prepare("
        UPDATE tb_marketplace_tokens
           SET access_token  = ?, refresh_token = ?, expires_in = ?, updated_at = NOW()
         WHERE user_id = ?
    ");
    $u->bind_param(
        'ssis',
        $creds['access_token'],
        $creds['refresh_token'],
        $creds['expires_in'],
        $creds['user_id']
    );
    $u->execute();
    $u->close();
}

/** 3) Borrar logs previos */
function clearOldLogs(mysqli $conn)
{
    $conn->query("
        DELETE FROM tb_marketplace_logs
         WHERE idMarketplace = 1
           AND instancia     = 'sincronizacion'
    ");
}

/** 4) Registrar un log de error */
function logError(mysqli $conn, string $msg)
{
    $stmt = $conn->prepare("
        INSERT INTO tb_marketplace_logs (idMarketplace, instancia, mensaje)
        VALUES (1, 'sincronizacion', ?)
    ");
    $stmt->bind_param('s', $msg);
    $stmt->execute();
    $stmt->close();
}

/** 5) Marcar producto simple como sincronizado */
function markProductSynced(mysqli $conn, int $id)
{
    $upd = $conn->prepare("
        UPDATE tb_marketplace_productos
           SET sincronizar         = 0,
               fechaSincronizacion = NOW()
         WHERE id = ?
    ");
    $upd->bind_param('i', $id);
    $upd->execute();
    $upd->close();
}

/** 6) Marcar variante como sincronizada */
function markVariantSynced(mysqli $conn, int $id)
{
    $upd = $conn->prepare("
        UPDATE tb_marketplace_variantes
           SET sincronizar         = 0,
               fechaSincronizacion = NOW()
         WHERE id = ?
    ");
    $upd->bind_param('i', $id);
    $upd->execute();
    $upd->close();
}

/**
 * 7) PUT /items/{itemId} con throttling y retry ante 429
 */
function updateWithRetry(Client $http, string $token, string $endpoint, array $payload, int $maxRetries = 3)
{
    $attempt = 0;
    while (true) {
        try {
            usleep(100000);  // 10 req/s
            $resp = $http->put($endpoint, [
                'headers' => [
                    'Authorization' => "Bearer {$token}",
                    'Content-Type'  => 'application/json',
                ],
                'json' => $payload,
            ]);
            return json_decode((string)$resp->getBody(), true);
        } catch (RequestException $e) {
            $code = $e->getResponse()?->getStatusCode() ?? 0;
            if ($code === 429 && $attempt < $maxRetries) {
                $wait = (int)$e->getResponse()->getHeaderLine('Retry-After') ?: 5;
                sleep($wait);
                $attempt++;
                continue;
            }
            throw $e;
        }
    }
}

/** 8) Proceso principal */
function sincronizacionML(mysqli $conn)
{
    // --- Configuración inicial ---
    $creds = getCredentials($conn);
    if (empty($creds['access_token'])) {
        die("ERROR: Ejecuta authorize.php para obtener token primero.\n");
    }
    $http  = new Client(['base_uri' => 'https://api.mercadolibre.com', 'timeout' => 30]);
    refreshTokenIfNeeded($http, $creds, $conn);
    $token = $creds['access_token'];

    clearOldLogs($conn);

    // --- 8.1) Leer flags de productos ---
    $productFlags = [];  // itemId => ['localId', 'title','description','price','stock']
    $sqlP = "
      SELECT id, refMP, tituloPublicacion, descripcionPublicacion,
             precioPublicacion, stockPublicacion, vendidosMP
        FROM tb_marketplace_productos
       WHERE idMarketplace = 1
         AND sincronizar   = 1 AND conectado=1";
    $resP = $conn->query($sqlP);
    while ($r = $resP->fetch_assoc()) {
        $productFlags[$r['refMP']] = [
            'localId'     => (int)$r['id'],
            'title'       => $r['tituloPublicacion'],
            'description' => $r['descripcionPublicacion'],
            'price'       => (float)$r['precioPublicacion'],
            'stock'       => (int)$r['stockPublicacion'],
            'sold_qty'    => (int)$r['vendidosMP'],
        ];
    }
    $resP->free();

    // --- 8.2) Leer flags de variantes ---
    $variantGroups = []; // itemId => [ [variation_id, price, stock, local_variant_id], ... ]
    $sqlV = "
      SELECT v.id AS local_variant_id,
            p.id AS local_prod_id,
             p.refMP AS item_id,
             v.refMPVariante AS variation_id,
             v.precioPublicacion AS price,
             v.stockPublicacion  AS stock
        FROM tb_marketplace_variantes v
        JOIN tb_marketplace_productos p
          ON v.idMarketplaceProducto = p.id
       WHERE v.sincronizar = 1
         AND p.idMarketplace = 1 AND v.conectado=1
    ";
    $resV = $conn->query($sqlV);
    while ($v = $resV->fetch_assoc()) {
        $variantGroups[$v['item_id']][] = [
            'variation_id'     => (string)$v['variation_id'],
            'price'            => (float)$v['price'],
            'stock'            => (int)$v['stock'],
            'local_variant_id' => (int)$v['local_variant_id'],
            'local_prod_id'    => (int)$v['local_prod_id'],  // **<– lo agregas aquí**
        ];
    }
    $resV->free();

    // --- 8.3) Obtener datos remotos de todos los ítems implicados ---
    $itemIds = array_unique(
        array_merge(
            array_keys($productFlags),
            array_keys($variantGroups)
        )
    );
    $remote = []; // itemId => body
    $chunks = array_chunk($itemIds, 20);
    foreach ($chunks as $chunk) {
        $idsParam = implode(',', $chunk);
        $resp = $http->get('/items', [
            'query'   => ['ids' => $idsParam],
            'headers' => ['Authorization' => "Bearer {$token}", 'Accept' => 'application/json'],
        ]);
        $batch = json_decode((string)$resp->getBody(), true);
        foreach ($batch as $entry) {
            $b = $entry['body'];
            $remote[$b['id']] = $b;
        }
    }

    // --- 8.4) Sincronizar productos *sin* variaciones ---
    foreach ($remote as $itemId => $body) {
        // ahora: sincroniza título/descr. de TODOS los productos flagged
        if (isset($productFlags[$itemId]) && (
            empty($body['variations'])      // simples
            || ! isset($variantGroups[$itemId])  // tiene variaciones, pero ninguna marcada
        )) {
            $p = $productFlags[$itemId];
            try {
                // descripción
                updateWithRetry($http, $token, "/items/{$itemId}/description", [
                    'plain_text' => $p['description'],
                ]);
                // título
                updateWithRetry($http, $token, "/items/{$itemId}", [
                    'title' => $p['title'],
                ]);
                // Si es simple (sin variaciones) también hace price/stock y marca...
                if (empty($body['variations'])) {
                    updateWithRetry($http, $token, "/items/{$itemId}", [
                        'price'              => $p['price'],
                        'available_quantity' => $p['stock'],
                    ]);
                }
                markProductSynced($conn, $p['localId']);
            } catch (RequestException $e) {
                logError($conn, "Error sync producto {$itemId}: " . $e->getMessage());
                $err = $conn->prepare("
        UPDATE tb_marketplace_productos
           SET sincronizar = 3
         WHERE id = ?
    ");
                $err->bind_param('i', $p['localId']);
                $err->execute();
                $err->close();
            }
        }
    }

    // --- 8.5) Sincronizar variaciones de items *con* variaciones ---
    foreach ($remote as $itemId => $body) {
        if (!empty($body['variations']) && isset($variantGroups[$itemId])) {
            $existing    = $body['variations'];
            $flagged     = $variantGroups[$itemId];
            $localProdId = $flagged[0]['local_prod_id']; // id del producto local

            // Construir payload completo de variations
            $varsPayload = [];
            foreach ($existing as $ev) {
                $vid      = (string)$ev['id'];
                $newPrice = $ev['price'];
                $newQty   = $ev['available_quantity'];

                // Si está en flagged, sobreescribir
                foreach ($flagged as $fv) {
                    if ($fv['variation_id'] === $vid) {
                        $newPrice = $fv['price'];
                        $newQty   = $fv['stock'];
                        break;
                    }
                }

                $varsPayload[] = [
                    'id'                 => (int)$vid,
                    'price'              => $newPrice,
                    'available_quantity' => $newQty,
                ];
            }

            try {
                // 1) Actualizar variaciones
                updateWithRetry($http, $token, "/items/{$itemId}", [
                    'variations' => $varsPayload
                ]);
                // 2) Actualizar descripción
                updateWithRetry($http, $token, "/items/{$itemId}/description", [
                    'plain_text' => $productFlags[$itemId]['description']
                ]);
                // 3) Actualizar título
                updateWithRetry($http, $token, "/items/{$itemId}", [
                    'title' => $productFlags[$itemId]['title'],
                ]);
                // 4) Marcar variantes y producto como sincronizados
                foreach ($flagged as $fv) {
                    markVariantSynced($conn, $fv['local_variant_id']);
                }
                markProductSynced($conn, $localProdId);
            } catch (RequestException $e) {
                // Loguear error
                logError($conn, "Error sync variaciones {$itemId}: " . $e->getMessage());

                // Marcar producto con error
                $errP = $conn->prepare("
                UPDATE tb_marketplace_productos
                   SET sincronizar = 3
                 WHERE id = ?
            ");
                $errP->bind_param('i', $localProdId);
                $errP->execute();
                $errP->close();

                // Marcar cada variante con error
                foreach ($flagged as $fv) {
                    $errV = $conn->prepare("
                    UPDATE tb_marketplace_variantes
                       SET sincronizar = 3
                     WHERE id = ?
                ");
                    $errV->bind_param('i', $fv['local_variant_id']);
                    $errV->execute();
                    $errV->close();
                }
            }
        }
    }


    // --- Al terminar de procesar todos los items, verifico si quedaron pendientes ---
    $cntP = $conn->query("
        SELECT COUNT(*) AS cnt
          FROM tb_marketplace_productos
         WHERE idMarketplace = 1
           AND sincronizar   = 1 AND conectado=1
    ")->fetch_assoc()['cnt'];
    $cntV = $conn->query("
        SELECT COUNT(*) AS cnt
          FROM tb_marketplace_variantes v
          JOIN tb_marketplace_productos p
            ON v.idMarketplaceProducto = p.id
         WHERE p.idMarketplace = 1
           AND v.sincronizar   = 1 AND v.conectado=1
    ")->fetch_assoc()['cnt'];

    if ($cntP > 0 || $cntV > 0) {
        echo "Sincronización incompleta ({$cntP} productos, {$cntV} variantes pendientes)\n";
        return false;
    }

    echo "¡Sincronización completada con éxito!\n";
    return true;
}



// ▶️ Ejecutar sincronización con hasta 3 reintentos
$maxRetries = 3;
for ($i = 1; $i <= $maxRetries; $i++) {
    if (sincronizacionML($conn)) {
        // éxito en el intento $i
        break;
    }
    if ($i === $maxRetries) {
        die("‼️ Sincronización falló tras {$maxRetries} intentos.\n");
    }
    echo "🔄 Reintentando sincronización (intento " . ($i + 1) . ") en 5 segundos…\n";
    sleep(5);
}

// Cerrar la conexión una sola vez
$conn->close();
