Subversion Repositories SmartDukaan

Rev

Rev 35458 | Rev 35505 | Go to most recent revision | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.spice.profitmandi.web.controller;

import com.jcraft.jsch.*;
import com.spice.profitmandi.common.enumuration.MessageType;
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
import com.spice.profitmandi.common.model.CustomRetailer;
import com.spice.profitmandi.common.model.ProfitMandiConstants;
import com.spice.profitmandi.common.model.SendNotificationModel;
import com.spice.profitmandi.common.util.FormattingUtils;
import com.spice.profitmandi.common.web.util.ResponseSender;
import com.spice.profitmandi.dao.entity.catalog.BrandCatalog;
import com.spice.profitmandi.dao.entity.catalog.Offer;
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
import com.spice.profitmandi.dao.entity.fofo.PartnerType;
import com.spice.profitmandi.dao.enumuration.catalog.ItemCriteriaType;
import com.spice.profitmandi.dao.enumuration.catalog.OfferSchemeType;
import com.spice.profitmandi.dao.model.CreateOfferRequest;
import com.spice.profitmandi.dao.model.ItemCriteriaPayout;
import com.spice.profitmandi.dao.model.TodayOfferModel;
import com.spice.profitmandi.dao.repository.catalog.*;
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
import com.spice.profitmandi.dao.repository.dtr.UserAccountRepository;
import com.spice.profitmandi.service.NotificationService;
import com.spice.profitmandi.service.authentication.RoleManager;
import com.spice.profitmandi.service.catalog.BrandsService;
import com.spice.profitmandi.service.offers.ItemCriteria;
import com.spice.profitmandi.service.offers.OfferService;
import com.spice.profitmandi.service.offers.TodayOfferService;
import com.spice.profitmandi.service.user.RetailerService;
import com.spice.profitmandi.web.model.LoginDetails;
import com.spice.profitmandi.web.util.CookiesProcessor;
import com.spice.profitmandi.web.util.MVCResponseSender;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
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.Value;
import org.springframework.cache.CacheManager;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.xhtmlrenderer.swing.Java2DRenderer;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

@Controller
@Transactional(rollbackFor = Throwable.class)
public class OfferController {
    private static final Logger LOGGER = LogManager.getLogger(OfferController.class);
    private static final String IMAGE_REMOTE_DIR = "/var/www/dtrdashboard/uploads/campaigns/";
    private static final String IMAGE_STATIC_SERVER_URL = "https://images.smartdukaan.com/uploads/campaigns";
    @Autowired
    UserAccountRepository userAccountRepository;
    @Autowired
    RoleManager roleManager;
    @Autowired
    private OfferRepository offerRepository;
    @Autowired
    private OfferMarginRepository offerMarginRepository;
    @Autowired
    private FofoStoreRepository fofoStoreRepository;
    @Autowired
    private ResponseSender responseSender;
    @Autowired
    private ViewResolver viewResolver;
    @Autowired
    private OfferPartnerRepository offerPartnerRepository;
    @Autowired
    private ItemRepository itemRepository;
    @Autowired
    private MVCResponseSender mvcResponseSender;
    @Autowired
    private RetailerService retailerService;
    @Autowired
    private NotificationService notificationService;
    @Autowired
    private CookiesProcessor cookiesProcessor;
    @Autowired
    private OfferService offerService;
    @Autowired
    private CacheManager oneDayCacheManager;

    @Autowired
    BrandsService brandsService;

    @Autowired
    private CatalogRepository catalogRepository;

    @Autowired
    TodayOfferService todayOfferService;

