<?php
/**
 * Per-user, per-dimension interest aggregator.
 *
 * Replaces the "ship every pageview to wpdeploy" volume bomb with running
 * tallies of "how many times has alice@x.com engaged with [keyword=victorian]"
 * across views/favourites/bids/wins. The tallies are bounded by users ×
 * distinct interests (typically a few dozen rows per user) — small enough
 * to sync hourly without flooding wpdeploy.
 *
 * Dimensions:
 *   keyword     -- tokens extracted from title + tags + author
 *   category    -- auction_tag taxonomy term_id
 *   author      -- slugified maker/artist name
 *   price_band  -- bucketed estimate (under_50, 50_200, …)
 */

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

require_once __DIR__ . '/class-keyword-extractor.php';

class AF_User_Interests {

    const T_INTERESTS = 'af_user_interests';
    const T_RECENT    = 'af_user_recent_activity';

    const RECENT_CAP_PER_USER = 50;

    public static function record_view($email, $post_id) {
        return self::record_event($email, $post_id, 'view', null);
    }

    public static function record_favorite($email, $post_id) {
        return self::record_event($email, $post_id, 'favorite', null);
    }

    public static function record_bid($email, $post_id, $amount = null) {
        return self::record_event($email, $post_id, 'bid', $amount);
    }

    public static function record_win($email, $post_id, $hammer_price = null) {
        return self::record_event($email, $post_id, 'win', $hammer_price);
    }

    /**
     * Single internal entry point. All four record_* helpers funnel here.
     */
    private static function record_event($email, $post_id, $type, $value) {
        if (empty($email) || empty($post_id)) return false;

        $email = strtolower(trim($email));
        if (!is_email($email)) return false;

        if (!self::tables_exist()) return false;

        // Skip admins / excluded roles / bots — using existing should_skip()
        // logic so admins browsing the catalogue don't pollute marketing data.
        if (class_exists('AF_Stats_Tracker') && method_exists('AF_Stats_Tracker', 'should_skip')) {
            if (AF_Stats_Tracker::should_skip()) return false;
        }

        // Build the dimension bag for this lot.
        $dimensions = self::dimensions_for_post($post_id);

        $col = self::count_column_for($type);
        if (!$col) return false;

        $now = current_time('mysql', true);

        global $wpdb;
        $table = $wpdb->prefix . self::T_INTERESTS;

        foreach ($dimensions as $row) {
            $wpdb->query($wpdb->prepare(
                "INSERT INTO $table
                    (user_email, dimension, dimension_value, dimension_label,
                     view_count, fav_count, bid_count, win_count,
                     first_engaged, last_engaged)
                 VALUES (%s, %s, %s, %s, %d, %d, %d, %d, %s, %s)
                 ON DUPLICATE KEY UPDATE
                    $col = $col + 1,
                    dimension_label = VALUES(dimension_label),
                    last_engaged = VALUES(last_engaged)",
                $email,
                $row['dimension'],
                $row['value'],
                $row['label'],
                $type === 'view'     ? 1 : 0,
                $type === 'favorite' ? 1 : 0,
                $type === 'bid'      ? 1 : 0,
                $type === 'win'      ? 1 : 0,
                $now,
                $now
            ));
        }

        // Recent-activity log (capped per user).
        $recent = $wpdb->prefix . self::T_RECENT;
        $wpdb->query($wpdb->prepare(
            "INSERT IGNORE INTO $recent
                (user_email, activity_type, lot_post_id, term_id, value, occurred_at)
             VALUES (%s, %s, %d, %d, %s, %s)",
            $email,
            $type,
            $post_id,
            self::primary_term_id($post_id),
            $value === null ? null : (string) $value,
            $now
        ));

        // Trim to last RECENT_CAP_PER_USER rows for this user.
        $wpdb->query($wpdb->prepare(
            "DELETE FROM $recent
             WHERE user_email = %s
               AND id NOT IN (
                  SELECT id FROM (
                     SELECT id FROM $recent
                     WHERE user_email = %s
                     ORDER BY occurred_at DESC, id DESC
                     LIMIT %d
                  ) t
               )",
            $email, $email, self::RECENT_CAP_PER_USER
        ));

        return true;
    }

    /**
     * Build the dimension rows we should increment for a given lot.
     * Returns array of ['dimension' => ..., 'value' => ..., 'label' => ...].
     */
    private static function dimensions_for_post($post_id) {
        $out = array();

        // 1. Keywords (primary signal) — from title + tags + author.
        foreach (AF_Keyword_Extractor::get_for_post($post_id) as $kw => $source) {
            $out[] = array(
                'dimension' => 'keyword',
                'value'     => $kw,
                'label'     => $kw,
            );
        }

        // 2. Categories — auction_tag taxonomy terms.
        $terms = get_the_terms($post_id, 'auction_tag');
        if ($terms && !is_wp_error($terms)) {
            foreach ($terms as $term) {
                $out[] = array(
                    'dimension' => 'category',
                    'value'     => (string) $term->term_id,
                    'label'     => $term->name,
                );
            }
        }

        // 3. Author/maker — from BidSpirit meta.
        global $wpdb;
        $bp = $wpdb->prefix . 'bp_meta_data';
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT author, estimatedPriceInt FROM $bp WHERE postId = %d LIMIT 1",
            $post_id
        ));

        if ($row && !empty($row->author)) {
            $slug = sanitize_title($row->author);
            if (!empty($slug)) {
                $out[] = array(
                    'dimension' => 'author',
                    'value'     => $slug,
                    'label'     => trim($row->author),
                );
            }
        }

        // 4. Price band — bucket the estimate.
        if ($row && $row->estimatedPriceInt !== null && $row->estimatedPriceInt !== '') {
            $band = self::price_band((int) $row->estimatedPriceInt);
            $out[] = array(
                'dimension' => 'price_band',
                'value'     => $band,
                'label'     => $band,
            );
        }

        return $out;
    }

    private static function primary_term_id($post_id) {
        $terms = get_the_terms($post_id, 'auction_tag');
        if ($terms && !is_wp_error($terms)) {
            $first = reset($terms);
            return (int) $first->term_id;
        }
        return 0;
    }

    private static function price_band($amount) {
        if ($amount < 50)    return 'under_50';
        if ($amount < 200)   return '50_200';
        if ($amount < 1000)  return '200_1000';
        if ($amount < 5000)  return '1000_5000';
        return '5000_plus';
    }

    private static function count_column_for($type) {
        switch ($type) {
            case 'view':     return 'view_count';
            case 'favorite': return 'fav_count';
            case 'bid':      return 'bid_count';
            case 'win':      return 'win_count';
        }
        return null;
    }

    private static function tables_exist() {
        static $ok = null;
        if ($ok !== null) return $ok;
        global $wpdb;
        $i = $wpdb->prefix . self::T_INTERESTS;
        $r = $wpdb->prefix . self::T_RECENT;
        $ok = ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $i)) === $i)
           && ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $r)) === $r);
        return $ok;
    }
}
