Subversion Repositories SmartDukaan

Rev

Rev 35791 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.spice.profitmandi.dao.service.shopify;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spice.profitmandi.common.enumuration.SchemeType;
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
import com.spice.profitmandi.common.web.client.RestClient;
import com.spice.profitmandi.dao.entity.dtr.WebOffer;
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
import com.spice.profitmandi.dao.repository.catalog.ItemRepository;
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
import com.spice.profitmandi.dao.repository.dtr.WebOfferRepository;
import com.spice.profitmandi.dao.repository.fofo.CurrentInventorySnapshotRepository;
import com.spice.profitmandi.service.inventory.FofoAvailabilityInfo;
import com.spice.profitmandi.service.inventory.FofoCatalogResponse;
import com.spice.profitmandi.service.inventory.SaholicInventoryService;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class ShopifyService {

    private static final Logger logger = LogManager.getLogger(ShopifyService.class);

    @Value("${new.solr.url}")
    private String solrUrl;

    @Autowired
    ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    ShopifyGraphQLService shopifyGraphQLService;
    @Autowired
    RestClient restClient;
    @Autowired
    FofoStoreRepository fofoStoreRepository;
    @Autowired
    ItemRepository itemRepository;
    @Autowired
    WebOfferRepository webOfferRepository;
    @Autowired
    CurrentInventorySnapshotRepository currentInventorySnapshotRepository;
    @Autowired
    SaholicInventoryService saholicInventoryService;

    // -------------------------------
    // ✅ 1. Create Product (with duplicate check)
    // -------------------------------
    public JsonNode createProduct(Map<String, Object> productInput) {
        try {
            return shopifyGraphQLService.productCreate(productInput);
        } catch (Exception e) {
            logger.error("createProduct Error: {}", e.getMessage());
            return objectMapper.createObjectNode();
        }
    }

    // -------------------------------
    // ✅ 2. Get All Products
    // -------------------------------
    public JsonNode getAllProducts() {
        try {
            String query = "query { products(first: 50) { edges { node { id title handle } } } }";
            return shopifyGraphQLService.execute(query, null);
        } catch (Exception e) {
            logger.error("getAllProducts Error: {}", e.getMessage());
            return objectMapper.createObjectNode();
        }
    }

    // -------------------------------
    // ✅ 3. Get Product by ID
    // -------------------------------
    public JsonNode getProductById(String productId) {
        try {
            String query = "query($id: ID!) { product(id: $id) { id title handle } }";
            Map<String, Object> vars = new HashMap<String, Object>();
            vars.put("id", productId);
            return shopifyGraphQLService.execute(query, vars);
        } catch (Exception e) {
            logger.error("getProductById Error: {}", e.getMessage());
            return objectMapper.createObjectNode();
        }
    }

    // -------------------------------
    // ✅ 4. Update Product by ID
    // -------------------------------
    public JsonNode updateProductById(String productId, Map<String, Object> params) {
        try {
            params.put("id", productId);
            String mutation = "mutation($input: ProductInput!) { productUpdate(input: $input) { product { id title } userErrors { field message } } }";
            Map<String, Object> vars = new HashMap<String, Object>();
            vars.put("input", params);
            return shopifyGraphQLService.execute(mutation, vars);
        } catch (Exception e) {
            logger.error("updateProductById Error: {}", e.getMessage());
            return objectMapper.createObjectNode();
        }
    }

    // -------------------------------
    // ✅ 5. Delete Product by ID
    // -------------------------------
    public JsonNode deleteProductById(String productId) {
        try {
            String mutation = "mutation($input: ProductDeleteInput!) { productDelete(input: $input) { deletedProductId userErrors { field message } } }";
            Map<String, Object> vars = new HashMap<String, Object>();
            Map<String, Object> input = new HashMap<String, Object>();
            input.put("id", productId);
            vars.put("input", input);
            return shopifyGraphQLService.execute(mutation, vars);
        } catch (Exception e) {
            logger.error("deleteProductById Error: {}", e.getMessage());
            return objectMapper.createObjectNode();
        }
    }

    public JsonNode getAllLocations() {
        try {
            String query = "query { locations(first: 10) { edges { node { id name } } } }";
            return shopifyGraphQLService.execute(query, null);
        } catch (Exception e) {
            logger.error("getAllLocations Error: {}", e.getMessage());
            return objectMapper.createObjectNode();
        }
    }

    public String getPrimaryLocationId() {
        try {
            JsonNode locationsResponse = getAllLocations();
            JsonNode edges = locationsResponse
                    .path("locations")
                    .path("edges");

            if (edges.isArray() && edges.size() > 0) {
                String locationId = edges.get(0).path("node").path("id").asText();
                logger.info("✅ Using Shopify Location ID: {}", locationId);
                return locationId;
            } else {
                logger.warn("⚠️ No Shopify locations found.");
                return null;
            }
        } catch (Exception e) {
            logger.error("getPrimaryLocationId Error: {}", e.getMessage());
            return null;
        }
    }

    public List<Map<String, Object>> buildAllVariantsBulk(List<FofoAvailabilityInfo> items, List<Map<String, String>> variantAttrs) {

        List<Map<String, Object>> list = new ArrayList<>();

        for (FofoAvailabilityInfo item : items) {
            String color = item.getColor().trim();
            String price = String.valueOf(item.getSellingPrice());
            String compareAt = String.valueOf(item.getMrp());

            for (Map<String, String> attr : variantAttrs) {
                String storageValue = attr.get("variantName").trim();
                String sku = String.valueOf(attr.get("catalogId"));
                list.add(buildVariant(color, storageValue, price, compareAt, sku));
            }
        }

        return list;
    }

    public Map<String, JsonNode> fetchVariantMapByOptions(String productId) throws Exception {
        String query =
            "query getVariants($id: ID!) { " +
                "  product(id: $id) { " +
                "    variants(first: 100) { " +
                "      edges { " +
                "        node { " +
                "          id " +
                "          title " +
                "          sku " +
                "          inventoryItem { id } " +
                "          selectedOptions { name value } " +
                "          image { originalSrc } " +
                "        } " +
                "      } " +
                "    } " +
                "  } " +
                "}";

        Map<String,Object> vars = new HashMap<>();
        vars.put("id", productId);

        JsonNode data = shopifyGraphQLService.execute(query, vars);
        JsonNode edges = data.path("product").path("variants").path("edges");

        Map<String, JsonNode> map = new HashMap<>();

        for (JsonNode edge : edges) {
            JsonNode node = edge.path("node");

            String color = "", variant = "", insurance = "";

            for (JsonNode opt : node.path("selectedOptions")) {
                String name = opt.path("name").asText();
                String value = opt.path("value").asText();
                if ("Color".equalsIgnoreCase(name)) color = value;
                if ("Variant".equalsIgnoreCase(name)) variant = value;
                if ("Insurance".equalsIgnoreCase(name)) insurance = value;
            }

            String key = color + "|" + variant + "|" + insurance;
            map.put(key.trim(), node);
        }

        return map;
    }

    public List<JsonNode> getAllOrders() {
        List<JsonNode> allOrders = new ArrayList<>();
        try {
            String cursor = null;
            while (true) {
                JsonNode page = shopifyGraphQLService.getOrders(cursor);
                JsonNode ordersNode = page.path("orders");
                JsonNode edges = ordersNode.path("edges");
                for (JsonNode edge : edges) {
                    allOrders.add(edge.path("node"));
                }
                boolean hasNext = ordersNode.path("pageInfo").path("hasNextPage").asBoolean();
                if (!hasNext) break;
                cursor = ordersNode.path("pageInfo").path("endCursor").asText();
            }
        } catch (Exception e) {
            logger.error("getAllOrders Error: {}", e.getMessage());
        }
        return allOrders;
    }

    public JsonNode getOrderDetailById(String id) {
        String graphQLId = "gid://shopify/Order/" + id;
        JsonNode order = null;
        try {
            JsonNode orderNode = shopifyGraphQLService.getOrderById(graphQLId);
            order = orderNode.path("order");
        } catch (Exception e) {
            logger.error("getAllOrders Error: {}", e.getMessage());
        }

        return order;
    }

    public List<FofoCatalogResponse> getAllDocs(FofoStore fofoStore) throws Exception {

        Map<String, String> params = new HashMap<String, String>();
        List<String> mandatoryQ = new ArrayList<String>();
        mandatoryQ.add("+active_b:true AND show_default_b:true");

        params.put("q", StringUtils.join(mandatoryQ, " "));
        params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");
        params.put("wt", "json");
        params.put("rows", "10");

        String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
        JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
        JSONArray docs = solrResponseJSONObj.getJSONArray("docs");

        return getCatalogResponse(docs, fofoStore.getId());

    }

    public List<FofoCatalogResponse> getAllDocsPaginated(FofoStore fofoStore, int batchSize) throws Exception {
        List<FofoCatalogResponse> allProducts = new ArrayList<>();
        int start = 0;

        while (true) {
            Map<String, String> params = new HashMap<>();
            params.put("q", "+active_b:true AND show_default_b:true");
            params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");
            params.put("wt", "json");
            params.put("rows", String.valueOf(batchSize));
            params.put("start", String.valueOf(start));

            String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
            JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
            int numFound = solrResponseJSONObj.getInt("numFound");
            JSONArray docs = solrResponseJSONObj.getJSONArray("docs");

            if (docs.length() == 0) break;

            List<FofoCatalogResponse> batch = getCatalogResponse(docs, fofoStore.getId());
            allProducts.addAll(batch);

            start += batchSize;
            if (start >= numFound) break;

            logger.info("Fetched {} / {} products from Solr", Math.min(start, numFound), numFound);
        }

        logger.info("Total products fetched from Solr: {}", allProducts.size());
        return allProducts;
    }

    public List<FofoCatalogResponse> getAllDocsPaginatedForWarehouse(int batchSize) throws Exception {
        List<FofoCatalogResponse> allProducts = new ArrayList<>();
        int start = 0;

        while (true) {
            Map<String, String> params = new HashMap<>();
            params.put("q", "+active_b:true AND show_default_b:true");
            params.put("fl", "*, [child parentFilter=id:catalog* childFilter=active_b:true ]");
            params.put("wt", "json");
            params.put("rows", String.valueOf(batchSize));
            params.put("start", String.valueOf(start));

            String response = restClient.get(SchemeType.HTTP, solrUrl, 8984, "solr/demo/select", params);
            JSONObject solrResponseJSONObj = new JSONObject(response).getJSONObject("response");
            int numFound = solrResponseJSONObj.getInt("numFound");
            JSONArray docs = solrResponseJSONObj.getJSONArray("docs");

            if (docs.length() == 0) break;

            List<FofoCatalogResponse> batch = getCatalogResponse(docs, 0);
            allProducts.addAll(batch);

            start += batchSize;
            if (start >= numFound) break;

            logger.info("Fetched {} / {} products from Solr (warehouse)", Math.min(start, numFound), numFound);
        }

        logger.info("Total products fetched from Solr (warehouse): {}", allProducts.size());
        return allProducts;
    }

    private List<FofoCatalogResponse> getCatalogResponse(JSONArray docs, int fofoId) throws ProfitMandiBusinessException {
        Map<Integer, Integer> ourItemAvailabilityMap = null;
        Map<Integer, Integer> partnerStockAvailabilityMap = new HashMap<>();
        List<FofoCatalogResponse> dealResponse = new ArrayList<>();
        if (docs.length() > 0) {
            HashSet<Integer> itemsSet = new HashSet<>();
            for (int i = 0; i < docs.length(); i++) {
                JSONObject doc = docs.getJSONObject(i);
                if (doc.has("_childDocuments_")) {
                    for (int j = 0; j < doc.getJSONArray("_childDocuments_").length(); j++) {
                        JSONObject childItem = doc.getJSONArray("_childDocuments_").getJSONObject(j);
                        int itemId = childItem.getInt("itemId_i");
                        itemsSet.add(itemId);
                    }
                }
            }
            if (itemsSet.isEmpty()) {
                return dealResponse;
            }
            if (fofoId > 0) {
                partnerStockAvailabilityMap = currentInventorySnapshotRepository.selectItemsStock(fofoId).stream().collect(Collectors.toMap(x -> x.getItemId(), x -> x.getAvailability()));
            }
            ourItemAvailabilityMap = saholicInventoryService.getTotalAvailabilityByItemIds(new ArrayList<>(itemsSet));
        }

        for (int i = 0; i < docs.length(); i++) {
            Map<Integer, FofoAvailabilityInfo> fofoAvailabilityInfoMap = new HashMap<>();
            JSONObject doc = docs.getJSONObject(i);
            FofoCatalogResponse ffdr = new FofoCatalogResponse();
            ffdr.setCatalogId(doc.getInt("catalogId_i"));
            ffdr.setImageUrl(doc.getString("imageUrl_s"));
            ffdr.setTitle(doc.getString("title_s"));
            ffdr.setSuperCatalogTitle(doc.getString("superCatalog_s"));
            ffdr.setSuperCatalogId(doc.getInt("superCatalog_i"));
            ffdr.setSuperCatalogVariants(doc.getString("superCatalogVariants_s"));
            List<WebOffer> webOffers = webOfferRepository.selectAllActiveOffers().get(ffdr.getCatalogId());
            if (webOffers != null && webOffers.size() > 0) {
                ffdr.setOffers(webOffers.stream().map(x -> x.getTitle()).collect(Collectors.toList()));
            }
            try {
                ffdr.setFeature(doc.getString("feature_s"));
            } catch (Exception e) {
                ffdr.setFeature(null);
            }
            ffdr.setBrand(doc.getJSONArray("brand_ss").getString(0));

            if (doc.has("_childDocuments_")) {
                for (int j = 0; j < doc.getJSONArray("_childDocuments_").length(); j++) {
                    try {
                        JSONObject childItem = doc.getJSONArray("_childDocuments_").getJSONObject(j);
                        int itemId = childItem.getInt("itemId_i");
                        ffdr.setIsSmartPhone(itemRepository.selectById(itemId).isSmartPhone());
                        float sellingPrice = (float) childItem.getDouble("sellingPrice_f");
                        if (fofoAvailabilityInfoMap.containsKey(itemId)) {
                            if (fofoAvailabilityInfoMap.get(itemId).getSellingPrice() > sellingPrice) {
                                fofoAvailabilityInfoMap.get(itemId).setSellingPrice(sellingPrice);
                                fofoAvailabilityInfoMap.get(itemId).setMop((float) childItem.getDouble("mop_f"));
                            }
                        } else {
                            FofoAvailabilityInfo fdi = new FofoAvailabilityInfo();
                            fdi.setSellingPrice(sellingPrice);
                            fdi.setMrp(childItem.getDouble("mrp_f"));
                            fdi.setMop((float) childItem.getDouble("mop_f"));
                            fdi.setColor(childItem.has("color_s") ? childItem.getString("color_s") : "");
                            fdi.setTagId(childItem.getInt("tagId_i"));
                            fdi.setItem_id(itemId);
                            fdi.setCatalog_id(doc.getInt("catalogId_i"));
                            fdi.setCashback(0);
                            fdi.setMinBuyQuantity(1);
                            int partnerAvailability = partnerStockAvailabilityMap.get(itemId) == null ? 0 : partnerStockAvailabilityMap.get(itemId);
                            int ourStockAvailability = ourItemAvailabilityMap.get(itemId) == null ? 0 : Math.max(0, ourItemAvailabilityMap.get(itemId));
                            fdi.setActive(partnerAvailability > 0);
                            fdi.setPartnerAvailability(partnerAvailability);
                            fdi.setAvailability(Math.min(5, ourStockAvailability + partnerAvailability));
                            fdi.setQuantityStep(1);
                            fdi.setMaxQuantity(fdi.getAvailability());
                            fofoAvailabilityInfoMap.put(itemId, fdi);
                        }
                    } catch (Exception e) {
                        logger.warn("Skipping stale item in Solr doc catalogId={}: {}", doc.getInt("catalogId_i"), e.getMessage());
                    }
                }
            }
            if (fofoAvailabilityInfoMap.values().size() > 0) {
                ffdr.setItems(fofoAvailabilityInfoMap.values().stream().sorted(Comparator.comparing(FofoAvailabilityInfo::isActive, Comparator.reverseOrder()).thenComparingInt(y -> -y.getAvailability())).collect(Collectors.toList()));
                dealResponse.add(ffdr);
            }
        }
        return dealResponse.stream().sorted(Comparator.comparing(FofoCatalogResponse::getItems, (s1, s2) -> (s2.get(0).isActive() ? 1 : 0) - (s1.get(0).isActive() ? 1 : 0)).thenComparing(FofoCatalogResponse::getItems, (x, y) -> y.get(0).getAvailability() - x.get(0).getAvailability())).collect(Collectors.toList());
    }

    private List<Map<String, String>> buildSelectedOptions(String color, String storage) {

        List<Map<String, String>> selectedOptions = new ArrayList<>();

        Map<String, String> c = new HashMap<>();
        c.put("name", "Colour");
        c.put("value", color);
        selectedOptions.add(c);

        Map<String, String> s = new HashMap<>();
        s.put("name", "Storage");
        s.put("value", storage);
        selectedOptions.add(s);

        return selectedOptions;
    }

    private Map<String, Object> buildVariant(String color, String storage, String price, String compareAt, String sku) {
        Map<String, Object> v = new HashMap<>();

        v.put("selectedOptions", this.buildSelectedOptions(color, storage));
        v.put("price", price);
        v.put("compareAtPrice", compareAt);
        v.put("sku", sku);

        return v;
    }
}