package com.bcxin.tenant.open.domains.services.impls;

import com.bcxin.tenant.open.domains.entities.BillStatusCheckpointEntity;
import com.bcxin.tenant.open.domains.entities.DailyBillEntity;
import com.bcxin.tenant.open.domains.entities.MonthlyBillEntity;
import com.bcxin.tenant.open.domains.pojo.DailyBillPojo;
import com.bcxin.tenant.open.domains.repositories.*;
import com.bcxin.tenant.open.domains.services.DailyBillService;
import com.bcxin.tenant.open.domains.services.commands.GenerateDailyBillCommand;
import com.bcxin.tenant.open.domains.utils.BillUtils;
import com.bcxin.tenant.open.infrastructures.UnitWork;
import com.bcxin.tenant.open.infrastructures.enums.BillPaymentStatus;
import com.bcxin.tenant.open.infrastructures.enums.BillType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

@Service
public class DailyBillServiceImpl implements DailyBillService {
    Logger logger = LoggerFactory.getLogger(DailyBillServiceImpl.class);
    private final UnitWork unitWork;
    private final DailyBillRepository dailyBillRepository;
    private final MonthlyBillRepository monthlyBillRepository;
    private final RdCompanyRepository rdCompanyRepository;
    private final BillStatusCheckpointRepository billStatusCheckpointRepository;

    public static int BATCH_SIZE = 100;


    public DailyBillServiceImpl(UnitWork unitWork, DailyBillRepository dailyBillRepository, MonthlyBillRepository monthlyBillRepository, RdCompanyRepository rdCompanyRepository, BillStatusCheckpointRepository billStatusCheckpointRepository) {
        this.unitWork = unitWork;
        this.dailyBillRepository = dailyBillRepository;
        this.monthlyBillRepository = monthlyBillRepository;
        this.rdCompanyRepository = rdCompanyRepository;
        this.billStatusCheckpointRepository = billStatusCheckpointRepository;
    }

    @Override
    public void dispatch(GenerateDailyBillCommand command) {
        Date date = command.getDate();

        Collection<BillType> billTypes = command.getBillTypes();
        if (CollectionUtils.isEmpty(billTypes)) {
            return;
        }

        /**
         * 以公司为事务粒度。所有类型的日账单，要么同时生成要么同时失败。
         */
        List<String> orgIds = rdCompanyRepository.selectAllCompanyIds();

        Map<String, List<BillStatusCheckpointEntity>> billStatusMap = billStatusCheckpointRepository.selectAll(0).stream()
                .collect(Collectors.groupingBy(BillStatusCheckpointEntity::getOrganizationId));
        Map<String,List<DailyBillEntity>> currentDailyBillsMap = getAllTypesDailyBills(billTypes,null,date,date).stream()
                .collect(Collectors.groupingBy(DailyBillEntity::getOrgId));


        int index = 0;
        String transactionID = unitWork.beginTransaction();
        List<DailyBillEntity> dailyBillEntityList = new ArrayList<>();
        List<BillStatusCheckpointEntity> insertBillStatusList = new ArrayList<>();
        List<BillStatusCheckpointEntity> updateBillStatusList = new ArrayList<>();
        //补漏的时候，看前天的账单是否有生成
        Date endDate = BillUtils.getPreviousDate(date);
        for (int i = 0; i < orgIds.size(); i++) {
            String orgId = orgIds.get(i);
            index++;
            try {
                //今天生成的昨天的日账单
                if(currentDailyBillsMap.get(orgId) != null){
                    dailyBillEntityList.addAll(currentDailyBillsMap.get(orgId));
                }

                //补漏，上次成功生成的日期的后一天到前天,包含前天的
                Date fromDate = null;
                List<BillStatusCheckpointEntity> billStatus = billStatusMap.get(orgId);
                if (!CollectionUtils.isEmpty(billStatus)) {
                    fromDate = BillUtils.getNextDate(billStatus.get(0).getLastUpdate());
                }
                //当最后一个日账单的生成日期比前天要早（也就是最起码前天的日账单都没有生成，此时有漏掉）
                if(BillUtils.compareDate(fromDate,endDate)<=0){
                    dailyBillEntityList.addAll(getAllTypesDailyBills(billTypes, orgId, fromDate, endDate));
                }

                if (CollectionUtils.isEmpty(billStatus)) {
                    insertBillStatusList.add(BillStatusCheckpointEntity.create(null,
                            orgId,
                            0,
                            null,
                            java.sql.Date.valueOf(new SimpleDateFormat("yyyy-MM-dd").format(date))));
                } else {
                    BillStatusCheckpointEntity entity = billStatus.get(0);
                    entity.setLastUpdate(java.sql.Date.valueOf(new SimpleDateFormat("yyyy-MM-dd").format(date)));
                    updateBillStatusList.add(entity);
                }


                if (index % BATCH_SIZE == 0 || i == orgIds.size() - 1) {
                    if (!dailyBillEntityList.isEmpty()) {
                        //判断月账单是否已经有记录。如果没有，日账单外键关联月账单，需要插入一条月账单记录。此时月账单无效，无实际数据。
                        List<String> monthlyBillIds = dailyBillEntityList.stream().map(ix -> ix.getMonthlyBillId()).distinct().collect(Collectors.toList());
                        List<MonthlyBillEntity> existMontlyBills = monthlyBillRepository.getByBillId(monthlyBillIds);

                        if (monthlyBillIds.size() != existMontlyBills.size()) {
                            List<MonthlyBillEntity> monthlyBills = dailyBillEntityList.stream()
                                    .filter(ix -> !existMontlyBills.stream().anyMatch(ii -> ii.getBillId().equals(ix.getMonthlyBillId())))
                                    .map(ix -> MonthlyBillEntity.create(
                                            ix.getMonthlyBillId(),
                                            ix.getType(),
                                            ix.getOrgId(),
                                            BillPaymentStatus.Unpaid,
                                            null,
                                            BillUtils.getDateFormat(ix.getDate(), "yyyyMM"),
                                            0,
                                            0,
                                            0,
                                            null,
                                            0
                                    )).distinct().collect(Collectors.toList());

                            monthlyBillRepository.insertEmptyBills(monthlyBills);
                        }

                        //由于外键限制，需要先插入月账单才能插入日账单
                        dailyBillRepository.insert(dailyBillEntityList);
                    }

                    /**
                     * 如果没有日账单生成，也要更新到今天
                     */
                    if (!CollectionUtils.isEmpty(insertBillStatusList)) {
                        billStatusCheckpointRepository.insertBatch(insertBillStatusList);
                    }
                    if (!CollectionUtils.isEmpty(updateBillStatusList)) {
                        billStatusCheckpointRepository.updateBatch(updateBillStatusList);
                    }

                    unitWork.commit(transactionID);
                    index = 0;
                    if (i < orgIds.size() - 1) {
                        transactionID = unitWork.beginTransaction();
                    }
                    dailyBillEntityList.clear();
                    insertBillStatusList.clear();
                    updateBillStatusList.clear();
                }
            } catch (Exception e) {
                logger.error("生成日账单发生异常。organizationId={},date={}",
                        dailyBillEntityList.stream().map(ix->ix.getOrgId()).collect(Collectors.toList()), date, e);
                unitWork.rollback(transactionID);
                index = 0;
                if (i < orgIds.size() - 1) {
                    transactionID = unitWork.beginTransaction();
                }
                dailyBillEntityList.clear();
                insertBillStatusList.clear();
                updateBillStatusList.clear();
            }
        }
    }

