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

import com.bcxin.Infrastructures.IdWorker;
import com.bcxin.Infrastructures.UnitWork;
import com.bcxin.Infrastructures.components.EventDispatcher;
import com.bcxin.Infrastructures.components.JsonProvider;
import com.bcxin.Infrastructures.enums.ApprovedStatus;
import com.bcxin.Infrastructures.enums.ResourceReferenceType;
import com.bcxin.Infrastructures.exceptions.ArgumentTenantException;
import com.bcxin.Infrastructures.exceptions.ConflictTenantException;
import com.bcxin.Infrastructures.exceptions.ForbidTenantException;
import com.bcxin.Infrastructures.exceptions.NotFoundTenantException;
import com.bcxin.tenant.domain.DomainConstraint;
import com.bcxin.tenant.domain.entities.ExternalGroupEntity;
import com.bcxin.tenant.domain.entities.ExternalMemberEntity;
import com.bcxin.tenant.domain.entities.OrganizationEntity;
import com.bcxin.tenant.domain.entities.TenantUserEntity;
import com.bcxin.tenant.domain.events.MemberCreatedEvent;
import com.bcxin.tenant.domain.events.SysMessageEvent;
import com.bcxin.tenant.domain.repositories.*;
import com.bcxin.tenant.domain.services.ExternalMemberService;
import com.bcxin.tenant.domain.services.commands.externalMembers.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;

@Service
public class ExternalMemberServiceImpl implements ExternalMemberService {
    private final UnitWork unitWork;

    private final IdWorker idWorker;
    private final ExternalMemberRepository externalMemberRepository;
    private final ExternalGroupRepository externalGroupRepository;
    private final OrganizationRepository organizationRepository;

    private final TenantUserRepository userRepository;

    private final EmployeeRepository employeeRepository;

    private final JsonProvider jsonProvider;
    private final EventDispatcher eventDispatcher;

    public ExternalMemberServiceImpl(UnitWork unitWork, IdWorker idWorker,
                                     ExternalMemberRepository externalMemberRepository,
                                     ExternalGroupRepository externalGroupRepository,
                                     OrganizationRepository organizationRepository,
                                     TenantUserRepository userRepository,
                                     EmployeeRepository employeeRepository,
                                     JsonProvider jsonProvider, EventDispatcher eventDispatcher) {
        this.unitWork = unitWork;
        this.idWorker = idWorker;
        this.externalMemberRepository = externalMemberRepository;
        this.externalGroupRepository = externalGroupRepository;
        this.organizationRepository = organizationRepository;
        this.userRepository = userRepository;
        this.employeeRepository = employeeRepository;
        this.jsonProvider = jsonProvider;
        this.eventDispatcher = eventDispatcher;
    }

    @Override
    public void update(UpdateExternalMemberCommand command) {
        Collection<ExternalMemberEntity> externalMembers = this.externalMemberRepository.getByIds(command.getIds());
        if (CollectionUtils.isEmpty(externalMembers)) {
            throw new NotFoundTenantException();
        }

        if (!externalMembers.stream().anyMatch(ix -> ix.getReferenceNumber().equalsIgnoreCase(command.getReferenceNumber()))) {
            throw new ForbidTenantException("禁止修改该分组人员信息");
        }

        Collection<ExternalGroupEntity> addGroups = null;
        if (!CollectionUtils.isEmpty(command.getAddGroupIds())) {
            addGroups = this.externalGroupRepository.getByIds(command.getAddGroupIds());
        }


        for (ExternalMemberEntity externalMember : externalMembers) {
            externalMember.resetGroups();
            externalMember.join(jsonProvider, addGroups);
            externalMember.markOperateLog(command.getOperatorId(), command.getOperatorName());

            this.unitWork.executeTran(() -> {
                this.externalMemberRepository.save(externalMember);
            });
        }
    }