    @RequestMapping(value = "/getCreateOffer", method = RequestMethod.GET)
    public String getCreateOffer(HttpServletRequest request, Model model) throws ProfitMandiBusinessException {
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
        List<Integer> fofoIds = fofoStoreRepository.selectActiveStores().stream().map(x -> x.getId())
                .collect(Collectors.toList());

        Set<String> brands = brandsService.getBrandsToDisplay(3).stream().map(x -> x.getName()).collect(Collectors.toSet());
        brands.addAll(itemRepository.selectAllBrands(ProfitMandiConstants.LED_CATEGORY_ID));
        brands.addAll(itemRepository.selectAllBrands(ProfitMandiConstants.SMART_WATCH_CATEGORY_ID));
        //Lets allow demo
        brands.add("Live Demo");

        Map<Integer, CustomRetailer> customRetailerMap = retailerService.getAllFofoRetailers();

        Map<Integer, CustomRetailer> customRetailersMap = fofoIds.stream().map(x -> customRetailerMap.get(x))
                .filter(x -> x != null).collect(Collectors.toList()).stream()
                .collect(Collectors.toMap(x -> x.getPartnerId(), x -> x));

        model.addAttribute("customRetailersMap", customRetailersMap);
        model.addAttribute("itemCriteriaType", ItemCriteriaType.values());
        model.addAttribute("brands", brands);
        model.addAttribute("partnerCategories", PartnerType.values());
        model.addAttribute("warehouseRegion", ProfitMandiConstants.WAREHOUSE_MAP);
        return "scheme_offer";

    }

    @RequestMapping(value = "/createOffer", method = RequestMethod.POST)
    public String createOffer(HttpServletRequest request, @RequestBody CreateOfferRequest createOfferRequest,
                              Model model) throws Exception {
        LOGGER.info("createOfferRequest [{}]", createOfferRequest);
        offerService.addOfferService(createOfferRequest);
        oneDayCacheManager.getCache("allOffers").evict(YearMonth.from(createOfferRequest.getStartDate()));
        model.addAttribute("response1", mvcResponseSender.createResponseString(true));
        return "response";

    }

    @RequestMapping(value = "/offers/published", method = RequestMethod.GET)
    public String getPublishedOffers(HttpServletRequest request, @RequestParam int fofoId, Model model)
            throws Exception {
        LOGGER.info("Published");
        offerService.getPublishedOffers(fofoId, YearMonth.from(LocalDateTime.now()));
        return "scheme_offer/published";

    }

    @Value("${prod}")
    private boolean isProd;

    @RequestMapping(value = "/offer/active/{offerId}", method = RequestMethod.GET)
    public String activateOffer(HttpServletRequest request, @PathVariable(name = "offerId") String offerIdsString,
                                Model model, @RequestParam(defaultValue = "true") boolean active)
            throws ProfitMandiBusinessException, Exception {
        List<Integer> offerIds = Arrays.stream(offerIdsString.split(",")).map(x -> Integer.parseInt(x))
                .collect(Collectors.toList());
        List<Offer> offers = offerRepository.selectAllByIds(offerIds);

        //Consider only offers that have opposite status
        offers = offers.stream().filter(x -> x.isActive() != active).collect(Collectors.toList());

        Set<YearMonth> yearMonthsToEvict = new HashSet<>();
        for (Offer offer : offers) {
            offer.setActive(active);
            yearMonthsToEvict.add(YearMonth.from(offer.getStartDate()));
        }
        //Evict caches
        for (YearMonth ymToEvict : yearMonthsToEvict) {
            oneDayCacheManager.getCache("catalog.published_yearmonth").evict(ymToEvict);
            oneDayCacheManager.getCache("allOffers").evict(ymToEvict);
        }
        if (active) {
            for (Offer offer : offers) {
                this.sendNotification(offer);
            }
        }


        model.addAttribute("response1", mvcResponseSender.createResponseString(true));
        return "response";
    }

    @RequestMapping(value = "/offer/testimage/{offerId}", method = RequestMethod.GET)
    public String testOffer(HttpServletRequest request, @PathVariable int offerId, Model model,
                            @RequestParam(defaultValue = "true") boolean active) throws ProfitMandiBusinessException, Exception {
        Offer offer = offerRepository.selectById(offerId);
        // model.addAttribute("response1", mvcResponseSender.createResponseString(true));
        // return "response";
        CreateOfferRequest createOfferRequest = offerService.getCreateOfferRequest(offer);
        Map<String, Object> model1 = new HashMap<>();
        model1.put("offer", createOfferRequest);
        model1.put("lessThan", "<");
        String htmlContent = this.getContentFromTemplate("offer_margin_detail_notify", model1);
        model.addAttribute("response1", htmlContent);
        return "response";
    }

