<?php

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

class AIEO_Exchanger_Feed_Builder {

    
    public static function build($feed_id) {
        $feed = AIEO_Exchanger_Feeds::get((int) $feed_id);
        if (!$feed) {
            return array('ok' => false, 'error' => 'feed_not_found', 'products' => 0, 'shards' => 0);
        }

        @set_time_limit(300); 
        $started_at = current_time('mysql', true);

        try {
            $product_ids = self::query_product_ids($feed);
        } catch (\Throwable $e) {
            AIEO_Exchanger_Feeds::record_build_result($feed['id'], 'query_error');
            AIEO_Exchanger_Run_Log::record('source', (int) $feed['id'], 0, 0, 0, 0, 0, array('phase' => 'query', 'error' => $e->getMessage()));
            return array('ok' => false, 'error' => $e->getMessage(), 'products' => 0, 'shards' => 0);
        }

        $shard_size = max(50, (int) ($feed['shard_size'] ?? 500));
        $chunks     = array_chunk($product_ids, $shard_size);
        $shards_meta = array();
        $total_serialized = 0;

        $base_path = AIEO_Exchanger_Feeds::storage_path($feed);
        $shards_dir = $base_path . '/shards';
        if (!file_exists($shards_dir)) { wp_mkdir_p($shards_dir); }

        
        foreach (glob($shards_dir . '/*.json') ?: array() as $old) { @wp_delete_file($old); }

        foreach ($chunks as $i => $ids) {
            $shard_n = $i + 1;
            $products = array();
            foreach ($ids as $pid) {
                $payload = AIEO_Exchanger_Product_Serializer::serialize((int) $pid);
                if ($payload) {
                    $products[] = $payload;
                    $total_serialized++;
                }
            }
            $shard_data = array(
                'shard'          => $shard_n,
                'product_count'  => count($products),
                'products'       => $products,
            );
            $shard_path = $shards_dir . '/shard-' . $shard_n . '.json';
            file_put_contents($shard_path, wp_json_encode($shard_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

            $shards_meta[] = array(
                'shard'   => $shard_n,
                'count'   => count($products),
                'rest'    => rest_url('aieo/v1/exchanger/' . rawurlencode($feed['slug']) . '/shards/' . $shard_n),
            );
        }

        $manifest = array(
            'exchanger_version' => AIEO_Exchanger_Product_Serializer::VERSION,
            'feed_slug'         => $feed['slug'],
            'feed_label'        => $feed['label'],
            'source_site_url'   => home_url(),
            'built_at'          => $started_at,
            'product_count'     => $total_serialized,
            'shard_count'       => count($chunks),
            'shard_size'        => $shard_size,
            'shards'            => $shards_meta,
        );
        file_put_contents($base_path . '/manifest.json', wp_json_encode($manifest, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));

        AIEO_Exchanger_Feeds::record_build_result($feed['id'], 'OK');
        AIEO_Exchanger_Run_Log::record('source', (int) $feed['id'], 0, $total_serialized, 0, 0, 0);

        return array(
            'ok'            => true,
            'products'      => $total_serialized,
            'shards'        => count($chunks),
            'manifest_path' => $base_path . '/manifest.json',
            'manifest_url'  => AIEO_Exchanger_Feeds::manifest_url($feed),
        );
    }

    
    public static function query_product_ids(array $feed) {
        global $wpdb;

        $op = strtoupper((string) ($feed['rule_operator'] ?? 'AND')) === 'OR' ? 'OR' : 'AND';

        $brand_ids    = (array) ($feed['brand_term_ids']    ?? array());
        $category_ids = (array) ($feed['category_term_ids'] ?? array());
        $need_ids     = (array) ($feed['need_term_ids']     ?? array());
        $direct_ids   = (array) ($feed['product_ids']       ?? array());

        $excl_brand_ids    = (array) ($feed['excluded_brand_ids']    ?? array());
        $excl_cat_ids      = (array) ($feed['excluded_category_ids'] ?? array());
        $excl_need_ids     = (array) ($feed['excluded_need_ids']     ?? array());
        $excl_product_ids  = (array) ($feed['excluded_product_ids']  ?? array());

        $tax_query = array('relation' => $op);
        if (!empty($brand_ids))    { $tax_query[] = array('taxonomy' => 'product_brand', 'field' => 'term_id', 'terms' => $brand_ids,    'operator' => 'IN'); }
        if (!empty($category_ids)) { $tax_query[] = array('taxonomy' => 'product_cat',   'field' => 'term_id', 'terms' => $category_ids, 'operator' => 'IN'); }
        if (!empty($need_ids))     { $tax_query[] = array('taxonomy' => 'pa_need',       'field' => 'term_id', 'terms' => $need_ids,     'operator' => 'IN'); }
        
        
        if (count($tax_query) === 1) { $tax_query = array(); }

        
        
        $excl_tax = array();
        if (!empty($excl_brand_ids)) { $excl_tax[] = array('taxonomy' => 'product_brand', 'field' => 'term_id', 'terms' => $excl_brand_ids, 'operator' => 'NOT IN'); }
        if (!empty($excl_cat_ids))   { $excl_tax[] = array('taxonomy' => 'product_cat',   'field' => 'term_id', 'terms' => $excl_cat_ids,   'operator' => 'NOT IN'); }
        if (!empty($excl_need_ids))  { $excl_tax[] = array('taxonomy' => 'pa_need',       'field' => 'term_id', 'terms' => $excl_need_ids,  'operator' => 'NOT IN'); }
        if (!empty($excl_tax)) {
            if (empty($tax_query)) { $tax_query = array('relation' => 'AND'); }
            elseif ($op === 'OR')  { $tax_query = array('relation' => 'AND', $tax_query); }
            foreach ($excl_tax as $ex) { $tax_query[] = $ex; }
        }

        $args = array(
            'post_type'      => 'product',
            'post_status'    => 'publish',
            'fields'         => 'ids',
            'posts_per_page' => -1,
            'orderby'        => 'ID',
            'order'          => 'ASC',
            'no_found_rows'  => true,
        );
        if (!empty($tax_query)) { $args['tax_query'] = $tax_query; }

        
        $date_query = array();
        if (!empty($feed['date_from'])) { $date_query['after']     = (string) $feed['date_from']; }
        if (!empty($feed['date_to']))   { $date_query['before']    = (string) $feed['date_to'];   }
        if (!empty($date_query))        { $date_query['inclusive'] = true; $args['date_query'] = array($date_query); }

        
        
        if (!empty($direct_ids)) {
            if ($op === 'AND' && !empty($tax_query)) {
                $args['post__in'] = $direct_ids;
            } elseif ($op === 'OR' && empty($tax_query)) {
                $args['post__in'] = $direct_ids;
            }
            
        }

        $q = new WP_Query($args);
        $ids = $q->posts;

        if ($op === 'OR' && !empty($direct_ids) && !empty($tax_query)) {
            $ids = array_values(array_unique(array_merge($ids, $direct_ids)));
        }

        
        
        
        $min = isset($feed['price_min']) && $feed['price_min'] !== null ? (float) $feed['price_min'] : null;
        $max = isset($feed['price_max']) && $feed['price_max'] !== null ? (float) $feed['price_max'] : null;
        if (($min !== null || $max !== null) && !empty($ids)) {
            $placeholders = implode(',', array_fill(0, count($ids), '%d'));
            
            $sql = "SELECT post_id FROM {$wpdb->postmeta}
                    WHERE meta_key = '_price'
                      AND post_id IN ($placeholders)";
            $params = $ids;
            if ($min !== null) { $sql .= ' AND CAST(meta_value AS DECIMAL(10,2)) >= %f'; $params[] = $min; }
            if ($max !== null) { $sql .= ' AND CAST(meta_value AS DECIMAL(10,2)) <= %f'; $params[] = $max; }
            $kept = $wpdb->get_col($wpdb->prepare($sql, $params));
            $ids = array_intersect(array_map('intval', $ids), array_map('intval', (array) $kept));
        }

        
        if (!empty($excl_product_ids)) {
            $ids = array_diff(array_map('intval', $ids), array_map('intval', $excl_product_ids));
        }

        sort($ids);
        return array_values(array_map('intval', $ids));
    }
}
