package com.bcxin.tenant.domain.services.impls;

import com.bcxin.Infrastructures.UnitWork;
import com.bcxin.Infrastructures.components.RetryProvider;
import com.bcxin.Infrastructures.exceptions.NotFoundTenantException;
import com.bcxin.Infrastructures.utils.AuthUtil;
import com.bcxin.tenant.domain.entities.ContractEntity;
import com.bcxin.tenant.domain.entities.EmployeeEntity;
import com.bcxin.tenant.domain.entities.OrganizationEntity;
import com.bcxin.tenant.domain.readers.TenantDbReader;
import com.bcxin.tenant.domain.repositories.CompositeDataRepository;
import com.bcxin.tenant.domain.repositories.ContractRepository;
import com.bcxin.tenant.domain.repositories.EmployeeRepository;
import com.bcxin.tenant.domain.repositories.OrganizationRepository;
import com.bcxin.tenant.domain.services.ContractService;
import com.bcxin.tenant.domain.services.commands.contracts.*;
import com.bcxin.tenant.domain.services.commands.contracts.results.BatchImportContractCommandResult;
import lombok.Synchronized;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class ContractServiceImpl implements ContractService {
    private final ContractRepository contractRepository;
    private final EmployeeRepository employeeRepository;
    private final OrganizationRepository organizationRepository;
    private final CompositeDataRepository compositeDataRepository;
    private final UnitWork unitWork;
    private final TenantDbReader dbReader;
    private final RetryProvider retryProvider;


    public ContractServiceImpl(ContractRepository contractRepository,
                               EmployeeRepository employeeRepository,
                               OrganizationRepository organizationRepository,
                               CompositeDataRepository compositeDataRepository,
                               UnitWork unitWork,
                               TenantDbReader dbReader,
                               RetryProvider retryProvider) {
        this.contractRepository = contractRepository;
        this.employeeRepository = employeeRepository;
        this.organizationRepository = organizationRepository;
        this.compositeDataRepository = compositeDataRepository;
        this.unitWork = unitWork;
        this.dbReader = dbReader;
        this.retryProvider = retryProvider;
    }

    @Override
    public void dispatch(CreateContractCommand command) {
        command.validate();

        EmployeeEntity employee = this.employeeRepository.getByOrganIdAndId(command.getOrganizationId(), command.getEmployeeId());
        if (employee == null) {
            throw new NotFoundTenantException("找不到该职员信息");
        }

        List<ContractEntity> contractList = contractRepository.getByOrganIdAndEmployeeId(command.getOrganizationId(), command.getEmployeeId());
        command.validateExist(contractList);

        this.unitWork.executeTran(() -> {
            ContractEntity contract =
                    ContractEntity.create(employee, command.getName(), command.getAName(), command.getBName(),
                            command.getBeginDate(), command.getEndDate(), command.isDateLimitless());
            contract.changeAttachment(command.getAttachment());
            contract.changeNote(command.getNote());
            contract.assignCreator(AuthUtil.getCurrentOperator());
            contract.assignModifier(AuthUtil.getCurrentOperator());
            this.contractRepository.save(contract);
        });
    }

    @Override
    public void dispatch(UpdateContractCommand command) {
        command.validate();

        ContractEntity contract = this.contractRepository.getByOrganIdAndId(command.getOrganizationId(), command.getId());
        if (contract == null) {
            throw new NotFoundTenantException("找不到合同信息");
        }

        List<ContractEntity> contractList = contractRepository.getByOrganIdAndEmployeeId(command.getOrganizationId(), contract.getEmployee().getId());
        command.validateExist(contract.getId(),contractList);

        this.unitWork.executeTran(() -> {
            contract.change(command.getName(), command.getAName(), command.getBName(), command.getNote(),
                    command.getBeginDate(), command.getEndDate(), command.isDateLimitless());
            contract.changeAttachment(command.getAttachment());
            contract.changeNote(command.getNote());
            contract.assignModifier(AuthUtil.getCurrentOperator());
            this.contractRepository.save(contract);
        });
    }

    @Override
    public void dispatch(DeleteContractCommand command) {
        command.validate();

        ContractEntity contract = this.contractRepository.getByOrganIdAndId(command.getOrganizationId(), command.getId());
        if (contract == null) {
            throw new NotFoundTenantException("找不到合同信息");
        }

        this.unitWork.executeTran(() -> {
            this.contractRepository.delete(contract);
        });
    }

    @Override
    public void dispatch(BatchDeleteContractCommand command) {
        command.validate();

        List<ContractEntity> contracts = this.contractRepository.getByOrganIdAndIds(command.getOrganizationId(), command.getIds());
        if (contracts.size() == 0) {
            throw new NotFoundTenantException("找不到合同信息");
        }
        for (ContractEntity contract : contracts) {
            this.unitWork.executeTran(() -> {
                this.contractRepository.delete(contract);
            });
        }
    }

    @Override
    public BatchImportContractCommandResult dispatch(BatchImportContractCommand command) {
        command.validate();

        Optional<OrganizationEntity> organizationOptional = this.organizationRepository.findById(command.getOrganizationId());
        if (!organizationOptional.isPresent()) {
            throw new NotFoundTenantException("组织无效!");
        }
        OrganizationEntity organization = organizationOptional.get();

        Collection<String> idNums = command.getItems().stream()
                .filter(ii -> ii.IsValid())
                .map(ii -> ii.getData().getIdNum()).collect(Collectors.toList());

        Collection<EmployeeEntity> employeeList = this.employeeRepository.getByIdNums(organization.getId(), idNums);
        if(employeeList.size() == 0){
            throw new NotFoundTenantException("找不到关联员工证件号码员工信息!");
        }

        Map<String, EmployeeEntity> employeeMaps = employeeList.stream().collect(Collectors.toMap(EmployeeEntity::getSelectIdNum, Function.identity()));

        Collection<BatchImportContractCommand.ContractCommandItem> validCommandItems = command.getItems().stream()
                .filter(ii -> ii.IsValid()).collect(Collectors.toList());
        List<ContractEntity> contractList = this.contractRepository.getByOrganIdAndEmployeeIds(organization.getId(),employeeList.stream().map(EmployeeEntity::getId).collect(Collectors.toList()));
        Map<String,List<ContractEntity>> contractMap = contractList.size() ==0 ? new HashMap<>():contractList.stream().collect(Collectors.groupingBy(ii -> ii.getEmployee().getId()));

        this.unitWork.executeTran(() -> {
            validCommandItems.forEach(item -> {
                EmployeeEntity employeeEntity = employeeMaps.get(item.getData().getIdNum());
                if (employeeEntity != null) {
                    boolean save = true;
                    List<ContractEntity> contracts = contractMap.get(employeeEntity.getId());
                    if(contracts != null) {
                        if (contracts.stream().anyMatch(ii -> (item.getBeginDate().getTime() <= ii.getBeginDate().getTime()
                                && item.getEndDate().getTime() >= ii.getBeginDate().getTime())
                                || (item.getBeginDate().getTime() <= ii.getEndDate().getTime()
                                && item.getEndDate().getTime() >= ii.getEndDate().getTime())
                                || (ii.getBeginDate().getTime() <= item.getBeginDate().getTime()
                                && ii.getEndDate().getTime() >= item.getBeginDate().getTime())
                                || (ii.getBeginDate().getTime() <= item.getEndDate().getTime()
                                && ii.getEndDate().getTime() >= item.getEndDate().getTime())
                        )) {
                            item.addError("在合同有效期内同一个人只能有一份合同");
                            save = false;
                        }
                    }
                    if(save) {
                        ContractEntity contract = ContractEntity.create(
                                employeeEntity, item.getData().getName(), item.getData().getAName(), item.getData().getBName(),
                                item.getBeginDate(), item.getEndDate(), false);
                        contract.assignCreator(AuthUtil.getCurrentOperator());
                        contract.assignModifier(AuthUtil.getCurrentOperator());
                        this.contractRepository.save(contract);
                    }
                } else {
                    item.addError(String.format("找不到职员(%s)信息", item.getData().getIdNum()));
                }
            });
        });

        AtomicReference<String> importedDataIdAto = new AtomicReference<>();

        retryProvider.execute(() -> {
            importedDataIdAto.set(this.compositeDataRepository.execute(organization, command));
        }, 10);

        int totalSuccess = (int) command.getItems().stream().filter(ii -> ii.IsValid()).count();
        int totalFailed = (int) command.getItems().stream().filter(ii -> !ii.IsValid()).count();

        return BatchImportContractCommandResult.create(
                importedDataIdAto.get(), totalSuccess,
                totalFailed, command.getItems().size()
        );
    }
}
