Subversion Repositories SmartDukaan

Rev

Rev 35971 | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.spice.profitmandi.service.user;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.hash.Hashing;
import com.google.gson.Gson;
import com.spice.profitmandi.common.enumuration.ActivationType;
import com.spice.profitmandi.common.enumuration.FofoType;
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
import com.spice.profitmandi.common.model.*;
import com.spice.profitmandi.common.util.StringUtils;
import com.spice.profitmandi.common.util.Utils;
import com.spice.profitmandi.dao.entity.catalog.BrandCatalog;
import com.spice.profitmandi.dao.entity.dtr.*;
import com.spice.profitmandi.dao.entity.dtr.User;
import com.spice.profitmandi.dao.entity.fofo.*;
import com.spice.profitmandi.dao.entity.inventory.State;
import com.spice.profitmandi.dao.entity.logistics.AST;
import com.spice.profitmandi.dao.entity.logistics.ASTRepository;
import com.spice.profitmandi.dao.entity.user.*;
import com.spice.profitmandi.dao.enumuration.dtr.AccountType;
import com.spice.profitmandi.dao.enumuration.dtr.RetailerType;
import com.spice.profitmandi.dao.enumuration.dtr.RoleType;
import com.spice.profitmandi.dao.enumuration.fofo.PaymentOptionType;
import com.spice.profitmandi.dao.enumuration.transaction.PartnerVerificationApprovalStatus;
import com.spice.profitmandi.dao.model.LastMonthCreditedIncomeModel;
import com.spice.profitmandi.dao.repository.dtr.*;
import com.spice.profitmandi.dao.repository.dtr.UserRepository;
import com.spice.profitmandi.dao.repository.fofo.*;
import com.spice.profitmandi.dao.repository.inventory.StateRepository;
import com.spice.profitmandi.dao.repository.trialOnboarding.TrialFormRepository;
import com.spice.profitmandi.dao.repository.user.*;
import com.spice.profitmandi.service.PostOfficeService;
import com.spice.profitmandi.service.catalog.BrandsService;
import in.shop2020.model.v1.user.CartStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class RetailerServiceImpl implements RetailerService {

    private static final Logger LOGGER = LogManager.getLogger(RetailerServiceImpl.class);
    private static final String[] TRIAL_CODE_CREATION_AUDIENCE = new String[]{"amit.gupta@smartdukaan.com"};
    //private static final String [] TRIAL_CODE_CREATION_AUDIENCE = new String[]{};

    @Autowired
    private RetailerRepository retailerRepository;

    @Autowired
    private RetailerBlockBrandsRepository retailerBlockBrandsRepository;

    @Autowired
    private UserAccountRepository userAccountRepository;

    @Autowired
    private Mongo mongoClient;

    @Autowired
    Gson gson;

    @Autowired
    private UserRepository userRepository;
    @Autowired
    ObjectMapper objectMapper;


    @Autowired
    private CartRepository cartRepository;

    @Autowired
    private RetailerRegisteredAddressRepository retailerRegisteredAddressRepository;

    @Autowired
    private AddressRepository addressRepository;

    @Autowired
    private ShopRepository shopRepository;

    @Autowired
    private ShopAddressRepository shopAddressRepository;

    @Autowired
    private UserRoleRepository userRoleRepository;

    @Autowired
    private DocumentRepository documentRepository;

    @Autowired
    private PrivateDealUserRepository privateDealUserRepository;

    @Autowired
    private RetailerContactRepository retailerContactRepository;

    @Autowired
    private PrivateDealUserAddressMappingRepository privateDealUserAddressMappingRepository;

    @Autowired
    private CounterRepository counterRepository;

    @Autowired
    private StateRepository stateRepository;
    @Autowired
    private TrialFormRepository trialFormRepository;

    @Autowired
    @Qualifier("userUserRepository")
    private com.spice.profitmandi.dao.repository.user.UserRepository userUserRepository;

    @Autowired
    private DistrictMasterRepository districtMasterRepository;

    @Autowired
    private FofoStoreRepository fofoStoreRepository;

    @Autowired
    private ASTRepository aSTRepository;

    @Autowired
    private PostOfficeService postOfficeService;

    @Autowired
    private PaymentOptionRepository paymentOptionRepository;

    @Autowired
    private FofoPartnerPaymentOptionRepository fofoPartnerPaymentOptionRepository;

    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    private StoreTimelineTatService storeTimelineTatService;

    @Autowired
    private PartnerOnBoardingPanelRepository partnerOnBoardingPanelRepository;

    @Autowired
    private PartnerOnboardingVerificationRepository partnerOnboardingVerificationRepository;

    @Autowired
    private PincodePartnerRepository pincodePartnerRepository;

    @Autowired
    SchemeInOutRepository schemeInOutRepository;

    @Autowired
    OfferPayoutRepository offerPayoutRepository;

    @Autowired
    PartnerTypeChangeService partnerTypeChangeService;

    @Autowired
    BrandsService brandsService;
    @Autowired
    private JavaMailSender googleMailSender;

    @Override
    public Map<String, Object> getByEmailIdOrMobileNumber(String emailIdOrMobileNumber) throws ProfitMandiBusinessException {
        User user = null;
        int fofoId = Utils.SYSTEM_PARTNER_ID;

        try {
            user = userRepository.selectByEmailIdOrMobileNumber(emailIdOrMobileNumber);
        } catch (ProfitMandiBusinessException profitMandiBusinessException) {

        }
        if (user == null) {
            try {
                user = userRepository.selectBySecondryEmailId(emailIdOrMobileNumber);
            } catch (ProfitMandiBusinessException profitMandiBusinessException) {

            }
        }
        Map<String, Object> map = new HashMap<>();
        map.put("stateNames", stateRepository.selectAll().stream().map(State::getName).collect(Collectors.toList()));
        LOGGER.info("user - {}", user);
        if (user != null) {

            List<UserRole> userRoles = userRoleRepository.selectByUserId(user.getId());

            Role role = roleRepository.selectByName(RoleType.FOFO.toString());

            map.put("userRoles", userRoles);
            map.put("user", user);
            // map.put("retailer", retailer);
            map.put("fofoRole", this.containsRoleType(userRoles, role.getId()));

            map.put("userRoleNames", this.toString(userRoles));
            try {
                int retailerId = userAccountRepository.selectRetailerIdByUserId(user.getId());
                Retailer retailer = retailerRepository.selectById(retailerId);

                map.put("retailer", retailer);

                List<String> retailerBlockBrands = null;
                Set<String> brands = null;
                if (retailer.getId() != fofoId) {
                    brands = brandsService.getBrands(retailer.getId(), null, 3).stream().map(BrandCatalog::getName).collect(Collectors.toSet());
                    retailerBlockBrands = brandsService.partnerIneligibleBrands(fofoId);

                } else {
                    LOGGER.info("fofoId" + fofoId);
                    brands = brandsService.getBrands(fofoId, null, 3).stream().map(BrandCatalog::getName).collect(Collectors.toSet());
                }
                map.put("brands", brands);
                map.put("retailerBlockBrands", retailerBlockBrands);

                try {
                    FofoStore gstStore = fofoStoreRepository.selectByRetailerId(retailer.getId());
                    map.put("gstNumber", gstStore != null ? gstStore.getGstNumber() : null);
                } catch (ProfitMandiBusinessException e) {
                    LOGGER.error("FofoStore not found for GST lookup");
                }
                try {
                    int retailerAddressId = retailerRegisteredAddressRepository.selectAddressIdByRetailerId(retailer.getId());
                    Address retailerAddress = addressRepository.selectById(retailerAddressId);
                    map.put("retailerAddress", retailerAddress);
                    State state = stateRepository.selectByName(retailerAddress.getState());
                    // LOGGER.info("retailerAddress.." + retailerAddress);
                    List<DistrictMaster> districtMasters = districtMasterRepository.selectByStateShortName(state.getShortName());
                    map.put("districtMasters", districtMasters);
                    // LOGGER.info("districtMasters" + districtMasters);
                    List<Shop> shops = shopRepository.selectByRetailerId(retailer.getId());
                    map.put("shops", shops);
                    this.addAddress(shops);

                    if (this.containsRoleType(userRoles, role.getId())) {
                        try {
                            FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(retailerId);
                            map.put("fofoStore", fofoStore);
                            List<AST> astDetail = postOfficeService.getAreasAndTerritoriesByStateName(retailerAddress.getState());
                            LOGGER.info("astDetail {}", astDetail);
                            map.put("astDetail", astDetail);


                            // map.put("counterSize", fofoStore.getCounterSize().toString());
                            // LOGGER.info("fofoStore" + fofoStore);
                        } catch (ProfitMandiBusinessException profitMandiBusinessException) {
                            LOGGER.error("FofoStore code not found");
                        }
                    }
                    // LOGGER.info("shops" + shops);
                } catch (ProfitMandiBusinessException profitMandiBusinessException) {
                    LOGGER.error("Retailer Registered Address not found");
                }
            } catch (ProfitMandiBusinessException profitMandiBusinessException) {
                LOGGER.error("Retailer not found in user_account");
            }
        } else {
            try {
                TrialForm trialForm = trialFormRepository.selectByEmailOrMobile(emailIdOrMobileNumber);
                LOGGER.info("trialForm - {}", trialForm);
                map.put("trialForm", gson.toJson(trialForm));
            } catch (Exception e) {
                LOGGER.error("Retailer not found in user_account");
            }
        }

        return map;
    }

    private boolean containsRoleType(List<UserRole> userRoles, int roleId) {
        return userRoles != null && userRoles.stream().anyMatch(ur -> ur.getRoleId() == roleId);
    }

    private List<UserRole> addRole(List<UserRole> userRoles, int userId, int roleId) {
        if (userRoles == null) {
            userRoles = new ArrayList<>();
        }
        if (!this.containsRoleType(userRoles, roleId)) {
            UserRole userRole = new UserRole();
            userRole.setUserId(userId);
            userRole.setRoleId(roleId);
            try {
                userRoleRepository.persist(userRole);
            } catch (ProfitMandiBusinessException e) {
                LOGGER.error("UserRole is already exist");
            }
            userRoles.add(userRole);
        }
        return userRoles;

    }

    private List<UserRole> removeRole(List<UserRole> userRoles, int userId, int roleId) {
        if (userRoles == null) {
            userRoles = new ArrayList<>();
            return userRoles;
        }
        try {
            userRoles.remove(userRoleRepository.deleteByRoleUserId(userId, roleId));
        } catch (Exception e) {

        }
        return userRoles;

    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<String, Object> updateRetailerDetails(UpdateRetailerRequest updateRetailerRequest) throws
            ProfitMandiBusinessException {
        Map<String, Object> map = this.getByEmailIdOrMobileNumber(updateRetailerRequest.getEmailIdOrMobileNumber());
        User dtrUser = (User) map.get("user");

        dtrUser = this.createUser(dtrUser, updateRetailerRequest);

        map.put("user", dtrUser);
        List<UserRole> userRoles = (List<UserRole>) map.get("userRoles");
        Role roleUser = roleRepository.selectByName(RoleType.USER.toString());
        userRoles = this.addRole(userRoles, dtrUser.getId(), roleUser.getId());
        Retailer retailer = (Retailer) map.get("retailer");
        retailer = this.updateRetailer(dtrUser, retailer, updateRetailerRequest);

        map.put("retailer", retailer);

        // Ensure UserAccount links new dtr user to retailer (handles case where
        // dtr.user was deleted and re-created with a new ID)
        try {
            userAccountRepository.selectSaholicByUserId(dtrUser.getId());
        } catch (ProfitMandiBusinessException e) {
            try {
                UserAccount ua = new UserAccount();
                ua.setAccountKey(retailer.getId());
                ua.setUserId(dtrUser.getId());
                ua.setType(AccountType.saholic);
                userAccountRepository.persist(ua);
            } catch (Exception ex) {
                LOGGER.error("Error creating saholic UserAccount for userId: {}", dtrUser.getId(), ex);
            }
        }

        List<String> retailerBlockBrands = retailerBlockBrandsRepository.selectAllByRetailer(retailer.getId()).stream().map(x -> x.getBlockBrands()).collect(Collectors.toList());

        LOGGER.info("retailerBlockBrands" + retailerBlockBrands);

        if (!retailerBlockBrands.isEmpty()) {
            retailerBlockBrandsRepository.deleteBrands(retailer.getId());
        }

        for (String blockBrand : updateRetailerRequest.getBlocksBrands()) {
            RetailerBlockBrands retailerBlockBrand = new RetailerBlockBrands();

            retailerBlockBrand.setFofoId(retailer.getId());
            retailerBlockBrand.setBlockBrands(blockBrand);
            retailerBlockBrandsRepository.persist(retailerBlockBrand);

        }
        map.put("retailerBlockBrands", retailerBlockBrands);

        Role roleRetailer = roleRepository.selectByName(RoleType.RETAILER.toString());

        userRoles = this.addRole(userRoles, dtrUser.getId(), roleRetailer.getId());

        Role roleFofo = roleRepository.selectByName(RoleType.FOFO.toString());

        if (updateRetailerRequest.isFofo()) {
            userRoles = this.addRole(userRoles, dtrUser.getId(), roleFofo.getId());
        } else {
            userRoles = this.removeRole(userRoles, dtrUser.getId(), roleFofo.getId());
        }
        map.put("fofoRole", this.containsRoleType(userRoles, roleFofo.getId()));

        if (this.containsRoleType(userRoles, roleFofo.getId())) {
            this.createDefaultPaymentOption(retailer.getId());
        }

        map.put("userRoles", userRoles);
        map.put("userRoleNames", this.toString(userRoles));

        Address retailerAddress = (Address) map.get("retailerAddress");
        retailerAddress = this.updateRetailerAddress(retailerAddress, updateRetailerRequest.getAddress(), retailer.getId());
        map.put("retailerAddress", retailerAddress);

        if (updateRetailerRequest.isFofo()) {
            FofoStore fofoStore = this.createFofoStoreCodeByRetailerId(retailer.getId(), updateRetailerRequest.getDistrictName(), retailerAddress.getState(), updateRetailerRequest);
            map.put("fofoStore", fofoStore);
            if (fofoStore.isClosed() && updateRetailerRequest.isActive()) {
                throw new ProfitMandiBusinessException("Store Closed", "Store Permanently Closed",
                        "This store is permanently closed and cannot be reactivated");
            }
            fofoStore.setActive(updateRetailerRequest.isActive());
            List<AST> astDetail = postOfficeService.getAreasAndTerritoriesByStateName(retailerAddress.getState());
            map.put("astDetail", astDetail);
        }

        this.createPrivateDealUser(dtrUser, updateRetailerRequest.isFofo(), updateRetailerRequest.getGstNumber(), retailer, retailerAddress.getId());
        map.put("gstNumber", updateRetailerRequest.getGstNumber());
        this.updateSaholicUser(retailer.getId(), retailerAddress.getId());

        List<Shop> shops = (List<Shop>) map.get("shops");
        if (shops == null) {
            shops = new ArrayList<>();
            map.put("shops", shops);
        }
        this.updateRetailerShops(shops, updateRetailerRequest.getShops(), retailer.getId(), retailerAddress);

        return map;

    }

    private void createDefaultPaymentOption(int fofoId) {
        List<Integer> paymentOptionIds = fofoPartnerPaymentOptionRepository.selectPaymentOptionIdsByFofoId(fofoId);
        if (paymentOptionIds.isEmpty()) {
            PaymentOption paymentOption = null;
            try {
                paymentOption = paymentOptionRepository.selectByName(PaymentOptionType.CASH.toString());
            } catch (ProfitMandiBusinessException profitMandiBusinessException) {
                paymentOption = new PaymentOption();
                paymentOption.setName(PaymentOptionType.CASH.toString());
                paymentOptionRepository.persist(paymentOption);
            }
            FofoPartnerPaymentOption fofoPartnerPaymentOption = new FofoPartnerPaymentOption();
            fofoPartnerPaymentOption.setFofoId(fofoId);
            fofoPartnerPaymentOption.setPaymentOptionId(paymentOption.getId());
            fofoPartnerPaymentOptionRepository.persist(fofoPartnerPaymentOption);
        }

    }

    private User createUser(User user, UpdateRetailerRequest updateRetailerRequest) throws
            ProfitMandiBusinessException {

        User dtrUser = null;
        try {
            dtrUser = userRepository.selectByMobileNumber(updateRetailerRequest.getUserMobileNumber());
        } catch (ProfitMandiBusinessException e) {
            //Ignore the exception
        }
        if (dtrUser != null && (user == null || user.getId() != dtrUser.getId()))
            throw new ProfitMandiBusinessException("Mobile already exist", updateRetailerRequest.getUserMobileNumber(), dtrUser.getEmailId());

        User dtrUserByEmail = null;
        try {
            dtrUserByEmail = userRepository.selectByEmailId(updateRetailerRequest.getUserEmailId());
        } catch (ProfitMandiBusinessException e) {
            //ignore the exception
        }
        if (user != null && dtrUserByEmail != null && user.getId() != dtrUserByEmail.getId())
            throw new ProfitMandiBusinessException("Email already exist", updateRetailerRequest.getUserEmailId(), "");


        if (user == null) {
            user = new User();
            user.setCity("");
            user.setPinCode(0);
            user.setState("");
            user.setPassword("");
            user.setMobile_verified(false);
            user.setReferral_url("");
            user.setGroup_id(1);
            user.setStatus(1);
            user.setActivated(true);
            user.setCreateTimestamp(LocalDateTime.now());
        }
        user.setActivated(updateRetailerRequest.isActive());
        user.setFirstName(updateRetailerRequest.getUserFirstName());
        user.setLastName(updateRetailerRequest.getUserLastName());
        user.setMobileNumber(updateRetailerRequest.getUserMobileNumber());
        user.setEmailId(updateRetailerRequest.getUserEmailId());
        user.setUsername(updateRetailerRequest.getUserEmailId());
        user.setUpdateTimestamp(LocalDateTime.now());
        userRepository.persist(user);
        return user;

    }

    private int createSaholicUser(User user, String retailerName) {
        com.spice.profitmandi.dao.entity.user.User saholicUser = null;
        try {
            saholicUser = userUserRepository.selectByEmailId(user.getEmailId());
        } catch (ProfitMandiBusinessException e) {
            LOGGER.info("User doesnt exist in old system");
        }
        if (saholicUser == null) {
            Cart cart = new Cart();
            cart.setCartStatus(CartStatus.ACTIVE);
            cartRepository.persist(cart);
            saholicUser = new com.spice.profitmandi.dao.entity.user.User();
            saholicUser.setEmailId(user.getEmailId());
            saholicUser.setName(retailerName);
            saholicUser.setActiveCartId(cart.getId());
            saholicUser.setCreateTimestamp(LocalDateTime.now());
            userUserRepository.persist(saholicUser);

            UserAccount ua = new UserAccount();
            ua.setAccountKey(saholicUser.getId());
            ua.setUserId(user.getId());
            ua.setType(AccountType.saholic);
            userAccountRepository.persist(ua);

            UserAccount ua2 = new UserAccount();
            ua2.setAccountKey(saholicUser.getActiveCartId());
            ua2.setUserId(user.getId());
            ua2.setType(AccountType.cartId);
            userAccountRepository.persist(ua2);
            LOGGER.info("created....");
        }
        return saholicUser.getId();
    }

    private com.spice.profitmandi.dao.entity.user.User createSaholicUser(
            LoginRequestResponseModel loginRequestResponseModel) {
        com.spice.profitmandi.dao.entity.user.User saholicUser = null;
        try {
            saholicUser = userUserRepository.selectByEmailId(loginRequestResponseModel.getEmail());
            saholicUser.setCreateTimestamp(LocalDateTime.now());
        } catch (ProfitMandiBusinessException e) {
            LOGGER.info("User doesnt exist in old system");
        }
        if (saholicUser == null) {
            Cart cart = new Cart();
            cart.setCartStatus(CartStatus.ACTIVE);
            cartRepository.persist(cart);
            saholicUser = new com.spice.profitmandi.dao.entity.user.User();
            saholicUser.setPassword(getHash256(loginRequestResponseModel.getPassword()));
            saholicUser.setEmailId(loginRequestResponseModel.getEmail());
            saholicUser.setName(loginRequestResponseModel.getCustomerName());
            saholicUser.setActiveCartId(cart.getId());
            saholicUser.setCreateTimestamp(LocalDateTime.now());
            loginRequestResponseModel.setPassword(null);
            userUserRepository.persist(saholicUser);
        }
        return saholicUser;
    }

    private Retailer updateRetailer(User user, Retailer retailer, UpdateRetailerRequest updateRetailerRequest) throws
            ProfitMandiBusinessException {
        if (retailer == null) {
            LOGGER.info("createSaholicUser.....");
            int saholicUserId = this.createSaholicUser(user, updateRetailerRequest.getName());
            retailer = new Retailer();
            retailer.setId(saholicUserId);
        }
        retailer.setActive(updateRetailerRequest.isActive());
        user.setActivated(updateRetailerRequest.isActive());//
        // this.createRole(user.getId(), RoleType.RETAILER);
        retailer.setName(updateRetailerRequest.getName());
        retailer.setNumber(updateRetailerRequest.getNumber());
        if (updateRetailerRequest.getNumber() == null || updateRetailerRequest.getNumber().isEmpty()) {
            retailer.setType(RetailerType.UNREGISTERED_SHOP);
        } else {
            retailer.setType(RetailerType.GSTIN);
        }
        if (updateRetailerRequest.getDocumentId() > 0) {
            if (retailer.getDocumentId() != null && retailer.getDocumentId() != updateRetailerRequest.getDocumentId()) {
                try {
                    documentRepository.deleteById(retailer.getDocumentId());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            retailer.setDocumentId(updateRetailerRequest.getDocumentId());
            documentRepository.markDocumentAsPersisted(updateRetailerRequest.getDocumentId());
        }
        retailerRepository.persist(retailer);
        return retailer;
    }

    private void updateSaholicUser(int retailerId, int retailerAddressId) {
        try {
            com.spice.profitmandi.dao.entity.user.User user = userUserRepository.selectById(retailerId);
            user.setAddressId(retailerAddressId);
            Cart cart = cartRepository.selectById(user.getActiveCartId());
            cart.setAddressId(retailerAddressId);
        } catch (ProfitMandiBusinessException e) {

        }
    }

    private void createPrivateDealUser(User user, boolean fofo, String gstNumber, Retailer retailer,
                                       int retailerAddressId) {
        PrivateDealUser privateDealUser = null;
        try {
            privateDealUser = privateDealUserRepository.selectById(retailer.getId());
        } catch (ProfitMandiBusinessException profitMandiBusinessException) {
            LOGGER.error("PrivateDealUser not found");
        }

        // = privateDealUserRepository.selectById(saholicUser.getId());
        if (privateDealUser == null) {
            Integer counterId = this.createCounter(user.getEmailId(), gstNumber, user.getMobileNumber(), retailer.getName(), retailerAddressId);
            try {
                this.createPrivateDealUser(retailer.getId(), counterId, fofo);
            } catch (Exception e) {
                LOGGER.error("ERROR : ", e);
            }
        } else {
            if (privateDealUser.getCounterId() == null) {
                Integer counterId = this.createCounter(user.getEmailId(), gstNumber, user.getMobileNumber(), retailer.getName(), retailerAddressId);
                privateDealUser.setCounterId(counterId);
                privateDealUser.setFofo(fofo);
                privateDealUserRepository.persist(privateDealUser);
            } else {
                Counter counter = null;
                try {
                    counter = counterRepository.selectById(privateDealUser.getCounterId());
                } catch (ProfitMandiBusinessException profitMandiBusinessException) {
                    LOGGER.error("Counter not found with id [{}]", privateDealUser.getCounterId());
                }
                if (counter == null) {
                    this.createCounter(user.getEmailId(), gstNumber, user.getMobileNumber(), retailer.getName(), retailerAddressId);
                } else {
                    counter.setGstin(gstNumber);
                    counterRepository.persist(counter);
                }
                privateDealUser.setFofo(fofo);
                privateDealUserRepository.persist(privateDealUser);
            }
        }

        PrivateDealUserAddressMapping privateDealUserAddressMapping = new PrivateDealUserAddressMapping();
        privateDealUserAddressMapping.setUserId(retailer.getId());
        privateDealUserAddressMapping.setAddressId(retailerAddressId);
        privateDealUserAddressMappingRepository.persist(privateDealUserAddressMapping);
    }

    // Specifically set isFofo to true that has to be used by old system
    private void createPrivateDealUser(int retailerId, int counterId, boolean fofo) {
        PrivateDealUser privateDealUser = new PrivateDealUser();
        privateDealUser.setActive(true);
        privateDealUser.setBulkShipmentAmountLimit(fofo ? 1000000 : 50000);
        privateDealUser.setId(retailerId);
        privateDealUser.setCounterId(counterId);
        privateDealUser.setFofo(fofo);
        privateDealUserRepository.persist(privateDealUser);
    }

    private Integer createCounter(String emailId, String gstNumber, String mobileNumber, String name, int addressId) {
        if (gstNumber != null && !gstNumber.isEmpty()) {
            Counter counter = new Counter();
            counter.setEmailId(emailId);
            counter.setGstin(gstNumber);
            counter.setMobileNumber(mobileNumber);
            counter.setName(name);
            counter.setAddressId(addressId);
            counterRepository.persist(counter);
            return counter.getId();
        } else {
            return null;
        }
    }

    private Address updateRetailerAddress(Address address, CustomAddress customAddress, int retailerId) throws
            ProfitMandiBusinessException {
        if (address == null) {
            // Try to find existing address via RetailerRegisteredAddress (handles case where
            // dtr.user was deleted but retailer/address still exist in database)
            try {
                int existingAddressId = retailerRegisteredAddressRepository.selectAddressIdByRetailerId(retailerId);
                address = addressRepository.selectById(existingAddressId);
            } catch (ProfitMandiBusinessException e) {
                // No existing address found for this retailer
            }

            if (address != null) {
                // Existing address found - update it
                this.updateAddress(address, customAddress);
                RetailerRegisteredAddress retailerRegisteredAddress = retailerRegisteredAddressRepository.selectByRetailerId(retailerId);
                retailerRegisteredAddress.setAddressId(address.getId());
                retailerRegisteredAddressRepository.persist(retailerRegisteredAddress);
            } else {
                // Truly new address
                address = new Address();
                address.setRetaierId(retailerId);
                this.updateAddress(address, customAddress);

                final RetailerRegisteredAddress retailerRegisteredAddress = new RetailerRegisteredAddress();
                retailerRegisteredAddress.setRetailerId(retailerId);
                retailerRegisteredAddress.setAddressId(address.getId());
                retailerRegisteredAddressRepository.persist(retailerRegisteredAddress);
            }
        } else {
            this.updateAddress(address, customAddress);
            RetailerRegisteredAddress retailerRegisteredAddress = retailerRegisteredAddressRepository.selectByRetailerId(retailerId);
            retailerRegisteredAddress.setAddressId(address.getId());
            retailerRegisteredAddressRepository.persist(retailerRegisteredAddress);
        }
        return address;
    }

    private void updateAddress(Address address, CustomAddress customAddress) throws ProfitMandiBusinessException {
        address.setName(customAddress.getName());
        address.setPhoneNumber(customAddress.getPhoneNumber());
        address.setLine1(customAddress.getLine1());
        address.setLine2(customAddress.getLine2());
        address.setCity(customAddress.getCity());
        address.setPinCode(customAddress.getPinCode());
        State state = stateRepository.selectByName(customAddress.getState());
        if (state == null) {
            throw new ProfitMandiBusinessException("State name", "Invalid State - Pls Contact Technology", "Invalid State - Pls Contact Technology");
        }
        address.setState(state.getName());
        addressRepository.persist(address);
        LOGGER.info("Address Updated" + address);
    }

    private void updateRetailerShops(List<Shop> shops, Set<CustomShop> customShops, int retailerId, Address
            sameAsRetailerAddressValue) throws ProfitMandiBusinessException {
        if (shops.isEmpty()) {
            for (CustomShop customShop : customShops) {
                Shop shop = new Shop();
                this.createOrUpdateShop(shop, customShop, retailerId, sameAsRetailerAddressValue);
                shops.add(shop);
            }
        } else {
            for (Shop shop : shops) {
                for (CustomShop customShop : customShops) {
                    if (shop.getId() == customShop.getShopId()) {
                        this.createOrUpdateShop(shop, customShop, retailerId, sameAsRetailerAddressValue);
                    }
                }
            }
            for (CustomShop customShop : customShops) {
                if (customShop.getShopId() == 0) {
                    Shop shop = new Shop();
                    this.createOrUpdateShop(shop, customShop, retailerId, sameAsRetailerAddressValue);
                    shops.add(shop);
                }
            }
        }
    }

    private void createOrUpdateShop(Shop shop, CustomShop customShop, int retailerId, Address
            sameAsRetailerAddressValue) throws ProfitMandiBusinessException {
        shop.setRetailerId(retailerId);
        shop.setName(customShop.getName());
        if (customShop.getDocumentId() > 0) {
            if (shop.getDocumentId() != null && shop.getDocumentId() != customShop.getDocumentId()) {
                try {
                    documentRepository.deleteById(shop.getDocumentId());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            shop.setDocumentId(customShop.getDocumentId());
            documentRepository.markDocumentAsPersisted(customShop.getDocumentId());
        }
        if (shop.getAddressId() == null) {
            Address address = null;
            if (customShop.isSameAsRetailerAddress()) {
                address = sameAsRetailerAddressValue;
            } else {
                // shop.setDocumentId(customShop.getDocumentId());
                address = new Address();
                this.updateAddress(address, customShop.getAddress());
            }
            shop.setAddressId(address.getId());
            shop.setAddress(address);
            shopRepository.persist(shop);
            ShopAddress shopAddress = null;
            try {
                shopAddress = shopAddressRepository.selectByShopId(shop.getId());
                shopAddress.setAddressId(address.getId());
            } catch (ProfitMandiBusinessException profitMandiBusinessException) {
                shopAddress = new ShopAddress();
                shopAddress.setAddressId(address.getId());
                shopAddress.setShopId(shop.getId());
            }
            shopAddressRepository.persist(shopAddress);
        } else {
            Address address = null;
            try {
                address = addressRepository.selectById(shop.getAddressId());
                CustomAddress customAddress = customShop.getAddress();
                if (!(address.getName().equals(customAddress.getName()) && address.getLine1().equals(customAddress.getLine1()) && address.getLine2().equals(customAddress.getLine2()) && address.getCity().equals(customAddress.getCity()) && address.getPinCode().equals(customAddress.getPinCode()) && address.getState().equals(customAddress.getState()))) {
                    address = new Address();
                    ShopAddress shopAddress = shopAddressRepository.selectByShopId(shop.getId());
                    this.updateAddress(address, customAddress);
                    shopAddress.setAddressId(address.getId());
                    shopAddressRepository.persist(shopAddress);
                    shop.setAddress(address);
                    shopRepository.persist(shop);
                }
            } catch (Exception e) {
                if (customShop.isSameAsRetailerAddress()) {
                    address = sameAsRetailerAddressValue;
                } else {
                    // shop.setDocumentId(customShop.getDocumentId());
                    address = new Address();
                    this.updateAddress(address, customShop.getAddress());
                }
                shop.setAddressId(address.getId());
                shop.setAddress(address);
                shopRepository.persist(shop);
                ShopAddress shopAddress = null;
                try {
                    shopAddress = shopAddressRepository.selectByShopId(shop.getId());
                    shopAddress.setAddressId(address.getId());
                } catch (ProfitMandiBusinessException profitMandiBusinessException) {
                    shopAddress = new ShopAddress();
                    shopAddress.setAddressId(address.getId());
                    shopAddress.setShopId(shop.getId());
                }
                shopAddressRepository.persist(shopAddress);
            }

        }

    }

    private void addAddress(List<Shop> shops) throws ProfitMandiBusinessException {
        Set<Integer> shopIds = this.toShopIds(shops);
        if (shopIds.isEmpty()) {
            return;
        }
        List<ShopAddress> shopAddresses = shopAddressRepository.selectByShopIds(shopIds);
        Map<Integer, Address> addressIdAddressMap = this.toAddressIdAddressMap(shopAddresses);

        for (Shop shop : shops) {
            shop.setAddress(addressIdAddressMap.get(shop.getAddressId()));
        }
    }

    private Map<Integer, Address> toAddressIdAddressMap(List<ShopAddress> shopAddresses) throws ProfitMandiBusinessException {
        Map<Integer, Address> addressIdAddressMap = new HashMap<>();
        List<Integer> addressIds = this.toAddressIds(shopAddresses);
        List<Address> addresses = addressRepository.selectByIds(addressIds);
        for (Address address : addresses) {
            addressIdAddressMap.put(address.getId(), address);
        }
        return addressIdAddressMap;
    }

    private List<Integer> toAddressIds(List<ShopAddress> shopAddresses) {
        return shopAddresses.stream()
                .map(ShopAddress::getAddressId)
                .collect(Collectors.toList());
    }

    private Set<Integer> toShopIds(List<Shop> shops) {
        return shops.stream()
                .map(Shop::getId)
                .collect(Collectors.toSet());
    }

    private String toString(List<UserRole> userRoles) throws ProfitMandiBusinessException {
        Set<Integer> roleIds = new HashSet<>();
        for (UserRole userRole : userRoles) {
            roleIds.add(userRole.getRoleId());
        }
        List<Role> roles = roleRepository.selectByIds(roleIds);
        Function<Role, String> roleToNameFunction = role -> String.valueOf(role.getName());
        Set<String> userRoleStrings = roles.stream().map(roleToNameFunction).collect(Collectors.toSet());
        return String.join(", ", userRoleStrings);
    }

    private FofoStore createFofoStoreCodeByRetailerId(int retailerId, String districtName, String
            stateName, UpdateRetailerRequest updateRetailerRequest) throws ProfitMandiBusinessException {
        FofoStore fofoStore = null;
        try {
            fofoStore = fofoStoreRepository.selectByRetailerId(retailerId);
            fofoStore.setActivationType(ActivationType.ACTIVE);

        } catch (ProfitMandiBusinessException profitMandiBusinessException) {

        }

        if (fofoStore != null) {
            fofoStore.setCounterSize(updateRetailerRequest.getCountersize());
            fofoStore.setAstId(updateRetailerRequest.getAstId());
            fofoStore.setMinimumInvestment(updateRetailerRequest.getMinInvestment());
            fofoStore.setWarehouseId(updateRetailerRequest.getWarehouseId());
            fofoStore.setCounterPotential(updateRetailerRequest.getCounterPotential());
            fofoStore.setFofoType(updateRetailerRequest.getFofoType());
            fofoStore.setGstNumber(updateRetailerRequest.getGstNumber());
        } else {
            int retailerAddressId = retailerRegisteredAddressRepository.selectAddressIdByRetailerId(retailerId);
            Address retailerAddress = addressRepository.selectById(retailerAddressId);

            // StateInfo stateInfo = null;
            State stateInfo = null;
            try {

                stateInfo = stateRepository.selectByName(retailerAddress.getState());
                // stateInfo = Utils.getStateInfo(retailerAddress.getState());
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                // throw new ProfitMandiBusinessException();
            }
            DistrictMaster districtMaster = districtMasterRepository.selectByNameAndStateShortName(districtName, stateInfo.getShortName());

            fofoStore = new FofoStore();
            fofoStore.setId(retailerId);
            fofoStore.setCounterSize(updateRetailerRequest.getCountersize());
            fofoStore.setMinimumInvestment(updateRetailerRequest.getMinInvestment());

            fofoStore.setCounterPotential(updateRetailerRequest.getCounterPotential());
            fofoStore.setWarehouseId(updateRetailerRequest.getWarehouseId());

            //Check for Trial users here
            int nextStoreCodeInt = fofoStoreRepository.selectLatestStore().getCodeInt() + 1;

            String fofoStoreCode = StringUtils.generateFofoStoreSequence(
                    districtMaster.getStateShortName() + districtMaster.getShortName(), nextStoreCodeInt);
            try {
                TrialForm trialForm = trialFormRepository.selectByEmailOrMobile(updateRetailerRequest.getUserMobileNumber());
                if (trialForm != null) {
                    fofoStoreCode = "T-" + fofoStoreCode;
                    fofoStore.setTrial(true);
                    fofoStore.setTrialStart(LocalDate.now());
                    fofoStore.setTrialEnd(LocalDate.now().plusDays(31));
                    trialForm.setStatus(ProfitMandiConstants.Status.TMP_STORE_CODE_CREATED);
                    //TODO:Amit G, Send store code notification to retailer.
                }
            } catch (ProfitMandiBusinessException e) {
            }
            fofoStore.setCode(fofoStoreCode);
            fofoStore.setCodeInt(nextStoreCodeInt);
            fofoStore.setBagsLastCredited(LocalDateTime.now());
            if (retailerAddress.getName().contains(ProfitMandiConstants.COMPANY_NAME) || retailerAddress.getName().contains(ProfitMandiConstants.COMPANY_SHORT_NAME)) {
                fofoStore.setInternal(true);
            } else {
                fofoStore.setInternal(false);
            }
            fofoStore.setFofoType(FofoType.FRANCHISE);
            fofoStore.setFofoType(updateRetailerRequest.getFofoType());
            fofoStore.setActivationType(ActivationType.ACTIVE);
            fofoStore.setGstNumber(updateRetailerRequest.getGstNumber());
            fofoStoreRepository.persist(fofoStore);

            // Link store code to onboarding panel (only for NEW store creation)
            try {
                PartnerOnBoardingPanel pobp = partnerOnBoardingPanelRepository.selectByPhoneNumber(
                        Long.parseLong(retailerAddress.getPhoneNumber()));
                if (pobp != null) {
                    PartnerVerificationpanel pvp = partnerOnboardingVerificationRepository
                            .selectByOnboardingId(pobp.getId());
                    if (pvp != null && PartnerVerificationApprovalStatus.YES.equals(pvp.getApproval())) {
                        pobp.setCode(fofoStore.getCode());
                        storeTimelineTatService.onCodeCreated(pobp.getId());
                        PartnerVerificationCheckboxes cb = partnerOnboardingVerificationRepository
                                .selectVerifiCheckboxByOnboardingId(pobp.getId());
                        if (cb == null) {
                            cb = new PartnerVerificationCheckboxes();
                            cb.setOnboardingId(pobp.getId());
                            partnerOnboardingVerificationRepository.persist(cb);
                        }
                    }
                }
            } catch (Exception e) {
                LOGGER.error("Error linking store code to onboarding for retailerId: {}", retailerId, e);
            }

            PincodePartner pincodePartner = pincodePartnerRepository.selectPartnerByPincode(retailerAddress.getPinCode(), fofoStore.getId());
            if (pincodePartner == null) {
                PincodePartner pinPartner = new PincodePartner();
                pinPartner.setFofoId(fofoStore.getId());
                pinPartner.setPincode(retailerAddress.getPinCode());
                pincodePartnerRepository.perist(pinPartner);
            }

            if (fofoStore.isTrial()) {
                storeTimelineTatService.sendTrialStoreCreationMail(fofoStore, updateRetailerRequest);
                storeTimelineTatService.sendTrialActivationMail(fofoStore, updateRetailerRequest);
            }
        }

        return fofoStore;
    }

    @Override
    public FofoStore createFofoStoreCodeByUserId(int userId, String districtName, String
            stateName, UpdateRetailerRequest updateRetailerRequest) throws ProfitMandiBusinessException {
        User user = userRepository.selectById(userId);
        // = userAccountRepository.selectRetailerIdByUserId(user.getId());
        UserAccount userAccounts = userAccountRepository.selectSaholicByUserId(user.getId());
        Retailer retailer = retailerRepository.selectById(userAccounts.getAccountKey());
        Role roleFofo = roleRepository.selectByName(RoleType.FOFO.toString());
        try {
            userRoleRepository.selectByUserIdAndRoleId(user.getId(), roleFofo.getId());
        } catch (ProfitMandiBusinessException profitMandiBusinessException) {
            throw new ProfitMandiBusinessException(ProfitMandiConstants.USER_ID, user.getId(), "USR_1013");
        }
        return this.createFofoStoreCodeByRetailerId(retailer.getId(), districtName, stateName, updateRetailerRequest);

    }

    @Override
    public List<DistrictMaster> getAllDistrictMaster(String stateName) {
        // StateInfo stateInfo = null;
        State stateInfo = null;
        try {
            // stateInfo = Utils.getStateInfo(stateName);

            stateInfo = stateRepository.selectByName(stateName);
        } catch (Exception e) {
            e.printStackTrace();
            // throw new ProfitMandiBusinessException();
        }
        return districtMasterRepository.selectByStateShortName(stateInfo.getShortName());
    }

    @Override
    @Cacheable(value = "getFofoRetailer", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<Integer, CustomRetailer> getFofoRetailers(List<Integer> fofoIds) throws ProfitMandiBusinessException {
        if (fofoIds == null || fofoIds.isEmpty()) {
            return new HashMap<>();
        }

        List<com.spice.profitmandi.dao.entity.user.User> saholicUsers = userUserRepository.selectByIds(fofoIds);

        // Check for saholicUsers without corresponding dtrUser
        List<String> saholicEmails = saholicUsers.stream()
                .map(com.spice.profitmandi.dao.entity.user.User::getEmailId)
                .filter(email -> email != null)
                .collect(Collectors.toList());
        Set<String> dtrUserEmails = userRepository.selectAllByEmailIds(saholicEmails).stream()
                .map(User::getEmailId)
                .collect(Collectors.toSet());
        for (com.spice.profitmandi.dao.entity.user.User saholicUser : saholicUsers) {
            if (saholicUser.getEmailId() != null && !dtrUserEmails.contains(saholicUser.getEmailId())) {
                LOGGER.warn("dtrUser not found for saholicUser - id: {}, email: {}", saholicUser.getId(), saholicUser.getEmailId());
            }
        }

        Map<Integer, com.spice.profitmandi.dao.entity.user.User> userAddressMap = saholicUsers.stream()
                .filter(x -> x.getAddressId() != null)
                .collect(Collectors.toMap(com.spice.profitmandi.dao.entity.user.User::getAddressId, x -> x));
        List<Address> addresses = addressRepository.selectByIds(new ArrayList<>(userAddressMap.keySet()));

        // Batch fetch all FofoStores to avoid N+1 queries
        Set<Integer> retailerIds = addresses.stream().map(Address::getRetaierId).collect(Collectors.toSet());
        Map<Integer, FofoStore> fofoStoreMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(retailerIds))
                .stream().collect(Collectors.toMap(FofoStore::getId, fs -> fs));

        Map<Integer, CustomRetailer> customRetailersMap = new HashMap<>();
        for (Address address : addresses) {
            com.spice.profitmandi.dao.entity.user.User user = userAddressMap.get(address.getId());
            FofoStore fs = fofoStoreMap.get(address.getRetaierId());
            if (fs == null) {
                continue;
            }

            CustomRetailer customRetailer = buildCustomRetailer(user, address, fs);
            customRetailersMap.put(customRetailer.getPartnerId(), customRetailer);
        }
        return customRetailersMap;
    }

    /**
     * Helper method to build CustomRetailer from fetched entities
     */
    private CustomRetailer buildCustomRetailer(com.spice.profitmandi.dao.entity.user.User user, Address address,
                                               FofoStore fs) {
        CustomRetailer customRetailer = new CustomRetailer();
        customRetailer.setEmail(user.getEmailId());
        customRetailer.setBusinessName(address.getName());
        customRetailer.setMobileNumber(address.getPhoneNumber());
        customRetailer.setCode(fs.getCode());
        customRetailer.setAstId(fs.getAstId());
        customRetailer.setActivationType(fs.getActivationType());
        customRetailer.setCounterSize(fs.getCounterSize());
        customRetailer.setCounterPotential(fs.getCounterPotential());
        customRetailer.setWarehouseId(fs.getWarehouseId());
        customRetailer.setPartnerId(fs.getId());
        customRetailer.setFofoType(fs.getFofoType());
        customRetailer.setCartId(user.getActiveCartId());

        customRetailer.setGstNumber(fs.getGstNumber());

        CustomAddress customAddress = buildCustomAddress(address);
        customRetailer.setAddress(customAddress);
        customRetailer.setDisplayName(customRetailer.getBusinessName() + "-" + customRetailer.getCode() + "- " + customAddress.getCity());
        return customRetailer;
    }

    /**
     * Helper method to build CustomAddress from Address entity
     */
    private CustomAddress buildCustomAddress(Address address) {
        CustomAddress customAddress = new CustomAddress();
        customAddress.setCity(address.getCity());
        customAddress.setState(address.getState());
        customAddress.setLine1(address.getLine1());
        customAddress.setLine2(address.getLine2());
        customAddress.setPinCode(address.getPinCode());
        customAddress.setName(address.getName());
        customAddress.setPhoneNumber(address.getPhoneNumber());
        return customAddress;
    }

    @Override
    @Cacheable(value = "getFofoRetailer", cacheManager = "thirtyMinsTimeOutCacheManager")
    public CustomRetailer getFofoRetailer(int fofoId) throws ProfitMandiBusinessException {
        com.spice.profitmandi.dao.entity.user.User saholicUser = userUserRepository.selectById(fofoId);
        User dtrUser = null;
        try {
            dtrUser = userRepository.selectByEmailId(saholicUser.getEmailId());
        } catch (ProfitMandiBusinessException e) {
            LOGGER.info("User not found for emailId: {} and id is : {}", saholicUser.getEmailId(), saholicUser.getId());
        }
        FofoStore store = fofoStoreRepository.selectByRetailerId(fofoId);
        Address address = addressRepository.selectById(saholicUser.getAddressId());

        CustomRetailer customRetailer = new CustomRetailer();
        customRetailer.setEmail(saholicUser.getEmailId());
        customRetailer.setBusinessName(address.getName());
        customRetailer.setMobileNumber(address.getPhoneNumber());
        customRetailer.setFirstName(dtrUser.getFirstName());
        customRetailer.setLastName(dtrUser.getLastName());
        customRetailer.setCartId(saholicUser.getActiveCartId());
        customRetailer.setPartnerId(address.getRetaierId());
        customRetailer.setCode(store.getCode());
        customRetailer.setAstId(store.getAstId());
        customRetailer.setActivationType(store.getActivationType());
        customRetailer.setWarehouseId(store.getWarehouseId());

        customRetailer.setGstNumber(store.getGstNumber());

        // Reuse helper method for CustomAddress
        CustomAddress customAddress = buildCustomAddress(address);
        customRetailer.setAddress(customAddress);
        customRetailer.setDisplayName(address.getName() + " - " + address.getCity());

        return customRetailer;
    }

    @Override
    public Map<Integer, CustomRetailer> getFofoRetailerUserId(List<Integer> fofoIds) throws ProfitMandiBusinessException {
        if (fofoIds == null || fofoIds.isEmpty()) {
            return new HashMap<>();
        }

        List<com.spice.profitmandi.dao.entity.user.User> saholicUsers = userUserRepository.selectByIds(fofoIds);
        Map<Integer, com.spice.profitmandi.dao.entity.user.User> userAddressMap = saholicUsers.stream()
                .filter(x -> x.getAddressId() != null)
                .collect(Collectors.toMap(com.spice.profitmandi.dao.entity.user.User::getAddressId, x -> x));
        List<Address> addresses = addressRepository.selectByIds(new ArrayList<>(userAddressMap.keySet()));

        // Batch fetch all retailerIds to userIds mapping to avoid N+1
        Set<Integer> retailerIds = addresses.stream().map(Address::getRetaierId).collect(Collectors.toSet());
        Map<Integer, Integer> retailerIdToUserIdMap = getUserIdRetailerIdMapReverse(retailerIds);

        // Batch fetch FofoStores for GST numbers
        Map<Integer, FofoStore> fofoStoreMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(retailerIds))
                .stream().collect(Collectors.toMap(FofoStore::getId, fs -> fs));

        Map<Integer, CustomRetailer> customRetailersMap = new HashMap<>();
        for (Address address : addresses) {
            com.spice.profitmandi.dao.entity.user.User user = userAddressMap.get(address.getId());
            Integer userId = retailerIdToUserIdMap.get(user.getId());
            if (userId == null) {
                continue;
            }

            CustomRetailer customRetailer = new CustomRetailer();
            customRetailer.setEmail(user.getEmailId());
            customRetailer.setBusinessName(address.getName());
            customRetailer.setMobileNumber(address.getPhoneNumber());
            customRetailer.setCartId(user.getActiveCartId());
            customRetailer.setPartnerId(address.getRetaierId());

            FofoStore fs = fofoStoreMap.get(address.getRetaierId());
            customRetailer.setGstNumber(fs != null ? fs.getGstNumber() : null);

            CustomAddress customAddress = buildCustomAddress(address);
            customRetailer.setAddress(customAddress);
            customRetailer.setDisplayName(customRetailer.getBusinessName() + "-" + customAddress.getCity());
            customRetailersMap.put(userId, customRetailer);
        }
        return customRetailersMap;
    }

    /**
     * Helper method to get retailerId to userId mapping
     */
    private Map<Integer, Integer> getUserIdRetailerIdMapReverse(Set<Integer> retailerIds) {
        List<UserAccount> userAccounts = userAccountRepository.selectAllSaholicByRetailerIds(retailerIds);
        return userAccounts.stream().collect(Collectors.toMap(UserAccount::getAccountKey, UserAccount::getUserId));
    }

    @Override
    public Map<Integer, String> getAllFofoRetailerIdEmailIdMap() throws ProfitMandiBusinessException {
        Role roleFofo = null;
        try {
            roleFofo = roleRepository.selectByName(RoleType.FOFO.toString());
        } catch (ProfitMandiBusinessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Role roleRetailer = null;
        try {
            roleRetailer = roleRepository.selectByName(RoleType.RETAILER.toString());
        } catch (ProfitMandiBusinessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Set<Integer> roleIds = new HashSet<>();
        roleIds.add(roleFofo.getId());
        roleIds.add(roleRetailer.getId());
        List<Integer> userIds = userRoleRepository.selectUserIdsByRoleIds(roleIds);
        List<User> users = userRepository.selectAllByIds(new HashSet<>(userIds));
        Map<Integer, Integer> userIdRetailerIdMap = this.getUserIdRetailerIdMap(userIds);
        Map<Integer, String> retailerIdEmailIdMap = new HashMap<>();
        for (User user : users) {
            retailerIdEmailIdMap.put(userIdRetailerIdMap.get(user.getId()), user.getEmailId());
        }
        return retailerIdEmailIdMap;
    }

    private Map<Integer, Integer> getUserIdRetailerIdMap(List<Integer> userIds) {
        List<UserAccount> userAccounts = userAccountRepository.selectAllSaholicByUserIds(new HashSet<>(userIds));
        Map<Integer, Integer> userIdRetailerIdMap = new HashMap<>();
        for (UserAccount userAccount : userAccounts) {
            userIdRetailerIdMap.put(userAccount.getUserId(), userAccount.getAccountKey());
        }
        return userIdRetailerIdMap;
    }

    private Map<Integer, String> getUserIdEmailIdMap(Set<Integer> userIds) throws ProfitMandiBusinessException {
        List<User> users = userRepository.selectAllByIds(userIds);
        Map<Integer, String> userIdEmailIdMap = new HashMap<>();
        for (User user : users) {
            userIdEmailIdMap.put(user.getId(), user.getEmailId());
        }
        return userIdEmailIdMap;
    }

    @Override
    public Map<Integer, String> getAllFofoRetailerIdEmailIdMap(Set<Integer> retailerIds) throws ProfitMandiBusinessException {
        List<UserAccount> userAccounts = userAccountRepository.selectAllSaholicByRetailerIds(retailerIds);
        Set<Integer> userIds = new HashSet<>();
        for (UserAccount userAccount : userAccounts) {
            userIds.add(userAccount.getUserId());
        }
        Map<Integer, String> retailerIdEmailIdMap = new HashMap<>();
        Map<Integer, String> userIdEmailIdMap = this.getUserIdEmailIdMap(userIds);
        for (UserAccount userAccount : userAccounts) {
            retailerIdEmailIdMap.put(userAccount.getAccountKey(), userIdEmailIdMap.get(userAccount.getUserId()));
        }
        return retailerIdEmailIdMap;
    }

    @Override
    public Map<Integer, String> getAllFofoRetailerIdNameMap(List<Integer> storeIds) throws ProfitMandiBusinessException {
        Map<Integer, CustomRetailer> retailersMap = this.getFofoRetailers(storeIds);
        Map<Integer, String> retailerIdNameMap = new HashMap<>();
        for (Map.Entry<Integer, CustomRetailer> entry : retailersMap.entrySet()) {
            retailerIdNameMap.put(entry.getKey(), entry.getValue().getBusinessName() + "-" + entry.getValue().getAddress().getCity());
        }
        return retailerIdNameMap;
    }

    @Override
    @Cacheable(value = "FofoRetailerIdNameMap", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<Integer, String> getAllFofoRetailerIdNameMap() throws ProfitMandiBusinessException {
        List<FofoStore> stores = fofoStoreRepository.selectAll();
        List<Integer> storeIds = stores.stream().map(x -> x.getId()).collect(Collectors.toList());
        return this.getAllFofoRetailerIdNameMap(storeIds);
    }

    @Override
    public List<MapWrapper<Integer, String>> getAllFofoRetailerIdNameList() throws ProfitMandiBusinessException {
        List<MapWrapper<Integer, String>> mapWrappers = new ArrayList<>();
        // TODO Auto-generated method stub
        Map<Integer, String> fofoIdNameMap = this.getAllFofoRetailerIdNameMap();
        fofoIdNameMap.forEach((fofoId, name) -> {
            MapWrapper<Integer, String> idWrapper = new MapWrapper<>();
            idWrapper.setKey(fofoId);
            idWrapper.setValue(name);
            mapWrappers.add(idWrapper);
        });
        return mapWrappers;
    }

    @Override
    @Cacheable(value = "retailerNames", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<Integer, CustomRetailer> getFofoRetailers(boolean activeOnly) throws ProfitMandiBusinessException {
        Stream<FofoStore> storeStream = fofoStoreRepository.selectAll().stream()
                .filter(fs -> fs.getFofoType().equals(FofoType.FRANCHISE));
        if (activeOnly) {
            storeStream = storeStream.filter(FofoStore::isActive);
        }
        List<Integer> storeIds = storeStream.map(FofoStore::getId).collect(Collectors.toList());
        return this.getFofoRetailers(storeIds);
    }

    @Override
    @Cacheable(value = "retailerNames", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<Integer, CustomRetailer> getFofoRetailers(boolean activeOnly, String pinCode) throws ProfitMandiBusinessException {
        List<PincodePartner> pincodePartners = pincodePartnerRepository.selectPartnersByPincode(pinCode);
        List<Integer> storeIds = new ArrayList<>();
        if (!pincodePartners.isEmpty()) {
            List<Integer> fofoIds = pincodePartners.stream().map(PincodePartner::getFofoId).collect(Collectors.toList());
            List<FofoStore> fofoStores = fofoStoreRepository.selectActivePartnersByRetailerIds(fofoIds);
            storeIds = fofoStores.stream().map(FofoStore::getId).collect(Collectors.toList());
        }
        return this.getFofoRetailers(storeIds);
    }

    @Override
    @Cacheable(value = "allFofoRetailers", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<Integer, CustomRetailer> getAllFofoRetailers() throws ProfitMandiBusinessException {
        List<Integer> storeIds = fofoStoreRepository.selectAll().stream()
                .map(FofoStore::getId)
                .collect(Collectors.toList());
        return this.getFofoRetailers(storeIds);
    }

    @Override
    @Cacheable(value = "allFofoRetailersInternalFalse", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<Integer, CustomRetailer> getAllFofoRetailersInternalFalse() throws ProfitMandiBusinessException {
        List<Integer> storeIds = fofoStoreRepository.selectAll().stream()
                .filter(fs -> fs.getFofoType().equals(FofoType.FRANCHISE))
                .map(FofoStore::getId)
                .collect(Collectors.toList());
        return this.getFofoRetailers(storeIds);
    }

    @Override
    public LoginRequestResponseModel registerWebUser(LoginRequestResponseModel loginRequestModel) throws
            ProfitMandiBusinessException {

        com.spice.profitmandi.dao.entity.user.User saholicUser = this.createSaholicUser(loginRequestModel);
        loginRequestModel.setUserId(saholicUser.getId());

        this.createRetailerAddress(loginRequestModel);

        this.createPrivateDealUser(loginRequestModel);
        this.updateSaholicUser(saholicUser.getId(), loginRequestModel.getBusinessAddress().getId());

        return loginRequestModel;

    }

    private void createPrivateDealUser(LoginRequestResponseModel loginRequestModel) {

        Integer counterId = this.createCounter(loginRequestModel.getEmail(), loginRequestModel.getGstNumber(), loginRequestModel.getBusinessAddress().getPhoneNumber(), loginRequestModel.getBusinessAddress().getName(), loginRequestModel.getBusinessAddress().getId());
        try {
            this.createPrivateDealUser(loginRequestModel.getUserId(), counterId, false);
        } catch (Exception e) {
            LOGGER.error("ERROR : ", e);
        }

        PrivateDealUserAddressMapping privateDealUserAddressMapping = new PrivateDealUserAddressMapping();
        privateDealUserAddressMapping.setUserId(loginRequestModel.getUserId());
        privateDealUserAddressMapping.setAddressId(loginRequestModel.getBusinessAddress().getId());
        privateDealUserAddressMappingRepository.persist(privateDealUserAddressMapping);

    }

    private void createRetailerAddress(LoginRequestResponseModel loginRequestResponseModel) throws
            ProfitMandiBusinessException {
        Address businessAddress = loginRequestResponseModel.getBusinessAddress();
        businessAddress.setRetaierId(loginRequestResponseModel.getUserId());
        addressRepository.persist(businessAddress);
    }

    private String getHash256(String originalString) {
        String hashString = Hashing.sha256().hashString(originalString, StandardCharsets.UTF_8).toString();
        LOGGER.info("Hash String {}", hashString);
        return hashString;
    }

    @Override
    @Cacheable(value = "fofoIdUrl", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<Integer, String> getAllFofoRetailerIdUrlMap() {
        Map<Integer, String> fofoRetailerUrlMap = new HashMap<>();
        List<FofoStore> stores = fofoStoreRepository.selectAll().stream()
                .filter(FofoStore::isActive)
                .collect(Collectors.toList());

        // Batch fetch all district masters to avoid N+1 queries
        List<DistrictMaster> allDistrictMasters = districtMasterRepository.selectAll();
        Map<String, DistrictMaster> districtMasterMap = allDistrictMasters.stream()
                .collect(Collectors.toMap(
                        dm -> dm.getStateShortName() + "_" + dm.getShortName(),
                        dm -> dm,
                        (existing, replacement) -> existing));

        // Get fallback district master
        DistrictMaster fallbackDistrict = districtMasterMap.get("HR_FB");

        for (FofoStore store : stores) {
            String codeWithoutDigits = store.getCode().replaceAll("\\d+", "");
            if (codeWithoutDigits.length() < 3) {
                LOGGER.warn("Invalid store code format: {}", store.getCode());
                continue;
            }
            String stateShortName = codeWithoutDigits.substring(0, 2);
            String districtShortName = codeWithoutDigits.substring(2);

            // Use pre-fetched map instead of N+1 query
            String key = stateShortName + "_" + districtShortName;
            DistrictMaster districtMaster = districtMasterMap.getOrDefault(key, fallbackDistrict);

            if (districtMaster == null) {
                LOGGER.warn("No district master found for key: {} and no fallback available", key);
                continue;
            }

            String urlString = districtMaster.getStateShortName() + "/" +
                    Utils.getHyphenatedString(districtMaster.getName()) + "/" + store.getCode();
            fofoRetailerUrlMap.put(store.getId(), urlString.toLowerCase());
        }
        return fofoRetailerUrlMap;
    }

    @Override
    @Cacheable(value = "storeCodeRetailerMap", cacheManager = "thirtyMinsTimeOutCacheManager")
    public Map<String, Integer> getStoreCodeRetailerMap() {
        Map<Integer, String> map = this.getAllFofoRetailerIdUrlMap();
        Map<String, Integer> returnMap = map.entrySet().stream().collect(Collectors.toMap(x -> {
            String[] splitString = x.getValue().split("/");
            return splitString[splitString.length - 1];
        }, x -> x.getKey()));
        LOGGER.info("returnMap {}", returnMap);
        return returnMap;
    }


    @Override
    public User getEmail() {
        return null;
    }

    @Override
    public void update(com.spice.profitmandi.dao.entity.user.User user) {
    }

    @Override
    public void updateRetailerEmail(int fofoId, String newEmail) throws ProfitMandiBusinessException {
        User dtrUser = null;
        com.spice.profitmandi.dao.entity.user.User user = null;

        try {
            dtrUser = userRepository.selectByEmailId(newEmail);
        } catch (Exception e) {
        }

        try {
            user = userUserRepository.selectByEmailId(newEmail);
        } catch (Exception e) {
        }

        if (user != null || dtrUser != null) {
            throw new ProfitMandiBusinessException("Email", newEmail, "Email already exist");
        }
        com.spice.profitmandi.dao.entity.user.User fofoUser = userUserRepository.selectById(fofoId);
        String oldEmail = fofoUser.getEmailId();
        LOGGER.info("old email {}", oldEmail);
        User thisDtrUser = userRepository.selectByEmailId(oldEmail);

        fofoUser.setEmailId(newEmail);
        thisDtrUser.setEmailId(newEmail);
    }


    //    @Override
    @Cacheable(value = "getContactsByFofoId", cacheManager = "oneDayCacheManager")
    public List<RetailerContact> getContactsByFofoId(int fofoId, boolean activeOnly) throws
            ProfitMandiBusinessException {
        List<RetailerContact> retailerContacts = retailerContactRepository.selectAllByRetailerId(fofoId, activeOnly);
        com.spice.profitmandi.dao.entity.user.User user = userUserRepository.selectById(fofoId);
        RetailerContact retailerContact = new RetailerContact();
        retailerContact.setMobile(user.getMobileNumber());
        retailerContact.setName(user.getName());
        retailerContact.setActive(true);
        retailerContact.setFofoId(fofoId);
        retailerContacts.add(retailerContact);
        return retailerContacts;
    }


    @Override
    public Map<Integer, CustomRetailer> getFofoRetailersPaginated(boolean activeOnly, int offset,
                                                                  int limit, FofoType fofoType) throws ProfitMandiBusinessException {

        Stream<FofoStore> storeStream = fofoStoreRepository.selectByStatusFofoType(activeOnly, fofoType, offset, limit).stream();

        List<Integer> storeIds = storeStream.map(x -> x.getId()).collect(Collectors.toList());
        return this.getFofoRetailers(storeIds);
    }

    @Override
    public Map<String, Object> computeIncomeMap(int fofoId, int yearMonth) {
        LocalDateTime startOfMonth = LocalDate.now().minusMonths(yearMonth).withDayOfMonth(1).atStartOfDay();
        LocalDateTime endOfMonth = startOfMonth.plusMonths(1);
        YearMonth monthYear = YearMonth.now();


        // 3) Fetch brand-wise data (same as original)
        List<LastMonthCreditedIncomeModel> lastMonthPendingIncomeModels = schemeInOutRepository
                .selectLastMonthPendingIncomeByFofoId(fofoId, startOfMonth, endOfMonth);

        List<LastMonthCreditedIncomeModel> lastMonthPurchaseInMargins = schemeInOutRepository
                .selectLastMonthPurchaseInMarginByFofoId(fofoId, startOfMonth, endOfMonth);

        List<LastMonthCreditedIncomeModel> lastMonthFrontEndIncomes = schemeInOutRepository
                .selectFrontIncomeByBrand(fofoId, startOfMonth, endOfMonth);

        List<LastMonthCreditedIncomeModel> lastMonthSaleMargins = schemeInOutRepository
                .selectLastMonthCreditedIncomeByFofoId(fofoId, startOfMonth, endOfMonth);

        List<OfferPayoutImeiIncomeModel> offerPayoutImeiIncomeModels = offerPayoutRepository
                .getTotalPayoutsByPartnerPeriod(
                        YearMonth.of(startOfMonth.getYear(), startOfMonth.getMonth()),
                        fofoId, null, null);

        // 4) Convert to maps for brand-wise access
        Map<String, LastMonthCreditedIncomeModel> lastMonthPendingIncomeMap =
                lastMonthPendingIncomeModels.stream()
                        .collect(Collectors.toMap(x -> x.getBrand(), x -> x));

        Map<String, LastMonthCreditedIncomeModel> lastMonthPurchaseInMarginMap =
                lastMonthPurchaseInMargins.stream()
                        .collect(Collectors.toMap(x -> x.getBrand(), x -> x));

        Map<String, LastMonthCreditedIncomeModel> lastMonthFrontEndIncomeMap =
                lastMonthFrontEndIncomes.stream()
                        .collect(Collectors.toMap(x -> x.getBrand(), x -> x));

        Map<String, LastMonthCreditedIncomeModel> lastMonthSaleMarginMap =
                lastMonthSaleMargins.stream()
                        .collect(Collectors.toMap(x -> x.getBrand(), x -> x));

        Map<String, Double> additionalPurchasePayout = offerPayoutImeiIncomeModels.stream()
                .collect(Collectors.groupingBy(OfferPayoutImeiIncomeModel::getBrand,
                        Collectors.summingDouble(OfferPayoutImeiIncomeModel::getPurchasePayout)));

        Map<String, Double> additionSalePayout = offerPayoutImeiIncomeModels.stream()
                .collect(Collectors.groupingBy(OfferPayoutImeiIncomeModel::getBrand,
                        Collectors.summingDouble(OfferPayoutImeiIncomeModel::getSalePayout)));

        // 5) Get all unique brands
        Set<String> brandSet = new HashSet<>();
        brandSet.addAll(lastMonthPurchaseInMarginMap.keySet());
        brandSet.addAll(lastMonthSaleMarginMap.keySet());
        brandSet.addAll(lastMonthPendingIncomeMap.keySet());
        brandSet.addAll(additionalPurchasePayout.keySet());
        brandSet.addAll(additionSalePayout.keySet());
        brandSet.addAll(lastMonthFrontEndIncomeMap.keySet());

        // 6) Calculate total amount per brand (same logic as original)
        Map<String, Float> totalAmountMap = new HashMap<>();
        brandSet.forEach(brand -> {
            float total =
                    (lastMonthSaleMarginMap.get(brand) == null ? 0 : lastMonthSaleMarginMap.get(brand).getAmount()) +
                            (lastMonthPurchaseInMarginMap.get(brand) == null ? 0 : lastMonthPurchaseInMarginMap.get(brand).getAmount()) +
                            (lastMonthPendingIncomeMap.get(brand) == null ? 0 : lastMonthPendingIncomeMap.get(brand).getAmount()) +
                            (additionalPurchasePayout.get(brand) == null ? 0 : additionalPurchasePayout.get(brand).floatValue()) +
                            (additionSalePayout.get(brand) == null ? 0 : additionSalePayout.get(brand).floatValue()) +
                            (lastMonthFrontEndIncomeMap.get(brand) == null ? 0 : lastMonthFrontEndIncomeMap.get(brand).getAmount());
            totalAmountMap.put(brand, total);
        });

        // 7) Calculate aggregated totals
        float totalIncome = totalAmountMap.values().stream()
                .reduce(0f, Float::sum);

        float pendingIncome = lastMonthPendingIncomeMap.values().stream()
                .map(LastMonthCreditedIncomeModel::getAmount)
                .reduce(0f, Float::sum);

        float creditedIncome = totalIncome - pendingIncome;

        // 8) Build month labels
        Map<Integer, String> monthValueMap = new HashMap<>();
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MMM''uu");
        for (int i = 0; i <= 5; i++) {
            LocalDateTime m = LocalDateTime.now()
                    .withDayOfMonth(1)
                    .minusMonths(i);
            monthValueMap.put(i, m.format(fmt));
        }

        // 9) Assemble result with all brand-wise data
        Map<String, Object> result = new HashMap<>();
        result.put("totalIncome", totalIncome);
        result.put("creditedIncome", creditedIncome);
        result.put("pendingIncome", pendingIncome);
        result.put("monthIndex", yearMonth);
        result.put("monthValueMap", monthValueMap);

        // Include brand-wise data for detailed analysis
        result.put("brandSet", brandSet);
        result.put("totalAmountMap", totalAmountMap);
        result.put("lastMonthPurchaseInMarginMap", lastMonthPurchaseInMarginMap);
        result.put("lastMonthSaleMarginMap", lastMonthSaleMarginMap);
        result.put("lastMonthPendingIncomeMap", lastMonthPendingIncomeMap);
        result.put("additionalPurchasePayoutMap", additionalPurchasePayout);
        result.put("additionalSalePayoutMap", additionSalePayout);
        result.put("lastMonthFrontEndIncomeMap", lastMonthFrontEndIncomeMap);

        return result;
    }

}