<?php

namespace auctionforge\inc\sync;

use auctionforge\inc\abs\Importer;
use auctionforge\inc\controllers\MultiLanguageSupport;
use auctionforge\inc\controllers\PolylangSupport;
use auctionforge\inc\controllers\CachePurge;
use auctionforge\inc\Debug;
use auctionforge\inc\helpers\SettingsHelper;
use auctionforge\inc\model\Auction;
use auctionforge\inc\import\InsertLotsBS;
use auctionforge\inc\model\Lot;
use auctionforge\inc\InstanceDB;

class ItemUpdateWorker
{
    /**
     * Process an item_update job.
     * Reuses the same logic as AuctionsAPI::itemsUpdated() but runs from the queue.
     *
     * @param object $job  The queue job row from af_sync_queue
     * @return array  ['updated' => bool, 'errors' => array]
     */
    public static function process($job)
    {
        $auctionId = $job->auction_id;
        $startTime = microtime(true);
        $result = ['updated' => false, 'errors' => []];

        SyncLogger::log('worker', "▶ Starting item_update for auction {$auctionId}", 'info', $auctionId, $job->id);

        try {
            // Stage 1: Parse payload
            SyncLogger::log('worker', "  [1/4] Parsing webhook payload...", 'info', $auctionId, $job->id);
            $payload = json_decode($job->payload);
            if (empty($payload) || empty($payload->itemsByLanguage)) {
                SyncLogger::log('worker', "  [1/4] No itemsByLanguage in payload — skipping", 'warning', $auctionId, $job->id);
                return $result;
            }

            // Stage 2: Extract item data from languages
            $langCode = SettingsHelper::getOption("lang", 'en');
            SyncLogger::log('worker', "  [2/4] Extracting item data (lang: {$langCode})...", 'info', $auctionId, $job->id);
            $response = $payload->itemsByLanguage;
            $item = new \stdClass();
            $itemSecond = new \stdClass();

            if (PolylangSupport::isPolylangActive() && PolylangSupport::isFullSupport()) {
                $secondLangCode = MultiLanguageSupport::getSecondLangCode();
                if (!empty($response->{$secondLangCode})) {
                    $secondObj = (array) $response->{$secondLangCode};
                    $itemSecond = $secondObj[0]->item;
                    SyncLogger::log('worker', "  [2/4] Second language ({$secondLangCode}) data loaded", 'info', $auctionId, $job->id);
                }
            }

            if (!empty($response->{$langCode})) {
                $obj = (array) $response->{$langCode};
                $item = $obj[0]->item;

                if (!empty($obj[0]->leadingBid)) {
                    if (isset($obj[0]->leadingBid->price)) {
                        $item->leadingBid = $obj[0]->leadingBid->price;
                    }
                }

                if (!empty($obj[0]->soldBid)) {
                    if (isset($obj[0]->soldBid->price)) {
                        $item->soldBid = $obj[0]->soldBid->price;
                    }
                }

                if (!empty($obj[0]->paddedIndex)) {
                    $item->paddedIndex = $obj[0]->paddedIndex;
                }
            }

            if (is_null($item) || empty($item->id)) {
                SyncLogger::log('worker', "  [2/4] Item data is null/empty — skipping", 'warning', $auctionId, $job->id);
                return $result;
            }

            $itemName = $item->name ?? 'unnamed';
            $bidInfo = '';
            if (!empty($item->leadingBid)) $bidInfo .= " leading={$item->leadingBid}";
            if (!empty($item->soldBid)) $bidInfo .= " sold={$item->soldBid}";
            SyncLogger::log('worker', "  [2/4] Item #{$item->id}: \"{$itemName}\"{$bidInfo}", 'info', $auctionId, $job->id);

            // Stage 3: Build attributes + save
            SyncLogger::log('worker', "  [3/4] Building lot attributes and saving to WordPress...", 'info', $auctionId, $job->id);
            $item->lotId = $item->id;
            $item->auctionSlug = $item->auctionId ?? $auctionId;
            $item->syncTimes = 0;
            $item->priority = 1;
            $item->createDatetime = date(Importer::DATE_FORMAT);

            if (isset($itemSecond->name)) {
                $item->nameL2 = $itemSecond->name;
            } else {
                $item->nameL2 = $item->name ?? '';
            }
            if (isset($itemSecond->description)) {
                $item->descriptionL2 = $itemSecond->description;
            } else {
                $item->descriptionL2 = $item->description ?? '';
            }
            if (isset($itemSecond->author)) {
                $item->authorL2 = $itemSecond->author;
            } else {
                $item->authorL2 = $item->author ?? '';
            }

            $lot = new Lot();
            $lot->loadAttributes($item);
            $lot->setPriority(1);
            $lots = [$lot];
            $saveResult = Lot::bulkSave($lots);

            if ($saveResult) {
                SyncLogger::log('worker', "  [3/5] Staged to import_history", 'info', $auctionId, $job->id);

                // Stage 4: Process staging → WP posts + bp_meta_data
                $db = InstanceDB::getInstanceDB();
                $db->query('COMMIT;');
                $db->query('SET autocommit = 1;');

                SyncLogger::log('worker', "  [4/5] Processing staged data → WP post + meta...", 'info', $auctionId, $job->id);
                $insertLots = new InsertLotsBS();
                $insertLots->init(['limit' => 10]);
                $insertInfo = $insertLots->getLoggedInfo();
                $batchCount = isset($insertInfo['updated lots']) ? (int) $insertInfo['updated lots'] : 0;

                $result['updated'] = true;
                // Lot::getId() holds the BIDSPIRIT lot id (loaded straight off the API
                // payload), NOT the WP post id — resolve the real post before purging.
                $bsLotId = $lot->getId();
                $postId  = self::resolveWpPostId($bsLotId, $auctionId, $langCode);
                SyncLogger::log('worker', "  [4/5] Processed {$batchCount} lot(s) into WP", 'info', $auctionId, $job->id);

                // Stage 5: Clear cache. Routed through CachePurge so EVERY configured layer
                // is invalidated (Cloudflare, nginx FastCGI, LiteSpeed, Hummingbird). This
                // previously called Hummingbird-only helpers, which are a no-op on any site
                // without that plugin — so webhook item updates never purged the page cache.
                $postLabel = $postId ? "post #{$postId}" : "lot #{$bsLotId} (post unresolved)";
                SyncLogger::log('worker', "  [5/5] Clearing cache for {$postLabel}...", 'info', $auctionId, $job->id);
                if ($postId) {
                    CachePurge::purgeLot($postId);
                }
                Auction::doClearCacheForAuctionPages();

                $duration = round(microtime(true) - $startTime, 2);
                SyncLogger::log('worker', "✓ Item updated: lot #{$item->lotId} \"{$itemName}\" → {$postLabel} ({$duration}s)", 'success', $auctionId, $job->id);
            } else {
                $duration = round(microtime(true) - $startTime, 2);
                SyncLogger::log('worker', "⚠ Lot::bulkSave returned false for lot #{$item->lotId} ({$duration}s)", 'warning', $auctionId, $job->id);
            }

        } catch (\Exception $e) {
            $result['errors'][] = $e->getMessage();
            $duration = round(microtime(true) - $startTime, 2);
            SyncLogger::log('worker', "✗ Exception in item_update after {$duration}s: " . $e->getMessage(), 'error', $auctionId, $job->id);
        } catch (\Throwable $t) {
            $result['errors'][] = $t->getMessage();
            $duration = round(microtime(true) - $startTime, 2);
            SyncLogger::log('worker', "✗ Fatal in item_update after {$duration}s: " . $t->getMessage(), 'error', $auctionId, $job->id);
        }

        return $result;
    }

