<?php
if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Centralized rule validations for reuse (UI saves and Import pipeline)
 */
class KNSDD_Rules_Validator {

    /**
     * Validate intra-group conflicts on a given list of sanitized rules.
     * Returns a structured report with errors/warnings and precomputed sets.
     *
     * @param array $rules Sanitized rules
     * @param string $context 'ui' or 'import'
     * @param array $extra_data Additional context data
     * @return array { ok: bool, errors: array[], warnings: array[], sets: array [category_only, product_ids, category_only_ranges] }
     */
    public static function validate_intra_group( $rules, $context = 'ui', $extra_data = [] ) {
        $rules = is_array($rules) ? $rules : [];

        $parse_range = function($min, $max) {
            $mn = is_numeric($min) ? floatval($min) : 0.0;
            $mx = is_numeric($max) ? floatval($max) : 0.0;
            if ($mn < 0) { $mn = 0.0; }
            if ($mx < 0) { $mx = 0.0; }
            $mx = ($mx == 0.0) ? INF : $mx; // 0 = infinity
            return [ 'min' => $mn, 'max' => $mx ];
        };
        $ranges_overlap_nums = function($A, $B) { return ($A['min'] <= $B['max']) && ($B['min'] <= $A['max']); };

        // Collect rows for range overlap validations WITH customer data
        $range_category_rows = [];
        $range_product_rows  = [];
        $indexed_rules = []; // Store full rule data by index
        
        foreach ((array)$rules as $idx => $r) {
            $type = isset($r['rule_type']) ? $r['rule_type'] : 'standard_discount';
            if ($type === 'special_campaign' && isset($r['campaign_type']) && $r['campaign_type'] === 'cart_total_discount') { continue; }
            
            // FIX: Kategori array veya scalar olabilir (AJAX handler array olarak normalize ediyor)
            $cat = 0;
            if (isset($r['category'])) {
                if (is_array($r['category'])) {
                    // Array ise ilk elementi al (çoklu kategori desteği için hazırlık)
                    $cat = !empty($r['category']) ? absint($r['category'][0]) : 0;
                } else {
                    // Scalar ise direkt al (backward compatibility)
                    $cat = absint($r['category']);
                }
            }
            $products = (isset($r['product']) && is_array($r['product'])) ? array_filter(array_map('absint', $r['product'])) : [];
            $rng = $parse_range( isset($r['min']) ? $r['min'] : 0, isset($r['max']) ? $r['max'] : 0 );
            
            $indexed_rules[$idx] = $r;
            
            if ($cat > 0 && empty($products)) { 
                $range_category_rows[] = [ 
                    'category' => $cat, 
                    'range' => $rng,
                    'rule_idx' => $idx
                ]; 
            }
            if (!empty($products)) { 
                $range_product_rows[] = [ 
                    'products' => array_values(array_unique($products)), 
                    'range' => $rng,
                    'rule_idx' => $idx
                ]; 
            }
        }

        $errors = [];
        $warnings = [];

        // Category-category: same category and range overlap WITH customer check
        $cntCat = count($range_category_rows);
        for ($i=0; $i<$cntCat; $i++) {
            for ($j=$i+1; $j<$cntCat; $j++) {
                $A = $range_category_rows[$i]; $B = $range_category_rows[$j];
                
                if ($A['category'] === $B['category'] && $ranges_overlap_nums($A['range'], $B['range'])) {
                    // Check customer conflict
                    $ruleA = $indexed_rules[$A['rule_idx']];
                    $ruleB = $indexed_rules[$B['rule_idx']];
                    
                    if (self::has_customer_conflict($ruleA, $ruleB)) {
                        $errors[] = [
                            'code' => 'intra_range_conflict_category',
                            'message' => __( 'Aynı kapsamda miktar aralığı çakışan kurallar var (kategori). Lütfen aralıkları ayrıştırın.', 'kns-dynamic-discounts-for-woocommerce' )
                        ];
                        return [ 'ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'sets' => [[], [], []] ];
                    }
                }
            }
        }

        // Product-product: intersecting product sets with overlapping ranges WITH customer check
        $cntPro = count($range_product_rows);
        for ($i=0; $i<$cntPro; $i++) {
            for ($j=$i+1; $j<$cntPro; $j++) {
                $A = $range_product_rows[$i]; $B = $range_product_rows[$j];
                $setA = array_flip($A['products']);
                $intersects = false;
                foreach ($B['products'] as $pid) { if (isset($setA[(int)$pid])) { $intersects = true; break; } }
                if ($intersects && $ranges_overlap_nums($A['range'], $B['range'])) {
                    // Check customer conflict
                    $ruleA = $indexed_rules[$A['rule_idx']];
                    $ruleB = $indexed_rules[$B['rule_idx']];
                    
                    if (self::has_customer_conflict($ruleA, $ruleB)) {
                        $errors[] = [
                            'code' => 'intra_range_conflict_product',
                            'message' => __( 'Aynı kapsamda miktar aralığı çakışan kurallar var (ürün). Lütfen aralıkları ayrıştırın.', 'kns-dynamic-discounts-for-woocommerce' )
                        ];
                        return [ 'ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'sets' => [[], [], []] ];
                    }
                }
            }
        }

        // Repeated product ranges (per single product) WITH customer check
        $seen_product_ranges = [];
        foreach ($range_product_rows as $entry) {
            foreach ($entry['products'] as $pid) {
                $k = (int)$pid;
                $arr = isset($seen_product_ranges[$k]) ? $seen_product_ranges[$k] : [];
                foreach ($arr as $prevEntry) {
                    if ($ranges_overlap_nums($prevEntry['range'], $entry['range'])) {
                        // Check customer conflict
                        $ruleA = $indexed_rules[$prevEntry['rule_idx']];
                        $ruleB = $indexed_rules[$entry['rule_idx']];
                        
                        if (self::has_customer_conflict($ruleA, $ruleB)) {
                            $errors[] = [
                                'code' => 'repeat_overlap_product_ranges',
                                'message' => __( 'Aynı ürün için miktar aralıkları çakışan birden fazla kural var. Lütfen aralıkları ayrıştırın.', 'kns-dynamic-discounts-for-woocommerce' )
                            ];
                            return [ 'ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'sets' => [[], [], []] ];
                        }
                    }
                }
                $arr[] = [ 'range' => $entry['range'], 'rule_idx' => $entry['rule_idx'] ];
                $seen_product_ranges[$k] = $arr;
            }
        }

        // Build sets for further checks and for callers
        $my_category_only = [];
        $my_product_ids   = [];
        $my_category_only_ranges = [];

        $is_effective_rule = function($r) {
            if (empty($r) || !is_array($r)) return false;
            $type = isset($r['rule_type']) ? $r['rule_type'] : 'standard_discount';
            if ($type === 'special_campaign' && isset($r['campaign_type']) && $r['campaign_type'] === 'cart_total_discount') return false;
            $cat = isset($r['category']) ? absint($r['category']) : 0;
            $products = (isset($r['product']) && is_array($r['product'])) ? array_filter(array_map('absint', $r['product'])) : [];
            return ($cat > 0) || (!empty($products));
        };

        foreach ((array)$rules as $r) {
            if (!$is_effective_rule($r)) { continue; }
            $cat = isset($r['category']) ? absint($r['category']) : 0;
            $products = (isset($r['product']) && is_array($r['product'])) ? array_filter(array_map('absint', $r['product'])) : [];
            if ($cat > 0 && empty($products)) {
                $my_category_only[] = $cat;
                $rng = $parse_range( isset($r['min']) ? $r['min'] : 0, isset($r['max']) ? $r['max'] : 0 );
                if (!isset($my_category_only_ranges[$cat])) { $my_category_only_ranges[$cat] = []; }
                $my_category_only_ranges[$cat][] = $rng;
            }
            if (!empty($products)) { $my_product_ids = array_merge($my_product_ids, $products); }
        }
        $my_category_only = array_values(array_unique($my_category_only));
        $my_product_ids   = array_values(array_unique($my_product_ids));

        // Intra: product categories vs own category-only
        $product_to_cats = [];
        if (!empty($my_product_ids)) {
            $terms = wp_get_object_terms( $my_product_ids, 'product_cat', [ 'fields' => 'all_with_object_id' ] );
            if (! is_wp_error($terms)) {
                foreach ($terms as $t) {
                    $pid = isset($t->object_id) ? (int)$t->object_id : 0;
                    $tid = isset($t->term_id) ? (int)$t->term_id : 0;
                    if ($pid && $tid) {
                        if (!isset($product_to_cats[$pid])) $product_to_cats[$pid] = [];
                        $product_to_cats[$pid][] = $tid;
                    }
                }
            }
            foreach ($my_product_ids as $pid) { if (!isset($product_to_cats[$pid])) $product_to_cats[$pid] = []; }
        }

        $my_cat_set = array_flip($my_category_only);
        
        // Kategori vs Ürün çakışması kontrolü WITH range AND customer check
        foreach ($my_product_ids as $pid) {
            $cats = isset($product_to_cats[$pid]) ? (array)$product_to_cats[$pid] : [];
            foreach ($cats as $c) { 
                if (isset($my_cat_set[(int)$c])) {
                    // Bu ürün için kural bul
                    $product_rule = null;
                    foreach ((array)$rules as $r) {
                        if (isset($r['product']) && is_array($r['product']) && in_array($pid, $r['product'])) {
                            $product_rule = $r;
                            break;
                        }
                    }
                    
                    // Bu kategori için kural bul
                    $category_rule = null;
                    foreach ((array)$rules as $r) {
                        if (isset($r['category']) && $r['category'] == $c && empty($r['product'])) {
                            $category_rule = $r;
                            break;
                        }
                    }
                    
                    // Her iki kural da bulunduysa aralık ve müşteri çakışması kontrol et
                    if ($product_rule && $category_rule) {
                        // Aralık kontrolü
                        $product_range = $parse_range(
                            isset($product_rule['min']) ? $product_rule['min'] : 0,
                            isset($product_rule['max']) ? $product_rule['max'] : 0
                        );
                        $category_range = $parse_range(
                            isset($category_rule['min']) ? $category_rule['min'] : 0,
                            isset($category_rule['max']) ? $category_rule['max'] : 0
                        );
                        
                        // Aralıklar kesişiyorsa ve müşteri çakışması varsa hata
                        if ($ranges_overlap_nums($product_range, $category_range)) {
                            if (self::has_customer_conflict($product_rule, $category_rule)) {
                                $errors[] = [
                                    'code' => 'intra_scope_conflict_product_vs_category',
                                    'message' => _x( 'Bu grupta aynı kategori için hem kategori düzeyinde hem de bu kategorideki bir ürüne yönelik kural tanımlı. Lütfen çakışmayı giderin.', 'validator error message', 'kns-dynamic-discounts-for-woocommerce' )
                                ];
                                return [ 'ok' => false, 'errors' => $errors, 'warnings' => $warnings, 'sets' => [[], [], []] ];
                            }
                        }
                    }
                }
            }
        }

        return [ 'ok' => true, 'errors' => $errors, 'warnings' => $warnings, 'sets' => [ $my_category_only, $my_product_ids, $my_category_only_ranges ] ];
    }

    /**
     * Check if two rules have customer-based conflict
     * Uses subset logic: specific customers ⊂ all members ⊂ everyone
     * 
     * @param array $rule1 First rule
     * @param array $rule2 Second rule
     * @return bool True = conflict exists (customer sets intersect), False = no conflict
     */
    private static function has_customer_conflict($rule1, $rule2) {
        $login1 = isset($rule1['login_required']) && $rule1['login_required'];
        $login2 = isset($rule2['login_required']) && $rule2['login_required'];
        $first1 = isset($rule1['first_order_only']) && $rule1['first_order_only'];
        $first2 = isset($rule2['first_order_only']) && $rule2['first_order_only'];
        $customers1 = isset($rule1['allowed_customers']) && is_array($rule1['allowed_customers']) ? $rule1['allowed_customers'] : [];
        $customers2 = isset($rule2['allowed_customers']) && is_array($rule2['allowed_customers']) ? $rule2['allowed_customers'] : [];

        // first_order_only creates disjoint sets (mutually exclusive)
        // first_order ∩ non_first_order = ∅
        if (($first1 && !$first2) || (!$first1 && $first2)) {
            return false; // No intersection
        }

        // Both have specific customers
        if (!empty($customers1) && !empty($customers2)) {
            $common = array_intersect($customers1, $customers2);
            if (empty($common)) {
                return false; // No common customers = no intersection
            }
            // Has common customers = intersection exists = conflict
            return true;
        }

        // One has specific customers, other doesn't
        // Specific customers ⊂ All members ⊂ Everyone
        // If one is specific and other is broader, intersection exists = conflict
        if (!empty($customers1) || !empty($customers2)) {
            // Specific customers are always a subset of members/everyone
            // Therefore intersection exists = conflict
            return true;
        }

        // Neither has specific customers
        // Check login_required (members vs everyone)
        if ($login1 !== $login2) {
            // One is "members only", other is "everyone"
            // Members ⊂ Everyone, intersection exists = conflict
            return true;
        }

        // Both have same login requirement (both members or both everyone)
        // Intersection exists = conflict
        return true;
    }
}