    private void sendNotification(Offer offer) throws Exception {
        if (!YearMonth.from(offer.getStartDate()).equals(YearMonth.now())) {
            return;
        }
        String fileName = "offer-" + offer.getId() + ".png";
        //String htmlFileName = fileName.replace("png", "html");
        CreateOfferRequest createOfferRequest = offerService.getCreateOfferRequest(offer);
        boolean isLiveDemo = createOfferRequest.getTargetSlabs().stream()
                .map(x -> x.getItemCriteriaPayouts())
                .flatMap(List::stream)
                .map(ItemCriteriaPayout::getItemCriteria)
                .map(ItemCriteria::getCatalogIds)
                .flatMap(List::stream)
                .anyMatch(catalogId -> catalogRepository.selectCatalogById(catalogId).getBrand().equals("Live Demo"));
        if (!isLiveDemo) {
            SendNotificationModel sendNotificationModel = new SendNotificationModel();
            sendNotificationModel.setCampaignName("SchemeOffer");
            sendNotificationModel.setTitle(offer.getName());
            sendNotificationModel.setMessage(createOfferRequest.getSchemeType().name() + " of select models, "
                    + FormattingUtils.formatDateMonth(offer.getStartDate()) + " to "
                    + FormattingUtils.formatDateMonth(offer.getEndDate()));
            sendNotificationModel.setType("url");
            String imageUrl = IMAGE_STATIC_SERVER_URL + "/" + "image" + LocalDate.now() + "/" + fileName;
            sendNotificationModel.setImageUrl(imageUrl);
            sendNotificationModel.setUrl("https://app.smartdukaan.com/pages/home/notifications");
            sendNotificationModel.setExpiresat(LocalDateTime.now().plusDays(1));
            sendNotificationModel.setMessageType(MessageType.scheme);
            //Map<Integer, List<Offer>> offersMap = offerRepository.selectAllPublishedMapByPartner(YearMonth.now());

            Map<String, InputStream> fileStreamsMap = new HashMap<>();
            Map<String, Object> model = new HashMap<>();
            model.put("offer", createOfferRequest);
            String htmlContent = this.getContentFromTemplate("offer_margin_detail_notify", model);
            LOGGER.info("this.getContentFromTemplate {}", htmlContent);
            fileStreamsMap.put(fileName, this.getImageBuffer(htmlContent));
            // fileStreamsMap.put(htmlFileName, new
            // ByteArrayInputStream(htmlContent.getBytes()));
            List<Integer> fofoIds = null;
            if (isProd) {
                this.uploadFile(fileStreamsMap);
            }

            List<Integer> fofoIdSet = new ArrayList<>(offerRepository.getEligibleFofoIds(offer));
            //LOGGER.info(fofoIdSet);
            List<Integer> userIds = userAccountRepository.selectUserIdsByRetailerIds(new ArrayList<>(fofoIdSet));
            sendNotificationModel.setUserIds(userIds);
            notificationService.sendNotification(sendNotificationModel);
            sendWhatsapp(offer, fofoIds, imageUrl);
        }
    }

    private void sendWhatsapp(Offer offer, List<Integer> fofoIds, String imageUrl) throws Exception {
        offerService.sendWhatsapp(offer, fofoIds, imageUrl);
    }

