<?php
/**
 * sync-lots (every 10 minutes)
 *
 * Mirrors each lot's title/category/author/keywords/image to wpdeploy so the
 * people view can render thumbnails and segment filters can join on category.
 * Watermarked on wp_bp_meta_data.lastUpdate so each batch is a small delta.
 */

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

require_once __DIR__ . '/class-sync-base.php';
require_once dirname(__DIR__) . '/stats/class-keyword-extractor.php';

class AF_Lots_Sync extends AF_Sync_Base {

    protected $endpoint     = 'sync-lots';
    protected $cron_hook    = 'auctionforge_sync_lots';
    protected $cron_schedule = 'bp_ten_minutely';
    protected $batch_size   = 200;
    protected $health_key   = 'lots';

    protected function read_batch() {
        global $wpdb;
        $bp = $wpdb->prefix . 'bp_meta_data';

        if (!$this->table_exists($bp)) return array();

        $watermark = get_option('auctionforge_lots_sync_watermark', '2000-01-01 00:00:00');

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT m.postId, m.name, m.author, m.categoryId, m.tags,
                    m.estimatedPriceInt, m.imagesListStr, m.auctionId, m.lastUpdate
               FROM $bp m
              WHERE m.lastUpdate >= %s
              ORDER BY m.lastUpdate ASC
              LIMIT %d",
            $watermark, $this->batch_size
        ), ARRAY_A);

        return $rows;
    }

    protected function build_payload($rows) {
        $records = array();
        foreach ($rows as $r) {
            $post_id = (int) $r['postId'];

            // Pull cached keywords + auction_tag terms for richer payload.
            $kw_rows = AF_Keyword_Extractor::get_for_post($post_id);
            $keywords = array_keys($kw_rows);

            $term_ids = array();
            $terms = get_the_terms($post_id, 'auction_tag');
            if ($terms && !is_wp_error($terms)) {
                foreach ($terms as $t) $term_ids[] = (int) $t->term_id;
            }

            // First image, fully resolved to a CDN URL via the existing
            // LotImage helper. The bare filename (e.g. "001.jpg") is the
            // raw BidSpirit storage value and useless to wpdeploy on its
            // own — the browser would resolve it against the wpdeploy host
            // and 404. Use LABEL_MEDIUM (~400px) since this is for the
            // people / keyword corpus thumbnail previews.
            $image_url = '';
            if (!empty($r['imagesListStr'])
                && class_exists('\\auctionforge\\inc\\model\\LotImage', false) === false) {
                // Force-load the class only when we have an image to resolve.
                @include_once dirname(dirname(__DIR__)) . '/inc/model/LotImage.php';
            }
            if (!empty($r['imagesListStr']) && class_exists('\\auctionforge\\inc\\model\\LotImage')) {
                try {
                    $lotImage = new \auctionforge\inc\model\LotImage($post_id);
                    $images = $lotImage->getImageList();
                    if (!empty($images[0])) {
                        // Prefer MEDIUM thumbnail; fall back to ORIGINAL or any URL we can find.
                        if (!empty($images[0][\auctionforge\inc\abs\ImageModel::LABEL_MEDIUM])) {
                            $image_url = (string) $images[0][\auctionforge\inc\abs\ImageModel::LABEL_MEDIUM];
                        } elseif (!empty($images[0][\auctionforge\inc\abs\ImageModel::LABEL_ORIGINAL])) {
                            $image_url = (string) $images[0][\auctionforge\inc\abs\ImageModel::LABEL_ORIGINAL];
                        } else {
                            $first = reset($images[0]);
                            if (is_string($first)) $image_url = $first;
                        }
                    }
                } catch (\Throwable $t) {
                    $image_url = '';
                }
            }
            // If the helper produced nothing usable, leave the column empty
            // rather than ship a bare filename — the wpdeploy view skips
            // <img> rendering when image_url isn't a real http(s) URL.
            if ($image_url !== '' && !preg_match('#^https?://#i', $image_url)) {
                $image_url = '';
            }

            // Tags as JSON array (split commas, trim).
            $tags_arr = array();
            if (!empty($r['tags'])) {
                foreach (explode(',', $r['tags']) as $t) {
                    $t = trim($t);
                    if ($t !== '') $tags_arr[] = $t;
                }
            }

            $records[] = array(
                'post_id'        => $post_id,
                'title'          => (string) $r['name'],
                'author'         => (string) $r['author'],
                'category_id'    => $r['categoryId'] !== null ? (int) $r['categoryId'] : null,
                'term_ids_json'  => wp_json_encode($term_ids),
                'tags_json'      => wp_json_encode($tags_arr),
                'keywords_json'  => wp_json_encode($keywords),
                'est_price_int'  => $r['estimatedPriceInt'] !== null ? (int) $r['estimatedPriceInt'] : null,
                'image_url'      => $image_url,
                'auction_id'     => $r['auctionId'] !== null ? (int) $r['auctionId'] : null,
                'last_modified'  => self::epoch_to_datetime($r['lastUpdate']),
            );
        }

        return array('records' => $records);
    }

    protected function mark_synced($rows, $response) {
        if (empty($rows)) return;
        $latest = end($rows);
        if (!empty($latest['lastUpdate'])) {
            update_option('auctionforge_lots_sync_watermark', $latest['lastUpdate'], 'no');
        }
    }

    /**
     * `wp_bp_meta_data.lastUpdate` is stored as a Unix epoch string (e.g.
     * "1756919754"). wpdeploy's `lots.last_modified` is a DATETIME column
     * — MariaDB rejects a bare epoch with 22007. Normalise to UTC datetime.
     */
    private static function epoch_to_datetime($v) {
        if ($v === null || $v === '') return null;
        // Already datetime-shaped? leave alone.
        if (is_string($v) && preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $v)) {
            return $v;
        }
        $ts = is_numeric($v) ? (int) $v : strtotime((string) $v);
        if ($ts <= 0) return null;
        return gmdate('Y-m-d H:i:s', $ts);
    }
}
