package com.utopiaindustries.service;

import com.utopiaindustries.dao.ctp.*;
import com.utopiaindustries.model.ctp.*;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class DashboardService {

    private final InventoryTransactionLegDAO inventoryTransactionLegDAO;
    private final JobCardDAO jobCardDAO;
    private final FinishedItemDAO finishedItemDAO;
    private final StitchingOfflineItemDAO stitchingOfflineItemDAO;
    private final InventoryAccountDAO inventoryAccountDAO;
    private final PackagingItemsDAO packagingItemsDAO;


    public DashboardService(InventoryTransactionLegDAO inventoryTransactionLegDAO, JobCardDAO jobCardDAO, FinishedItemDAO finishedItemDAO, StitchingOfflineItemDAO stitchingOfflineItemDAO, InventoryAccountDAO inventoryAccountDAO, PackagingItemsDAO packagingItemsDAO) {
        this.inventoryTransactionLegDAO = inventoryTransactionLegDAO;
        this.jobCardDAO = jobCardDAO;
        this.finishedItemDAO = finishedItemDAO;
        this.stitchingOfflineItemDAO = stitchingOfflineItemDAO;
        this.inventoryAccountDAO = inventoryAccountDAO;
        this.packagingItemsDAO = packagingItemsDAO;
    }

    public Map<String, Float> getPhasesProgressDayWise(String lineNo) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        HashMap<String, Float> progress = new HashMap<>();

        String cuttingAccount = "CUTTING ACCOUNT " + lineNo;
        String stitchingAccount = "STITCHING ACCOUNT " + lineNo;
        String finishingAccount = "FINISHING ACCOUNT " + lineNo;
        String packagingAccount = "A GRADE ACCOUNT " + lineNo;

        LocalDateTime today = LocalDateTime.now().withHour(0).withMinute(0).withSecond(1);
        String startDate1 = today.format(formatter);
        String endDate1 = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59).format(formatter);
        String forPreviousDate = LocalDateTime.now().minusDays(1).withHour(23).withMinute(59).withSecond(59).format(formatter);

        //set inventory accounts
        List<InventoryAccount> inventoryAccounts = inventoryAccountDAO.findAll();
        InventoryAccount inventoryAccount = inventoryAccounts.stream()
                .filter(e -> stitchingAccount.equals(e.getTitle())).findFirst().orElse(new InventoryAccount());
        Map<String, Integer> inventoryAccountMap = inventoryAccounts.stream()
                .filter(e -> cuttingAccount.equals(e.getTitle()) ||
                        stitchingAccount.equals(e.getTitle()) ||
                        finishingAccount.equals(e.getTitle()) ||
                        packagingAccount.equals(e.getTitle()))
                .collect(Collectors.toMap(InventoryAccount::getTitle, e -> (int) e.getId()));

        //get all in remaining transaction stitching
        List<Long> stitchingItemIds = inventoryTransactionLegDAO.findRemainingByParentTypeAndAccountID("STITCHING_OFFLINE", inventoryAccountMap.get(stitchingAccount));

        //get all out transaction stitching
        List<Long> stitchingOutIds = inventoryTransactionLegDAO.getOutParentIdByDate("STITCHING_OFFLINE", inventoryAccountMap.get(stitchingAccount));


        //get all in transaction finished
        List<Long> finishing = inventoryTransactionLegDAO.getInParentIdByDate("FINISHED_ITEM", inventoryAccountMap.get(finishingAccount));

        //get all in remaining transaction packaging
        List<Long> packagingItemIDs = inventoryTransactionLegDAO.findRemainingByParentTypeAndAccountID("PACKAGING", inventoryAccountMap.get(packagingAccount));

        //set stitching related details
        Long approvedStitchingOfflineItems = 0L;
        Long remaininfQcAlterPieces = 0L;
        Long approvedStitchingOfflineItemsThenReject = 0L;
        long qcReject = 0L;
        if (stitchingItemIds != null && !stitchingItemIds.isEmpty()) {
            qcReject = stitchingOfflineItemDAO.findByQCOperationDateAndIds(startDate1, endDate1, "REJECT", stitchingItemIds);
            remaininfQcAlterPieces = stitchingOfflineItemDAO.findByQCOperationDateAndIds(null, forPreviousDate, "REJECT", stitchingItemIds);
        }
        if(stitchingOutIds != null && !stitchingOutIds.isEmpty()) {
            approvedStitchingOfflineItems = stitchingOfflineItemDAO.findByQCOperationDateAndIds(startDate1, endDate1, "APPROVED", stitchingOutIds);
            approvedStitchingOfflineItemsThenReject = stitchingOfflineItemDAO.findByQCOperationDateAndIds(startDate1, endDate1, "REJECT", stitchingOutIds);
        }
        //set finishing related details
        Long alterationPieceFinish = 0L;
        Long rejectFinishedItem = 0L;
        Long washFinishedItem = 0L;
        Long approved = 0L;
        Long operationNotPerformed = 0L;
        Long remainingAlterationPieceFinish = 0L;
        if (finishing != null && !finishing.isEmpty()) {
            approved = finishedItemDAO.findByOperationDateAndIdsAndQaStatus(startDate1, endDate1, "APPROVED", finishing);
            operationNotPerformed = finishedItemDAO.findByOperationDateAndIdsAndQaStatus(startDate1, endDate1, "-", finishing);
            rejectFinishedItem = finishedItemDAO.findByOperationDateAndIdsAndQaStatus(startDate1, endDate1, "REJECT", finishing);
            washFinishedItem = finishedItemDAO.findByOperationDateAndIdsAndQaStatus(startDate1, endDate1, "WASHED", finishing);
            alterationPieceFinish = finishedItemDAO.findByOperationDateAndIdsAndQaStatus(startDate1, endDate1, "ALTER", finishing);
            remainingAlterationPieceFinish = finishedItemDAO.findByOperationDateAndIdsAndQaStatus(null, forPreviousDate, "ALTER", finishing);
        }

        //set packaging details
        Long packagingItems = 0L;
        if (packagingItemIDs != null && !packagingItemIDs.isEmpty()) {
            packagingItems = packagingItemsDAO.findByDateAndIds(startDate1, endDate1, packagingItemIDs);
        }

        //set shift wise details and time
        LocalDateTime statTime = LocalDateTime.now().withHour(9).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime endTime = statTime.plusMinutes(inventoryAccount.getShiftMinutes());
        long minutesPassed = 0;
        if (statTime.isBefore(LocalDateTime.now()) && LocalDateTime.now().isBefore(endTime)) {
            minutesPassed = Duration.between(statTime, LocalDateTime.now()).toMinutes();
        }

        //set efficiency
        long shiftTargetMinutesWise = getTargetShiftWiseOrHourlyWise(Math.max(0, minutesPassed), inventoryAccount);
        float efficiency;
        if (shiftTargetMinutesWise == 0) {
            efficiency = 0f;
        } else {
            efficiency = (float) approvedStitchingOfflineItems / shiftTargetMinutesWise;
        }

        progress.put("Stitching", (float) approvedStitchingOfflineItems + qcReject);
        progress.put("totalWips", (float) stitchingItemIds.size() - qcReject);
        progress.put("Alteration", (float) qcReject + approvedStitchingOfflineItemsThenReject);

        progress.put("finishing", (float) approved );
        progress.put("ALTER", (float) alterationPieceFinish);
        progress.put("Reject", (float) rejectFinishedItem);
        progress.put("wash", (float) washFinishedItem);
        progress.put("finishingValueForBarChart", (float) approved + alterationPieceFinish + rejectFinishedItem + washFinishedItem + packagingItems);

        progress.put("packaging", (float) packagingItems);
        progress.put("totalPackaging", (float) packagingItemIDs.size());
        progress.put("remainingFinishAlter", (float) remainingAlterationPieceFinish);
        progress.put("remainingQCAlter", (float) remaininfQcAlterPieces);

        progress.put("Efficiency", efficiency);
        return progress;
    }

    public Map<String, String> getLineDetails(String lineNo) {
        String stitchingAccount = "STITCHING ACCOUNT " + lineNo;
        List<InventoryAccount> inventoryAccounts = inventoryAccountDAO.findAll();

        InventoryAccount inventoryAccount = inventoryAccounts.stream()
                .filter(e -> stitchingAccount.equals(e.getTitle())).findFirst().orElse(new InventoryAccount());

        int shiftTarget = getTargetShiftWiseOrHourlyWise(inventoryAccount.getShiftMinutes(), inventoryAccount);
        int shiftHourlyTarget = getTargetShiftWiseOrHourlyWise(60, inventoryAccount);

        HashMap<String, String> details = new HashMap<>();
        details.put("Shift Target", shiftTarget + " Pcs");
        details.put("articleName", inventoryAccount.getArticleName());
        details.put("Hourly Target", shiftHourlyTarget + " Pcs");
        details.put("Total Machine", String.valueOf(inventoryAccount.getTotalMachines()));
        details.put("Total Worker", String.valueOf(30));
        details.put("line", "Line " + lineNo);
        return details;
    }

    //formula for calculating targets --> machine * shiftTime * Efficiency / SAM
    private int getTargetShiftWiseOrHourlyWise(long minutes, InventoryAccount inventoryAccount) {
        if (inventoryAccount == null ||
                inventoryAccount.getTotalMachines() == 0 ||
                inventoryAccount.getEfficiency() == null ||
                inventoryAccount.getSam() == null) {
            return 0;
        }
        BigDecimal totalMachines = BigDecimal.valueOf(inventoryAccount.getTotalMachines());
        BigDecimal efficiency = inventoryAccount.getEfficiency();
        BigDecimal sam = inventoryAccount.getSam();
        BigDecimal totalShiftProductiveTime = totalMachines
                .multiply(BigDecimal.valueOf(minutes))
                .multiply(efficiency);
        if (sam.longValue() != 0) {
            return totalShiftProductiveTime.divide(sam, 0, RoundingMode.HALF_UP).intValueExact();
        }
        return 0;
    }

}