    private List<DailyBillEntity> getAllTypesDailyBills(Collection<BillType> billTypes, String orgId, Date fromDate, Date endDate) {
        List<DailyBillEntity> dailyBillEntityList = new ArrayList<>();
        for (BillType billType : billTypes) {
            switch (billType) {
                case Attendance -> {
                    //签到签退
                    //不同公司查询的时间段可能不同
                    List<DailyBillPojo> attendaceList = dailyBillRepository.getAttendanceBillInfo(orgId, fromDate, endDate);
                    if (!attendaceList.isEmpty()) {
                        List<DailyBillEntity> attBillEntityList = attendaceList.stream().map(ix -> BillUtils.createDailyBillEntity(ix, ix.getDate(), BillType.Attendance))
                                .collect(Collectors.toList());
                        dailyBillEntityList.addAll(attBillEntityList);
                    }
                }
                case RollCall -> {
                    //点名轮换
                    List<DailyBillPojo> rollCallList = dailyBillRepository.getRollCallBillInfo(orgId,fromDate,endDate);
                    if(!rollCallList.isEmpty()){
                        List<DailyBillEntity> rollCallEntityList = rollCallList.stream().map(ix->BillUtils.createDailyBillEntity(ix,ix.getDate(),BillType.RollCall))
                                .collect(Collectors.toList());
                        dailyBillEntityList.addAll(rollCallEntityList);
                    }
                }
                case Track -> {
                    //轨迹查询
                    List<DailyBillPojo> locationQueries = dailyBillRepository.getEmployeeLocationBillInfo(orgId, fromDate, endDate);
                    if (!locationQueries.isEmpty()) {
                        List<DailyBillEntity> trackBillEntityList = locationQueries.stream().map(ix -> BillUtils.createDailyBillEntity(ix, ix.getDate(), BillType.Track))
                                .collect(Collectors.toList());
                        dailyBillEntityList.addAll(trackBillEntityList);
                    }
                }
                case Fence -> {
                    //电子围栏
                    List<DailyBillPojo> secStationRailQueries = dailyBillRepository.getSecurityStationRailBillInfo(orgId, fromDate, endDate);
                    if (!secStationRailQueries.isEmpty()) {
                        List<DailyBillEntity> fenceBillEntityList = secStationRailQueries.stream().map(ix -> BillUtils.createDailyBillEntity(ix, ix.getDate(), BillType.Fence))
                                .collect(Collectors.toList());
                        dailyBillEntityList.addAll(fenceBillEntityList);
                    }
                }
            }
        }
        return dailyBillEntityList;
    }
}