    /**
     * Map a BidSpirit lot id back to its WordPress post id.
     *
     * Lot::getId() carries the BIDSPIRIT id (it is loaded straight off the API
     * payload), so it must never be used as a post id — doing so pointed the old
     * cache purge at a nonexistent permalink and made the "post #NNNNN" log line
     * wrong. bp_meta_data is the authoritative join: one row per (lot, language)
     * holding both ids.
     *
     * @param  int|string $bsLotId   BidSpirit lot id
     * @param  int|string $auctionId BidSpirit auction id
     * @param  string     $langCode  e.g. 'en'
     * @return int  WP post id, or 0 when it cannot be resolved.
     */
    private static function resolveWpPostId($bsLotId, $auctionId, $langCode)
    {
        global $wpdb;

        $bsLotId = (int) $bsLotId;
        if ($bsLotId <= 0 || empty($wpdb)) {
            return 0;
        }
        $table = $wpdb->prefix . 'bp_meta_data';

        // Narrowest match first, then widen: single-language sites and rows written
        // before auctionId was populated still resolve.
        $attempts = array(
            array('id = %d AND auctionId = %s AND lang = %s', array($bsLotId, (string) $auctionId, (string) $langCode)),
            array('id = %d AND auctionId = %s',               array($bsLotId, (string) $auctionId)),
            array('id = %d',                                  array($bsLotId)),
        );

        foreach ($attempts as $attempt) {
            $postId = (int) $wpdb->get_var(
                $wpdb->prepare(
                    'SELECT postId FROM ' . $table . ' WHERE ' . $attempt[0] . ' ORDER BY postId DESC LIMIT 1',
                    $attempt[1]
                )
            );
            if ($postId > 0 && get_post_status($postId) !== false) {
                return $postId;
            }
        }

        return 0;
    }
}
