<?php
/**
 * POST /api/emag-sync.php
 *
 * Acțiuni:
 *  action=page     — sincronizează O pagină din API eMAG; returnează stats + has_more
 *      Body: account_id, page (1..N)
 *
 *  action=finalize — închide sesiunea de sync (scrie sync_logs + last_sync_at)
 *      Body: account_id, new, updated, errors, status (success|partial|failed)
 *
 * Frontend-ul (import-emag.php) apelează action=page într-o buclă până când
 * has_more este false, apoi apelează action=finalize cu totalurile acumulate.
 */
require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/emag-api.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    json_response(['ok' => false, 'error' => 'Doar POST'], 405);
}
if (!csrf_check($_POST['csrf_token'] ?? null)) {
    json_response(['ok' => false, 'error' => 'CSRF invalid'], 403);
}

$action    = $_POST['action'] ?? 'page';
$accountId = (int)($_POST['account_id'] ?? 0);

if ($accountId <= 0) {
    json_response(['ok' => false, 'error' => 'account_id lipsește'], 422);
}

// Încarcă contul
$accountRows = db_select("SELECT * FROM emag_accounts WHERE id = ? AND is_active = 1", [$accountId]);
if (!$accountRows) {
    json_response(['ok' => false, 'error' => 'Cont inexistent sau inactiv'], 404);
}
$account = $accountRows[0];

// ============================================================
// ACTION: finalize
// ============================================================
if ($action === 'finalize') {
    $new     = max(0, (int)($_POST['new'] ?? 0));
    $updated = max(0, (int)($_POST['updated'] ?? 0));
    $errors  = max(0, (int)($_POST['errors'] ?? 0));
    $status  = in_array($_POST['status'] ?? '', ['success','partial','failed'], true)
               ? $_POST['status'] : 'success';
    $message = trim((string)($_POST['message'] ?? ''));

    try {
        db_exec(
            "INSERT INTO sync_logs (emag_account_id, products_new, products_updated, products_errors, status, message)
             VALUES (?,?,?,?,?,?)",
            [$accountId, $new, $updated, $errors, $status, $message ?: null]
        );
        db_exec("UPDATE emag_accounts SET last_sync_at = CURRENT_TIMESTAMP WHERE id = ?", [$accountId]);
        json_response(['ok' => true]);
    } catch (Throwable $e) {
        error_log('[sync/finalize] ' . $e->getMessage());
        json_response(['ok' => false, 'error' => $e->getMessage()], 500);
    }
}

// ============================================================
// ACTION: page (default)
// ============================================================
$page    = max(1, (int)($_POST['page'] ?? 1));
$perPage = EMAG_PAGE_SIZE;

// Throttle: max 3 req/sec pe resurse non-orders → sleep 350ms după prima pagină.
// (Pagina 1 nu așteaptă pentru a porni instant; paginile următoare așteaptă.)
if ($page > 1) {
    usleep(350000);
}

try {
    $api  = new EmagAPI($account['username'], $account['password'], $account['platform'] ?? 'ro');
    $resp = $api->getProducts($page, $perPage);
} catch (Throwable $e) {
    error_log('[sync/page] ' . $e->getMessage());
    json_response(['ok' => false, 'error' => $e->getMessage()], 502);
}

$stats = ['new' => 0, 'updated' => 0, 'errors' => 0, 'excluded' => 0, 'excluded_category' => 0, 'name_fixed' => 0, 'hotness' => 0];
$errorMessages = [];
$touchedPnks   = []; // PNK-uri atinse în această pagină (pentru reconcilierea numelor)
$processedEans = []; // EAN-uri atinse în această pagină (pentru hotness/find_by_eans)

// Set rapid pentru lookup: EAN excluse (global)
$excludedRows = db_select("SELECT ean FROM excluded_eans");
$excludedSet  = [];
foreach ($excludedRows as $r) {
    $excludedSet[$r['ean']] = true;
}

// Cache categorii pentru pagina curentă: emag_category_id => ['id' => N, 'is_excluded' => 0|1]
// Preîncărcăm toate categoriile existente.
$categoryCache = [];
foreach (db_select("SELECT id, emag_category_id, is_excluded FROM categories WHERE emag_category_id IS NOT NULL") as $c) {
    $categoryCache[(int)$c['emag_category_id']] = [
        'id'          => (int)$c['id'],
        'is_excluded' => (int)$c['is_excluded'],
    ];
}

