Subversion Repositories SmartDukaan

Rev

Blame | Last modification | View Log | RSS feed

package com.smartdukaan.cron.offercircular;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.spice.profitmandi.dao.repository.offers.CircularIngestRepository;

/**
 * Human-confirmed exceptions from offers.product_alias, consulted BEFORE any matching.
 *
 * These exist because the circular and our catalog sometimes name the same product
 * differently - Apple ship "iPhone Air" while our catalog rows read "iPhone 17 Air" -
 * and because some products are genuinely not ours (no Pixel SKUs are stocked). A
 * confirmation recorded once keeps applying every following month, which is the whole
 * point: without it the same review queue regenerates identically.
 */
public final class ProductAliases {

    private static final Pattern VARIANT_SPEC = Pattern.compile(
            "\\(?\\s*\\d+\\s*[+/]\\s*\\d+\\s*(?:GB|TB|G|T)?\\s*\\)?|\\d+\\s*(?:GB|TB)",
            Pattern.CASE_INSENSITIVE);

    public static final class Alias {
        private final String action;
        private final String canonicalText;
        private final Integer catalogId;
        private final Integer superCatalogId;

        Alias(String action, String canonicalText, Integer catalogId, Integer superCatalogId) {
            this.action = action;
            this.canonicalText = canonicalText;
            this.catalogId = catalogId;
            this.superCatalogId = superCatalogId;
        }
        public boolean isIgnore() { return "IGNORE".equals(action); }
        public boolean isPin() { return "PIN".equals(action) && (catalogId != null || superCatalogId != null); }
        public boolean isRewrite() { return "REWRITE".equals(action) && canonicalText != null; }
        public Integer getCatalogId() { return catalogId; }
        public Integer getSuperCatalogId() { return superCatalogId; }

        /**
         * Substitutes the catalog's spelling while preserving whatever variant spec
         * the circular gave, so "iPhone Air" becomes "iPhone 17 Air" and
         * "RENO16C 12+256GB" would become "<canonical> 12+256GB".
         */
        public String rewrite(String rawText) {
            StringBuilder sb = new StringBuilder(canonicalText);
            Matcher m = VARIANT_SPEC.matcher(rawText);
            while (m.find()) {
                sb.append(' ').append(m.group());
            }
            return sb.toString().trim();
        }
    }

    private final Map<String, Alias> byLabelAndText = new HashMap<>();

    private ProductAliases() { }

    public static ProductAliases load(CircularIngestRepository repository) {
        ProductAliases aliases = new ProductAliases();
        for (Object[] row : repository.selectProductAliases()) {
            String label = (String) row[0];
            String rawText = (String) row[1];
            String action = (String) row[2];
            String canonical = (String) row[3];
            int catalogId = ((Number) row[4]).intValue();
            int superCatalogId = ((Number) row[5]).intValue();
            aliases.byLabelAndText.put(key(label, rawText),
                    new Alias(action,
                            canonical == null || canonical.isEmpty() ? null : canonical,
                            catalogId == 0 ? null : catalogId,
                            superCatalogId == 0 ? null : superCatalogId));
        }
        return aliases;
    }

    /** Keyed on the normalised product text so trivial spacing differences still hit. */
    private static String key(String divisionLabel, String rawText) {
        return divisionLabel + "" + ProductNames.norm(rawText);
    }

    public Alias find(String divisionLabel, String rawText) {
        return byLabelAndText.get(key(divisionLabel, rawText));
    }

    public int size() { return byLabelAndText.size(); }
}