    private InputStream asInputStream(BufferedImage bi) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(bi, "png", baos);
        return new ByteArrayInputStream(baos.toByteArray());

    }

    private ChannelSftp setupJsch() throws JSchException {
        JSch jsch = new JSch();
        Session jschSession = jsch.getSession("root", "172.105.58.16");
        // Session jschSession = jsch.getSession("root", "173.255.254.24");
        LOGGER.info("getClass().getResource(\"id_rsa\") {}",
                getClass().getClassLoader().getResource("id_rsa").getPath());
        jsch.addIdentity(getClass().getClassLoader().getResource("id_rsa").getPath());
        // jschSession.setPassword("spic@2015static0");
        jschSession.setConfig("StrictHostKeyChecking", "no");
        jschSession.connect();
        return (ChannelSftp) jschSession.openChannel("sftp");
    }

    private void fileUpload(ChannelSftp channelSftp, Map<String, InputStream> streamsFileMap, String destinationPath)
            throws SftpException, FileNotFoundException {

        channelSftp.cd(destinationPath);
        String folderName = "image" + LocalDate.now();

        channelSftp.cd(destinationPath);
        SftpATTRS attrs = null;

        // check if the directory is already existing
        try {
            attrs = channelSftp.stat(folderName);
        } catch (Exception e) {
            System.out.println(destinationPath + "/" + folderName + " not found");
        }

        // else create a directory
        if (attrs == null) {
            channelSftp.mkdir(folderName);
            channelSftp.chmod(0755, ".");
        }
        channelSftp.cd(folderName);

        for (Map.Entry<String, InputStream> streamsFileEntry : streamsFileMap.entrySet()) {
            channelSftp.put(streamsFileEntry.getValue(), streamsFileEntry.getKey(), ChannelSftp.OVERWRITE);
        }

    }

    private void uploadFile(Map<String, InputStream> fileStreamsMap) throws Exception {
        ChannelSftp channelSftp = setupJsch();
        channelSftp.connect();
        this.fileUpload(channelSftp, fileStreamsMap, IMAGE_REMOTE_DIR + "");
        channelSftp.exit();
    }

    private InputStream getImageBuffer(String html) throws Exception {

        String fileName = "/tmp/" + Instant.now().toEpochMilli();
        FileUtils.writeStringToFile(new File(fileName), html, "UTF-8");
        String address = "file:" + fileName;
        Java2DRenderer renderer = new Java2DRenderer(address, 400);
        RenderingHints hints = new RenderingHints(RenderingHints.KEY_COLOR_RENDERING,
                RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        hints.add(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
        hints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
        hints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC));
        renderer.setRenderingHints(hints);
        BufferedImage img = renderer.getImage();
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(img, "png", os);
        return new ByteArrayInputStream(os.toByteArray());
    }

    private String getContentFromTemplate(String template, Map<String, Object> model) throws Exception {
        View resolvedView = viewResolver.resolveViewName(template, Locale.US);
        MockHttpServletResponse mockResp = new MockHttpServletResponse();
        MockHttpServletRequest req = new MockHttpServletRequest();
        LOGGER.info("Resolved view ->  {}, {}, {}, {}", resolvedView, model, req, mockResp);
        resolvedView.render(model, req, mockResp);
        return mockResp.getContentAsString();
    }

    @RequestMapping(value = "/offerHistory", method = RequestMethod.GET)
    public String getPaginatedOffers(HttpServletRequest request, @RequestParam YearMonth yearMonth, Model model)
            throws ProfitMandiBusinessException {

        List<CreateOfferRequest> publishedOffers = offerService.getAllOffers(yearMonth).values().stream()
                .sorted(Comparator.comparing(CreateOfferRequest::getId).reversed()).collect(Collectors.toList());
        model.addAttribute("offers", publishedOffers);
        model.addAttribute("yearMonth", yearMonth);
        model.addAttribute("currentMonth", yearMonth.equals(YearMonth.now()));

        return "offer_history";
    }

    @RequestMapping(value = "/offer-details", method = RequestMethod.GET)
    public String schemeDetails(HttpServletRequest request, @RequestParam int offerId, Model model)
            throws ProfitMandiBusinessException {
        CreateOfferRequest createOfferRequest = offerService.getOffer(0, offerId);

        model.addAttribute("offer", createOfferRequest);
        return "offer-details";
    }

    @RequestMapping(value = "/offer/process/{offerId}", method = RequestMethod.GET)
    public ResponseEntity<?> processOfferRequest(HttpServletRequest request, @PathVariable int offerId, Model model)
            throws Exception {
        CreateOfferRequest createOfferRequest = offerService.getOffer(0, offerId);
        if (!createOfferRequest.isActive()) {
            throw new ProfitMandiBusinessException("Offer not active", "Offer not active", "Offer not active");
        }
        if (createOfferRequest.getSchemeType().equals(OfferSchemeType.SELLIN)) {
            offerService.processSellin(createOfferRequest);
        } else if (createOfferRequest.getSchemeType().equals(OfferSchemeType.ACTIVATION)) {
            offerService.processActivationtOffer(createOfferRequest);
        }
        return responseSender.ok(true);
    }

    @RequestMapping(value = "/offerDownload", method = RequestMethod.GET)
    public ResponseEntity<?> dowloadOfferSummary(HttpServletRequest request, @RequestParam int offerId, Model model)
            throws Exception {
        final HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        headers.set("Content-disposition", "inline; filename=offer-" + offerId + ".csv");
        CreateOfferRequest createOfferRequest = offerService.getOffer(0, offerId);
        ByteArrayOutputStream baos = offerService.createCSVOfferReport(createOfferRequest);
        final InputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
        final InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
        return new ResponseEntity<>(inputStreamResource, headers, HttpStatus.OK);
    }

    @RequestMapping(value = "/offerById", method = RequestMethod.GET)
    public String offerById(HttpServletRequest request, int offerId, Model model) throws ProfitMandiBusinessException {
        Offer offer = offerRepository.selectById(offerId);
        model.addAttribute("offer", offer);
        return "offer-edit";

    }

    @RequestMapping(value = "/published-offers", method = RequestMethod.GET)
    public String publishedOffersOnMonthBefore(HttpServletRequest request, @RequestParam int yearMonth, @RequestParam(required = false, defaultValue = "") String brandFilter, Model model)
            throws ProfitMandiBusinessException {
        LOGGER.info("publishedOffersCalled");
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
        int fofoId = loginDetails.getFofoId();
        List<CreateOfferRequest> createOffers = offerService.getPublishedOffers(fofoId,
                YearMonth.from(LocalDate.now()).minusMonths(yearMonth));

        List<CreateOfferRequest> publishedOffers = null;
        if (!brandFilter.isEmpty()) {
            publishedOffers = createOffers.stream()
                    .filter(createOffer -> createOffer.getTargetSlabs().stream()
                            .map(x -> x.getItemCriteriaPayouts())
                            .flatMap(List::stream)
                            .map(ItemCriteriaPayout::getItemCriteria)
                            .map(ItemCriteria::getBrands)
                            .flatMap(List::stream)
                            .anyMatch(brand -> brand.equals(brandFilter)))
                    .collect(Collectors.toList());
        } else {
            publishedOffers = createOffers.stream().filter(createOffer -> createOffer.getTargetSlabs().stream()
                    .map(x -> x.getItemCriteriaPayouts())
                    .flatMap(List::stream)
                    .map(ItemCriteriaPayout::getItemCriteria)
                    .map(ItemCriteria::getCatalogIds)
                    .flatMap(List::stream)
                    .noneMatch(catalogId -> catalogRepository.selectCatalogById(catalogId).getBrand().equals("Live Demo"))).collect(Collectors.toList());
        }

        model.addAttribute("publishedOffers", publishedOffers);

        return "published-offers";
    }

    @PostMapping(value = "/offers/upload")
    public String uploadOffers(HttpServletRequest request, @RequestPart("file") MultipartFile targetFile, Model model)
            throws Exception {
        offerService.createOffers(targetFile.getInputStream());
        model.addAttribute("response1", true);
        return "response";
    }

    @RequestMapping(value = "/getOfferMargins", method = RequestMethod.GET)
    public String getOfferMargins(HttpServletRequest request,
                                  @RequestParam(name = "offerId", defaultValue = "0") int offerId, Model model) throws Exception {
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
        boolean isAdmin = roleManager.isAdmin(loginDetails.getRoleIds());
        CreateOfferRequest createOfferRequest = offerService.getOffer(isAdmin ? 0 : loginDetails.getFofoId(), offerId);

        model.addAttribute("offer", createOfferRequest);
        model.addAttribute("isAdmin", isAdmin);

        return "offer_margin_detail_partner";

    }

    @RequestMapping(value = "/todayOffer")
    public String todayOffer(HttpServletRequest request, Model model, @RequestParam(name = "fofoId", defaultValue = "0") int fofoId) throws ProfitMandiBusinessException {

        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
        if (fofoId == 0) {
            fofoId = loginDetails.getFofoId();
        }
        List<BrandCatalog> allBrands = brandsService.getBrandsToDisplay(3);

        // 1. IDs to exclude entirely
        List<Integer> excludedIds = Arrays.asList(132, 133, 28, 17, 125);

        // 2. Brands that must come first (in this specific order)
        List<String> priorityOrder = Arrays.asList("Samsung", "Oppo", "Vivo", "Xiaomi", "Realme");

        List<BrandCatalog> sortedBrands = allBrands.stream()
                .filter(brand -> !excludedIds.contains(brand.getId())) // Remove excluded
                .sorted((b1, b2) -> {
                    // Get the index of the brand name in our priority list
                    int index1 = priorityOrder.indexOf(b1.getName());
                    int index2 = priorityOrder.indexOf(b2.getName());

                    // If brand is NOT in priority list, give it a high index (move to bottom)
                    int p1 = (index1 != -1) ? index1 : Integer.MAX_VALUE;
                    int p2 = (index2 != -1) ? index2 : Integer.MAX_VALUE;

                    if (p1 != p2) {
                        return Integer.compare(p1, p2); // Sort by priority first
                    }

                    // If both are "Others", sort them alphabetically
                    return b1.getName().compareToIgnoreCase(b2.getName());
                })
                .collect(Collectors.toList());

        model.addAttribute("brands", sortedBrands);
        model.addAttribute("fofoId", fofoId);
        model.addAttribute("date", FormattingUtils.format(LocalDateTime.now()));

        return "today-offer";
    }

    @RequestMapping(value = "/todayOfferList")
    public String todayOfferList(HttpServletRequest request, Model model, @RequestParam String brand, @RequestParam(defaultValue = "0", required = false) int fofoId) throws ProfitMandiBusinessException {

        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
        if (fofoId == 0) {
            fofoId = loginDetails.getFofoId();
        }
        FofoStore fs = fofoStoreRepository.selectByRetailerId(fofoId);

        List<String> brands = brandsService.getBrandsToDisplay(3).stream().map(x -> x.getName()).collect(Collectors.toList());

        List<TodayOfferModel> todayOfferModels = todayOfferService.findAllTodayOffer(brand, fofoId);

        List<TodayOfferModel> groupedOffers = todayOfferService.groupSameOffers(todayOfferModels);
        model.addAttribute("brands", brands);
        model.addAttribute("todayOfferModels", todayOfferModels);
        model.addAttribute("groupedOffers", groupedOffers);


        return "today-offer-list";
    }


    @RequestMapping(value = "/todayFofoOffer")
    public String todayFofoOffer(HttpServletRequest request, Model model, @RequestParam(name = "fofoId", defaultValue = "0") int fofoId) throws ProfitMandiBusinessException {

        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
        if (fofoId == 0) {
            fofoId = loginDetails.getFofoId();
        }
        List<BrandCatalog> allBrands = brandsService.getBrandsToDisplay(3);

        // 1. IDs to exclude entirely
        List<Integer> excludedIds = Arrays.asList(132, 133, 28, 17, 125);

        // 2. Brands that must come first (in this specific order)
        List<String> priorityOrder = Arrays.asList("Samsung", "Oppo", "Vivo", "Xiaomi", "Realme");

        List<BrandCatalog> sortedBrands = allBrands.stream()
                .filter(brand -> !excludedIds.contains(brand.getId())) // Remove excluded
                .sorted((b1, b2) -> {
                    // Get the index of the brand name in our priority list
                    int index1 = priorityOrder.indexOf(b1.getName());
                    int index2 = priorityOrder.indexOf(b2.getName());

                    // If brand is NOT in priority list, give it a high index (move to bottom)
                    int p1 = (index1 != -1) ? index1 : Integer.MAX_VALUE;
                    int p2 = (index2 != -1) ? index2 : Integer.MAX_VALUE;

                    if (p1 != p2) {
                        return Integer.compare(p1, p2); // Sort by priority first
                    }

                    // If both are "Others", sort them alphabetically
                    return b1.getName().compareToIgnoreCase(b2.getName());
                })
                .collect(Collectors.toList());

        model.addAttribute("brands", sortedBrands);
        model.addAttribute("fofoId", fofoId);
        model.addAttribute("date", FormattingUtils.format(LocalDateTime.now()));

        return "today-fofo-offer";
    }

}