/**
 * Asigură existența categoriei în DB; returnează ['id' => …, 'is_excluded' => …].
 * Folosește cache-ul local pentru a evita query-uri repetate.
 */
$ensureCategory = function (?int $emagCategoryId, ?string $name) use (&$categoryCache): ?array {
    if (!$emagCategoryId) return null;
    if (isset($categoryCache[$emagCategoryId])) return $categoryCache[$emagCategoryId];

    $effectiveName = $name ?: ('Categorie ' . $emagCategoryId);
    db_exec(
        "INSERT INTO categories (emag_category_id, name) VALUES (?, ?)",
        [$emagCategoryId, $effectiveName]
    );
    $id = (int)db_scalar("SELECT id FROM categories WHERE emag_category_id = ?", [$emagCategoryId]);
    $categoryCache[$emagCategoryId] = ['id' => $id, 'is_excluded' => 0];
    return $categoryCache[$emagCategoryId];
};

foreach ($resp['results'] as $item) {
    try {
        // Mapare câmpuri API → DB
        $emagId         = isset($item['id']) ? (string)$item['id'] : null;
        $pnk            = $item['part_number_key'] ?? null;
        $eanRaw         = $item['ean'] ?? null;
        $ean            = is_array($eanRaw) ? ($eanRaw[0] ?? null) : ($eanRaw ?: null);
        $name           = trim((string)($item['name'] ?? ''));
        $partNumber     = $item['part_number'] ?? null;
        $emagCategoryId = isset($item['category_id']) ? (int)$item['category_id'] : null;
        $categoryName   = $item['category_name'] ?? null;
        // Preț curent raportat de eMAG (sale_price = fără TVA) + moneda
        $salePriceRaw   = $item['sale_price'] ?? null;
        $salePrice      = ($salePriceRaw !== null && $salePriceRaw !== '') ? (float)$salePriceRaw : null;
        $currency       = $item['currency'] ?? null;
        // Intervalul de preț permis (docs: „Sale price will be validated against
        // min_sale_price and max_sale_price. Any offer that is not within the
        // specified range will be rejected."). Read-only aici — se setează pe eMAG.
        $minSalePrice   = (isset($item['min_sale_price']) && $item['min_sale_price'] !== '') ? (float)$item['min_sale_price'] : null;
        $maxSalePrice   = (isset($item['max_sale_price']) && $item['max_sale_price'] !== '') ? (float)$item['max_sale_price'] : null;
        // Stoc curent raportat de eMAG. `stock` vine de regulă ca listă
        // [{warehouse_id, value}]; uneori ca număr simplu sau prin general_stock.
        $currentStock = null;
        $stockRaw     = $item['stock'] ?? null;
        if (is_array($stockRaw)) {
            $sum = 0; $found = false;
            foreach ($stockRaw as $s) {
                if (is_array($s) && isset($s['value']) && is_numeric($s['value'])) {
                    $sum += (int)$s['value']; $found = true;
                }
            }
            if ($found) $currentStock = $sum;
        } elseif (is_numeric($stockRaw)) {
            $currentStock = (int)$stockRaw;
        }
        if ($currentStock === null && isset($item['general_stock']) && is_numeric($item['general_stock'])) {
            $currentStock = (int)$item['general_stock'];
        }
        // Status ofertă eMAG: 1 = activ, 0 = inactiv, 2 = end of life
        $offerStatus    = isset($item['status']) && $item['status'] !== '' ? (int)$item['status'] : null;
        // Ownership documentație. ATENȚIE: deși docs zice integer 1/2, API-ul
        // întoarce în practică `true` (boolean) când ești owner și `""` (string
        // gol) când NU ești (ofertă atașată / FBE). Normalizăm la 1 = owner,
        // 2 = atașat. Cheia absentă cu totul → null (necunoscut).
        $ownership = null;
        if (array_key_exists('ownership', $item)) {
            $ov = $item['ownership'];
            $isOwner = ($ov === true) || ($ov === 1) || ($ov === '1');
            $ownership = $isOwner ? 1 : 2;
        }
        // Metrici de competiție (din product_offer/read)
        $buyButtonRank = isset($item['buy_button_rank']) && $item['buy_button_rank'] !== ''
                         ? (int)$item['buy_button_rank'] : null;
        $numberOfOffers = isset($item['number_of_offers']) && $item['number_of_offers'] !== ''
                          ? (int)$item['number_of_offers'] : null;
        // Status validare documentație. 11 = „Actualizare în validare" — statusul
        // primit de un produs aprobat după ce i-ai editat documentația (ex. descrierea).
        // Cheia e o LISTĂ (un produs poate avea mai multe statusuri simultan); aici,
        // fără filtru, luăm statusul principal după prioritate + reținem lista completă.
        // Lista valorilor în EmagAPI::VALIDATION_STATUSES; pagina dedicată: validare.php.
        $vs = EmagAPI::parseValidationStatus($item['validation_status'] ?? null);
        // text fallback pentru afișare rapidă (denormalizat)
        $categoryText   = $categoryName ?: ($emagCategoryId ? ('ID ' . $emagCategoryId) : null);

        // 1. Skip după EAN exclus
        $allEans = is_array($eanRaw) ? $eanRaw : ($eanRaw ? [$eanRaw] : []);
        foreach ($allEans as $candidateEan) {
            if (isset($excludedSet[$candidateEan])) {
                $stats['excluded']++;
                continue 2;
            }
        }

        // 2. Asigură categoria + skip dacă e excluded la nivel de categorie
        $category = $ensureCategory($emagCategoryId, $categoryName);
        if ($category && $category['is_excluded']) {
            $stats['excluded_category']++;
            continue;
        }
        $categoryDbId = $category['id'] ?? null;

        if ($name === '') {
            $stats['errors']++;
            $errorMessages[] = "Produs fără nume (emag_id=$emagId)";
            continue;
        }

        // Match: 1) după emag_id  2) după EAN  3) după PNK
        $existing = null;
        if ($emagId) {
            $existing = db_select(
                "SELECT id FROM products WHERE emag_id = ? AND emag_account_id = ? LIMIT 1",
                [$emagId, $accountId]
            );
        }
        if (!$existing && $ean) {
            $existing = db_select(
                "SELECT id FROM products WHERE ean = ? AND emag_account_id = ? LIMIT 1",
                [$ean, $accountId]
            );
        }
        if (!$existing && $pnk) {
            $existing = db_select(
                "SELECT id FROM products WHERE pnk = ? AND emag_account_id = ? LIMIT 1",
                [$pnk, $accountId]
            );
        }

        if ($existing) {
            db_exec(
                "UPDATE products
                 SET ean = COALESCE(?, ean),
                     product_name = ?,
                     pnk = COALESCE(?, pnk),
                     emag_id = COALESCE(?, emag_id),
                     emag_part_number = COALESCE(?, emag_part_number),
                     category = COALESCE(?, category),
                     category_id = COALESCE(?, category_id),
                     current_sale_price = COALESCE(?, current_sale_price),
                     min_sale_price = COALESCE(?, min_sale_price),
                     max_sale_price = COALESCE(?, max_sale_price),
                     current_stock = COALESCE(?, current_stock),
                     currency = COALESCE(?, currency),
                     emag_offer_status = COALESCE(?, emag_offer_status),
                     emag_ownership = COALESCE(?, emag_ownership),
                     emag_buy_button_rank = ?,
                     emag_number_of_offers = ?,
                     status = 'synced'
                 WHERE id = ?",
                [$ean, $name, $pnk, $emagId, $partNumber, $categoryText, $categoryDbId, $salePrice, $minSalePrice, $maxSalePrice, $currentStock, $currency, $offerStatus, $ownership, $buyButtonRank, $numberOfOffers, $existing[0]['id']]
            );
            $productId = (int)$existing[0]['id'];
            $stats['updated']++;
        } else {
            $productId = db_exec(
                "INSERT INTO products
                  (emag_account_id, ean, product_name, pnk, emag_id, emag_part_number, category, category_id, current_sale_price, min_sale_price, max_sale_price, current_stock, currency, emag_offer_status, emag_ownership, emag_buy_button_rank, emag_number_of_offers, status)
                 VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?, 'synced')",
                [$accountId, $ean, $name, $pnk, $emagId, $partNumber, $categoryText, $categoryDbId, $salePrice, $minSalePrice, $maxSalePrice, $currentStock, $currency, $offerStatus, $ownership, $buyButtonRank, $numberOfOffers]
            );
            $stats['new']++;
        }

        // Statusul de validare îl scriem separat, DOAR dacă eMAG l-a întors în
        // acest răspuns — altfel un răspuns fără cheia `validation_status` ar
        // șterge snapshot-ul făcut din validare.php.
        if ($productId && $vs['value'] !== null) {
            db_exec(
                "UPDATE products
                    SET emag_validation_status = ?,
                        emag_validation_desc   = ?,
                        emag_validation_errors = ?,
                        emag_validation_all    = ?,
                        validation_checked_at  = CURRENT_TIMESTAMP
                  WHERE id = ?",
                [
                    $vs['value'],
                    $vs['description'],
                    $vs['errors'] ? json_encode($vs['errors'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
                    $vs['all'] ? implode(',', $vs['all']) : (string)$vs['value'],
                    $productId,
                ]
            );
        }

        // Reține PNK-ul atins pentru reconcilierea numelor de la owner (vezi mai jos).
        if ($pnk !== null && $pnk !== '') {
            $touchedPnks[$pnk] = true;
        }
        // Reține EAN-ul pentru extragerea hotness (find_by_eans) după pagină.
        if ($ean !== null && $ean !== '') {
            $processedEans[$ean] = true;
        }
    } catch (Throwable $e) {
        $stats['errors']++;
        $errorMessages[] = $e->getMessage();
        error_log('[sync/upsert] ' . $e->getMessage());
    }
}

// ============================================================
// Reconciliere nume pentru oferte atașate (FBE)
// ------------------------------------------------------------
// Pentru produsele atașate la un produs de catalog existent (ownership = 2,
// tipic FBE), eMAG NU întoarce prin product_offer/read numele de catalog
// afișat pe site, ci doar valoarea trimisă de seller (deseori veche). Numele
// corect ESTE însă în aplicație, pe contul care deține documentația
// (ownership = 1), cu același part_number_key.
//
// Aici, pentru fiecare PNK atins în această pagină, copiem numele de la oferta
// owner (ownership=1) peste ofertele atașate (ownership=2) cu același PNK, dacă
// numele diferă. Nu atingem niciodată oferta owner.
foreach (array_keys($touchedPnks) as $pnk) {
    $ownerName = db_scalar(
        "SELECT product_name FROM products
         WHERE pnk = ? AND emag_ownership = 1
           AND product_name IS NOT NULL AND TRIM(product_name) != ''
         LIMIT 1",
        [$pnk]
    );
    if ($ownerName === null || $ownerName === '') {
        continue; // nu avem (încă) un cont owner sincronizat pentru acest PNK
    }
    $fixed = db_exec(
        "UPDATE products
         SET product_name = ?
         WHERE pnk = ? AND emag_ownership = 2 AND product_name != ?",
        [$ownerName, $pnk, $ownerName]
    );
    if ($fixed > 0) {
        $stats['name_fixed'] += $fixed;
    }
}

// ============================================================
// Performanță produs (hotness) — documentation/find_by_eans
// ------------------------------------------------------------
// hotness („Product performance": SUPER HOT / HOT / WARM / COLD / SUPER COLD)
// NU vine din product_offer/read, ci dintr-un endpoint separat indexat după EAN.
// Îl extragem o dată per pagină (max 100 EAN-uri/request — fix cât o pagină) și
// actualizăm TOATE produsele cu acel EAN (e atribut de catalog, comun pe conturi).
// Nefatal: dacă eșuează, sync-ul rămâne reușit.
$eanList = array_keys($processedEans);
if ($eanList) {
    try {
        $hotMap = $api->getHotnessByEans($eanList);
        foreach ($hotMap as $hEan => $hotness) {
            $n = db_exec(
                "UPDATE products SET hotness = ? WHERE ean = ?",
                [$hotness, (string)$hEan]
            );
            if ($n > 0) $stats['hotness']++;
        }
    } catch (Throwable $e) {
        error_log('[sync/hotness] ' . $e->getMessage());
    }
}

$processed = count($resp['results']);
$hasMore   = $processed >= $perPage; // dacă pagina e plină, mai sunt probabil

json_response([
    'ok'           => true,
    'page'         => $page,
    'processed'    => $processed,
    'stats'        => $stats,
    'has_more'     => $hasMore,
    'next_page'    => $page + 1,
    'total_known'  => $resp['total'] ?? null,
    'errors_log'   => $errorMessages,
]);