    @Override
    public void create(CreateExternalMemberCommand command) {
        OrganizationEntity organization = this.organizationRepository.findById(command.getReferenceNumber()).orElse(null);
        if (organization == null) {
            throw new NotFoundTenantException(String.format("当前组织(%s)无效", command.getReferenceNumber()));
        }

        TenantUserEntity tenantUser = this.userRepository.findById(command.getTenantUserId()).orElse(null);
        if (tenantUser == null) {
            throw new NotFoundTenantException("找不到当前用户信息");
        }

        ExternalGroupEntity group = null;
        if (StringUtils.hasLength(command.getGroupId())) {
            group = this.externalGroupRepository.findById(command.getGroupId()).orElse(null);
        }

        ExternalMemberEntity externalMember =
                this.externalMemberRepository.getByTenantUserIdAndRefNumAndRefType(
                        tenantUser.getId(),
                        organization.getId(),
                        ResourceReferenceType.Organization
                );

        if (externalMember != null) {
            checkAndChangeExternalMember(externalMember, tenantUser,organization);
        } else {
            /**
             *  用于解决: 用于解决现有employee才有memberId的问题
             */
            String empId =
                    this.employeeRepository.getByOrgIdAndUserId(organization.getId(), tenantUser.getId());
            externalMember = ExternalMemberEntity.create(
                    jsonProvider,
                    empId,
                    command.getInviteType(),
                    command.getInviteCode(),
                    tenantUser, group, organization
            );
        }

        Collection<String> adminMemberIds  = this.externalMemberRepository.getAdminIds(organization.getId());
        try {
            ExternalMemberEntity finalExternalMember = externalMember;
            this.unitWork.executeTran(() -> {
                this.externalMemberRepository.save(finalExternalMember);
                this.eventDispatcher.dispatch(MemberCreatedEvent.create(
                        finalExternalMember.getId(),
                        organization.getId(),
                        finalExternalMember.getMemberType(),
                        tenantUser.getName(),
                        tenantUser.getTelephone(),
                        organization.getName(),
                        adminMemberIds,false
                ));
            });
        } catch (Exception ex) {
            if (DomainConstraint.isUniqueConstraintIssue(ex)) {
                externalMember =
                        this.externalMemberRepository.getByTenantUserIdAndRefNumAndRefType(
                                tenantUser.getId(),
                                organization.getId(),
                                ResourceReferenceType.Organization
                        );

                if (externalMember != null) {
                    checkAndChangeExternalMember(externalMember, tenantUser,organization);
                    ExternalMemberEntity finalExternalMember1 = externalMember;
                    this.unitWork.executeTran(() -> {
                        this.externalMemberRepository.save(finalExternalMember1);
                        this.eventDispatcher.dispatch(MemberCreatedEvent.create(
                                finalExternalMember1.getId(),
                                organization.getId(),
                                finalExternalMember1.getMemberType(),
                                tenantUser.getName(),
                                tenantUser.getTelephone(),
                                organization.getName(),
                                adminMemberIds,
                                true
                        ));
                    });
                } else {
                    throw ex;
                }
            }

            throw ex;
        }
    }

    @Override
    public void delete(DeleteExternalMemberCommand command) {
        Collection<ExternalMemberEntity> externalMembers = getAllowedMembers(command.getIds(),command.getReferenceNumber());
        if (CollectionUtils.isEmpty(externalMembers) && !CollectionUtils.isEmpty(command.getIds())) {
            throw new ArgumentTenantException("无效参数信息(非法删除非该组织数据)");
        }

        this.unitWork.executeTran(() -> {
            for (ExternalMemberEntity member : externalMembers) {
                member.delete(command.getOperatorId(),command.getOperatorName());

                this.externalMemberRepository.save(member);
            }
        });
    }

    @Override
    public void updateStatus(UpdateExternalMemberStatusCommand command) {
        Collection<ExternalMemberEntity> externalMembers = getAllowedMembers(command.getIds(), command.getReferenceNumber());
        if (CollectionUtils.isEmpty(externalMembers) && !CollectionUtils.isEmpty(command.getIds())) {
            throw new ArgumentTenantException("无效参数信息(非法删除非该组织数据)");
        }

        if (command.getStatus() == ApprovedStatus.Init) {
            throw new ForbidTenantException("不允许重置审批状态");
        }

        this.unitWork.executeTran(() -> {
            //Collection<SysMessageEvent.MessageItemEvent> itemEvents = new ArrayList<>();
            for (ExternalMemberEntity member : externalMembers) {
                member.changeStatus(command.getStatus(), command.getNote(), command.getOperatorId(), command.getOperatorName());

                this.externalMemberRepository.save(member);
            }
        });
    }

    @Override
    public void create(CreateOrgExternalMemberCommand command) {
        this.unitWork.executeTran(() -> {
            ExternalMemberEntity externalMember = ExternalMemberEntity.create(
                    jsonProvider, command.getTenantUser(), command.getOrganization()
            );

            this.externalMemberRepository.save(externalMember);
        });
    }

    private Collection<ExternalMemberEntity> getAllowedMembers(Collection<String> ids,String referenceNumber) {
        Collection<ExternalMemberEntity> externalMembers =
                this.externalMemberRepository.getByIds(ids).stream()
                        .filter(ii -> ii.getReferenceNumber().equalsIgnoreCase(referenceNumber))
                        .collect(Collectors.toList());

        return externalMembers;
    }

    private void checkAndChangeExternalMember(
            ExternalMemberEntity externalMember,
                                     TenantUserEntity tenantUser,
            OrganizationEntity organization
            ) {
        if((externalMember.getApprovedInformationValueType().getStatus() == ApprovedStatus.NoPassed ||
                externalMember.getApprovedInformationValueType().getStatus() == ApprovedStatus.Deleted)) {
            externalMember.changeStatus(
                    ApprovedStatus.Init, "重新申请加入",
                    externalMember.getId(),
                    tenantUser.getName()
            );

        }else {
            throw new ConflictTenantException(String.format("该用户(%s)已存在（%s）提交记录, 请勿重复提交!", tenantUser.getName(), organization.getName()));
        }
    }
}
