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

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.bcxin.Infrastructures.TenantContext;
import com.bcxin.Infrastructures.TenantUserContext;
import com.bcxin.Infrastructures.UnitWork;
import com.bcxin.Infrastructures.components.EventDispatcher;
import com.bcxin.Infrastructures.components.JsonProvider;
import com.bcxin.Infrastructures.components.LockProvider;
import com.bcxin.Infrastructures.components.RetryProvider;
import com.bcxin.Infrastructures.enums.ApprovedStatus;
import com.bcxin.Infrastructures.enums.MasterSlaveType;
import com.bcxin.Infrastructures.enums.ResourceReferenceType;
import com.bcxin.Infrastructures.enums.TrueFalseStatus;
import com.bcxin.Infrastructures.exceptions.BadTenantException;
import com.bcxin.Infrastructures.exceptions.ConflictTenantException;
import com.bcxin.Infrastructures.exceptions.NotFoundTenantException;
import com.bcxin.Infrastructures.exceptions.RetryableTenantException;
import com.bcxin.Infrastructures.utils.AuthUtil;
import com.bcxin.api.interfaces.buses.MessageRpcProvider;
import com.bcxin.api.interfaces.buses.enums.MessageType;
import com.bcxin.api.interfaces.buses.requests.MessageRequest;
import com.bcxin.api.interfaces.commons.PlatformOperateLogRpcProvider;
import com.bcxin.api.interfaces.enums.ExternalMemberInviteType;
import com.bcxin.api.interfaces.tenants.requests.operatelog.PlatformOperateLogRequest;
import com.bcxin.tenant.domain.DomainConstraint;
import com.bcxin.tenant.domain.conditions.TenantUserSameValidator;
import com.bcxin.tenant.domain.conditions.requests.TenantUserSameCheckRequest;
import com.bcxin.tenant.domain.configs.EnvConfig;
import com.bcxin.tenant.domain.entities.*;
import com.bcxin.tenant.domain.entities.valueTypes.ItemValueType;
import com.bcxin.tenant.domain.entities.valueTypes.LocationValueType;
import com.bcxin.tenant.domain.enums.EventAction;
import com.bcxin.tenant.domain.enums.EventProcessedStatus;
import com.bcxin.tenant.domain.events.OrganizationCreatedEvent;
import com.bcxin.tenant.domain.events.OrganizationUpdatedEvent;
import com.bcxin.tenant.domain.repositories.*;
import com.bcxin.tenant.domain.repositories.dtos.TenantUserRepositoryDto;
import com.bcxin.tenant.domain.services.*;
import com.bcxin.tenant.domain.services.commands.BatchCreateTenantUserCommand;
import com.bcxin.tenant.domain.services.commands.CreateTenantEventCommand;
import com.bcxin.tenant.domain.services.commands.externalMembers.CreateExternalMemberCommand;
import com.bcxin.tenant.domain.services.commands.externalMembers.CreateOrgExternalMemberCommand;
import com.bcxin.tenant.domain.services.commands.organizations.*;
import com.bcxin.tenant.domain.services.commands.tenantUsers.NotifyNewUserCommand;
import com.bcxin.tenant.domain.utils.PassAssembleUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;

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

@Service
public class OrganizationServiceImpl implements OrganizationService {
    private final Logger logger = LoggerFactory.getLogger(OrganizationServiceImpl.class);
    private final OrganizationRepository organizationRepository;
    private final RegionRepository regionRepository;
    private final TenantEventRepository tenantEventRepository;
    private final TenantUserService userService;
    private final PlatformTransactionManager transactionManager;
    private final UnitWork unitWork;
    private final OrganizationAdminRepository organizationAdminRepository;
    private final OrganizationAdminService organizationAdminService;
    private final EnvConfig envConfig;
    private final TenantEventService tenantEventService;
    private final RetryProvider retryProvider;
    private final LockProvider lockProvider;
    private final TenantEventRepository eventRepository;
    private final MessageRpcProvider messageRpcProvider;
    private final TenantUserRepository tenantUserRepository;
    private final TenantUserSameValidator tenantUserSameValidator;
    private final JsonProvider jsonProvider;
    private final PlatformOperateLogRpcProvider platformOperateLogRpcProvider;

    private final ExternalMemberService externalMemberService;

    public OrganizationServiceImpl(OrganizationRepository organizationRepository,
                                   RegionRepository regionRepository,
                                   TenantEventRepository tenantEventRepository,
                                   TenantUserService userService,
                                   PlatformTransactionManager transactionManager,
                                   UnitWork unitWork,
                                   OrganizationAdminRepository organizationAdminRepository,
                                   OrganizationAdminService organizationAdminService,
                                   EnvConfig envConfig, TenantEventService tenantEventService,
                                   RetryProvider retryProvider, LockProvider lockProvider,
                                   TenantEventRepository eventRepository, MessageRpcProvider messageRpcProvider,
                                   TenantUserRepository tenantUserRepository,
                                   TenantUserSameValidator tenantUserSameValidator,
                                   JsonProvider jsonProvider,
                                   PlatformOperateLogRpcProvider platformOperateLogRpcProvider,
                                   ExternalMemberService externalMemberService) {
        this.organizationRepository = organizationRepository;
        this.regionRepository = regionRepository;
        this.tenantEventRepository = tenantEventRepository;
        this.userService = userService;
        this.transactionManager = transactionManager;
        this.unitWork = unitWork;
        this.organizationAdminRepository = organizationAdminRepository;
        this.organizationAdminService = organizationAdminService;
        this.envConfig = envConfig;
        this.tenantEventService = tenantEventService;
        this.retryProvider = retryProvider;
        this.lockProvider = lockProvider;
        this.eventRepository = eventRepository;
        this.messageRpcProvider = messageRpcProvider;
        this.tenantUserRepository = tenantUserRepository;
        this.tenantUserSameValidator = tenantUserSameValidator;
        this.jsonProvider = jsonProvider;
        this.platformOperateLogRpcProvider = platformOperateLogRpcProvider;
        this.externalMemberService = externalMemberService;
    }


    @Override
    public void update(UpdateCompanyCommand command) {
        try {
            this.unitWork.executeTran(() -> {
                Optional<OrganizationEntity> optional = organizationRepository.findById(command.getId());
                OrganizationEntity organization = optional.get();
                organization.change(command.getSysName(),
                        command.getDescription(),
                        command.getLogoPath(),
                        command.getLongitude(),
                        command.getLatitude(),
                        command.getInstitutionalCode(),
                        command.getIndustryDetailType(),
                        command.getName(),
                        command.getType(),
                        command.getTelephone(),
                        command.getEconomicType(),
                        command.getNature(),
                        translate2LocationValueType(command.getPlaceOfRegister()),
                        translate2LocationValueType(command.getPlaceOfBusiness()),
                        command.getParentName(),
                        command.getParentCertificateType(),
                        command.getParentCertificateNumber(),
                        command.getParentLegalPersonName(),
                        command.getParentLegalPersonTelephone(),
                        command.getLegalPersonName(),
                        command.getLegalPersonTelephone(),
                        command.getLegalPersonCredentialType(),
                        command.getLegalPersonCredentialNumber(),
                        command.getLegalPersonNationality(),
                        command.getSecurityPersonName(),
                        command.getSecurityPersonTelephone(),
                        command.getSecurityPersonCredentialType(),
                        command.getSecurityPersonCredentialNumber(),
                        command.getRegisteredCapital(),
                        command.getFixedCapital(),
                        command.getAnnualSalesVolume(),
                        command.getAnnualProfitTax(),
                        command.getCertificateType(),
                        command.getUnifySocialCreditCode(),
                        command.getUnifySocialCreditCodeFile(),
                        command.getServicePermitNumber(),
                        command.getNameOfIssuingAuthority(),
                        command.getFirstIssueServicePermit(),
                        command.getServicePermitAttachment(),
                        command.getSecurityApprovalNumber(),
                        command.getServiceScopeList(),
                        command.getSecurityPreparednessRating(),
                        command.getSecurityArmedRating(),
                        command.getSecuritySafetyDefenseRating(),
                        command.getIsRiskAssessment(),
                        command.getTrainContent(),
                        command.getNumberOfSecurityGuards(),
                        command.getIsPublicSecuritySecurity(),
                        command.getIsPropertyEnterprise(),
                        command.getNatureOfSelfRecruitedUnits(),
                        command.getPlaceOfSupervise(),
                        command.getSuperviseDepartId(),
                        command.getSuperviseDepartName(),
                        command.getFax(),
                        command.getAdminUserName(),
                        command.getAdminUserTelephone(),
                        jsonProvider,
                        command.getLocationAddress());


                /**
                 * 广播事件
                 */
                TenantContext.getInstance()
                        .resolve(EventDispatcher.class)
                        .dispatch(OrganizationUpdatedEvent.create(organization));

                this.organizationRepository.save(organization);
            });

        } catch (Exception ex) {
            if (DomainConstraint.isUniqueConstraintIssue(ex)) {
                throw new ConflictTenantException("该企业信息已经存在!");
            }
            throw ex;
        }
    }

    @Override
    public void create(CreateMyOrganizationCommand command) {
        TenantUserEntity tenantUser = this.tenantUserRepository.findById(command.getTenantUserId()).orElse(null);
        if (tenantUser == null) {
            throw new NotFoundTenantException("当前用户无效");
        }

        command.validate();
        try {
            this.unitWork.executeTran(() -> {
                OrganizationEntity organization = OrganizationEntity.createBasic(command.getIndustryCode(),
                        command.getInstitutionalCode(),
                        command.getName(),
                        tenantUser.getTelephone());

                DepartmentEntity department = organization.getDepartments().stream().findFirst().orElse(null);
                organization.addEmployee(
                        department, MasterSlaveType.Master, tenantUser, "系统管理员", true,
                        AuthUtil.getCurrentOperator()
                );

                //设置基础信息
                organization.setCompanyEntity(command);
                //设置组织归属人
                organization.changeOwner(tenantUser);

                if(command.isAsTeamMember()) {
                    this.externalMemberService.create(CreateOrgExternalMemberCommand.create(
                            organization, tenantUser
                    ));
                }

                Map<String, String> dynamic = new HashMap<>();

                //广播事件
                TenantContext.getInstance()
                        .resolve(EventDispatcher.class)
                        .dispatch(OrganizationCreatedEvent.create(organization, organization.getName(), dynamic));

                this.organizationRepository.save(organization);

                organization.getEmployees().forEach(ii -> {
                    ii.dispatchAfterCreatedEvent(envConfig.isRequiredPhoneAsLoginName());
                });
            });
        } catch (Exception ex) {
            if (DomainConstraint.isUniqueConstraintIssue(ex)) {
                throw new ConflictTenantException("该企业信息已经存在!");
            }

            throw ex;
        }
    }

    @Override
    public void dispatch(NotifyNewUserCommand command) {
        AtomicReference<TenantUserEntity> OrganizationAtomic = new AtomicReference<>();

        this.retryProvider.execute(() -> {
            Optional<TenantUserEntity> tenantUserOptional = this.tenantUserRepository.findById(command.getId());
            if (tenantUserOptional.isPresent()) {
                OrganizationAtomic.set(tenantUserOptional.get());
            } else {
                throw new RetryableTenantException();
            }
        }, 10);

        TenantUserEntity tenantUserEntity = OrganizationAtomic.get();

        this.lockProvider.execute(String.format("nf_%s", command.getEventId()), () -> {
            Optional<TenantEventEntity> tenantEventOptional = this.eventRepository.findById(command.getEventId());
            if (!tenantEventOptional.isPresent()) {
                return;
            }

            if (tenantEventOptional.get().getEventAction() != EventAction.OrganizationEvenForSms) {
                throw new BadTenantException(String.format("程序有误, 该事件(%s, %s)并不是处理SMS消息内容",
                        tenantEventOptional.get().getId(), tenantEventOptional.get().getEventAction()));
            }
            TenantEventEntity selectedTenantEvent = tenantEventOptional.get();
            if (selectedTenantEvent.getStatus() == EventProcessedStatus.Processed) {
                logger.error("重复短信-该短信之前已经发送过(id={})", selectedTenantEvent.getId());
                return;
            }

            // 配置的密码前缀+身份证后六位
            String pass = PassAssembleUtils.assemble(tenantUserEntity.getSelectIdNum(),envConfig.getPassPrefix());

            Map<String, Object> huaWeiSmsContent = new HashMap<>();
            //您的百保盾企业账号已创建，登陆账号为您的身份证号，密码为身份证号后六位，请点击http://v5qy.baibaodun.cn/登录使用，并将管理员账号修改为使用人真实信息。
            huaWeiSmsContent.put("smsCode", "B99");
            List<String> sendParams = new ArrayList();
            sendParams.add(pass);
            huaWeiSmsContent.put("params", JSON.toJSONString(sendParams));
            String telephone = tenantUserEntity.getTelephone();
            huaWeiSmsContent.put("mobile", telephone);

            this.messageRpcProvider.dispatch(MessageRequest.create(
                    MessageType.SMS, huaWeiSmsContent
            ));

            this.unitWork.executeNewTran(() -> {
                selectedTenantEvent.done("事件消费成功-短信发送成功");
                this.eventRepository.save(selectedTenantEvent);
            });
        });
    }

    /**
     * 通过管理员创建一个组织
     * 1, 新增租户
     * 2, 新增组织和部门
     * 3, 添加员工
     * 4, 广播事件
     *
     * @param command
     * @return
     */
    @Override
    public String register(RegisterCompanyCommand command) {
        //验证必填项
        command.validate();
        //创建租户信息
        Collection<BatchCreateTenantUserCommand.TenantUserCommandItem> commandItems = new ArrayList<>();
        //系统管理员
        if (command.getAdministrator() != null) {
            commandItems.add(translate2UserCommandItem(command.getAdministrator()));
        }

        /**
         * 针对启用手机号码作为身份证的环境(内网和RT), 验证手机和身份证是否为正确持有者
         * added by lhc
         */
        Collection<TenantUserSameCheckRequest> sameCheckRequests =
        commandItems.stream().map(ii->
                TenantUserSameCheckRequest.create(ii.getCredentialType(),ii.getCredentialNumber(),ii.getTelephone()))
                .collect(Collectors.toList());
        this.tenantUserSameValidator.validate(sameCheckRequests);

        Collection<TenantUserEntity> tenantUsers =
                this.userService.create(BatchCreateTenantUserCommand.create(commandItems));

        try {
            String notExistsIdNums = commandItems.stream().map(
                    BatchCreateTenantUserCommand.TenantUserCommandItem::getCredentialNumber)
                    .distinct().filter(ii -> tenantUsers.stream().noneMatch(ix -> ix.getSelectedCredential().getNumber().equals(ii)))
                    .collect(Collectors.joining(","));
            if (StringUtils.isNotBlank(notExistsIdNums)) {
                throw new BadTenantException(String.format("系统异常, 无法创建租户(%s)信息", notExistsIdNums));
            }

            //设置组织归属人
            TenantUserEntity organizationCreator = tenantUsers.stream()
                    .filter(ii -> ii.getSelectedCredential().getNumber().equals(command.getAdministrator().getCredentialNumber()))
                    .findFirst().get();

            AtomicReference<String> organizationId = new AtomicReference<>();
            //新增组织和部门
            this.unitWork.executeTran(() -> {
                OrganizationEntity organization = OrganizationEntity.create(command.getIndustryCode(), command.getInstitutionalCode(),
                        command.getName(), command.getTelephone(), command.getType(), command.getIndustryDetailType(),
                        command.getPlaceOfSupervise().getDistrict().getCode(), command.getSuperviseDepartId(), command.getSuperviseDepartName(),
                        command.getShareOrganizationId(),
                        command.getShareOrganizationName(), command.getShareEmployeeId(), command.getShareEmployeeName(),
                        command.getDynamic());

                organization.change(
                        translate2LocationValueType(command.getPlaceOfRegister()), translate2LocationValueType(command.getPlaceOfBusiness()));

                organizationId.set(organization.getId());

                DepartmentEntity department = null;

                if (organization.getDepartments() != null && organization.getDepartments().size() > 0) {
                    department = organization.getDepartments().stream().findFirst().get();
                }

                //添加员工信息
                for (TenantUserEntity tenantUser : tenantUsers) {
                    MasterSlaveType slaveType = MasterSlaveType.Normal;
                    String position = "普通员工";
                    boolean isDomainAdmin = false;
                    if (command.getAdministrator() != null
                            && tenantUser.getSelectedCredential().getNumber().equals(command.getAdministrator().getCredentialNumber())) {
                        position = "系统管理员";
                        slaveType = MasterSlaveType.Master;
                        isDomainAdmin = true;
                    }

                    organization.addEmployee(department, slaveType, tenantUser, position, isDomainAdmin,AuthUtil.getCurrentOperator());
                }
                //设置基础信息
                organization.setCompanyEntity(command);
                //设置组织归属人
                organization.changeOwner(organizationCreator);

                //广播事件
                TenantContext.getInstance()
                        .resolve(EventDispatcher.class)
                        .dispatch(OrganizationCreatedEvent.create(organization, organization.getName(),command.getDynamic()));

                this.organizationRepository.save(organization);

                organization.getEmployees().forEach(ii -> {
                    ii.dispatchAfterCreatedEvent(envConfig.isRequiredPhoneAsLoginName());
                });
            });

            return organizationId.get();
        } catch (Exception ex) {
            if (DomainConstraint.isUniqueConstraintIssue(ex)) {
                throw new ConflictTenantException("该企业信息已经存在!");
            }

            throw ex;
        }
    }

    @Override
    public void dispatch(ApproveCompanyCommand command) {
        Optional<OrganizationEntity> organizationOptional = this.organizationRepository.findById(command.getId());
        if (!organizationOptional.isPresent()) {
            throw new NotFoundTenantException("找不到企业信息");
        }

        OrganizationEntity organization = organizationOptional.get();
        String name = organization.getName();
        this.unitWork.executeTran(() -> {
            organization.approve(command.isApproved() ? ApprovedStatus.Passed : ApprovedStatus.NoPassed, command.getNote());
            if (!command.isApproved()) {
                //审批不通过，公司作废
                organization.abandon();
            }
            this.organizationRepository.save(organization);

            //记录日志
            TenantUserContext.UserModel userModel = TenantContext.getInstance().getUserContext().get();
            platformOperateLogRpcProvider.logOperate(new PlatformOperateLogRequest(userModel.getId(), userModel.getEmployeeId(),
                    userModel.getName(), command.isApproved() ? 3 : 4, null, command.isApproved() ? "企业："+name+"，审批通过" : "企业："+name+"，审批不通过", command.getNote(),
                    null));
        });

        Collection<TenantUserRepositoryDto> tenantUsers=this.organizationRepository.getOrganization(organization.getId(), TrueFalseStatus.True,command.isApproved() ? ApprovedStatus.Passed : ApprovedStatus.NoPassed);
        if (CollectionUtil.isNotEmpty(tenantUsers)) {
            if(organization.getApprovedInformationValueType()!=null &&
                    organization.getApprovedInformationValueType().getStatus() == ApprovedStatus.Passed){
                tenantUsers.stream().forEach(tenantUser->{
                    tenantEventService.create(CreateTenantEventCommand.create(
                            EventAction.OrganizationEvenForSms,
                            tenantUser.getId(),
                            tenantUser.getTelephone()));
                });
            } else {
                //发送短信，您提交的企业注册审核不通过，原因：xxx
                tenantUsers.forEach(tenantUser->{
                    Map<String, Object> huaWeiSmsContent = new HashMap<>();
                    huaWeiSmsContent.put("smsCode", "B08");

                    List<String> sendParams = new ArrayList<>();
                    sendParams.add(command.getNote());
                    huaWeiSmsContent.put("params", JSON.toJSONString(sendParams));

                    String telephone = tenantUser.getTelephone();
                    huaWeiSmsContent.put("mobile", telephone);

                    this.messageRpcProvider.dispatch(MessageRequest.create(
                            MessageType.SMS, huaWeiSmsContent
                    ));
                });
            }
        }
    }

    private static BatchCreateTenantUserCommand.TenantUserCommandItem translate2UserCommandItem(RegisterCompanyCommand.ContactCommandItem contact) {
        return BatchCreateTenantUserCommand.TenantUserCommandItem.create(
                contact.getName(), contact.getTelephone(),
                contact.getCredentialType(), contact.getCredentialNumber(), contact.getNationality());
    }

    private static void pushAndValidateCommandItem(Collection<BatchCreateTenantUserCommand.TenantUserCommandItem> commandItems,
                                                   BatchCreateTenantUserCommand.TenantUserCommandItem newItem) {
        if (commandItems.stream().anyMatch(ii -> ii.getTelephone().equals(newItem.getTelephone()) &&
                (ii.getCredentialType().equals(newItem.getCredentialType()) && !ii.getCredentialNumber().equals(newItem.getCredentialNumber())))
        ) {
            throw new BadTenantException(String.format("手机与证件号(%s)匹配存在冲突", newItem.getCredentialNumber()));
        }

        if (commandItems.stream().anyMatch(ii -> !ii.getTelephone().equals(newItem.getTelephone()) &&
                (ii.getCredentialType().equals(newItem.getCredentialType()) && ii.getCredentialNumber().equals(newItem.getCredentialNumber())))
        ) {
            throw new BadTenantException(String.format("手机与证件号(%s)匹配存在冲突", newItem.getCredentialNumber()));
        }

        commandItems.add(newItem);
    }

    protected LocationValueType translate2LocationValueType(LocationCommandItem locationCommandItem) {
        if (locationCommandItem == null) {
            return null;
        }

        return LocationValueType.create(
                translate2ItemValueType(locationCommandItem.getProvince()),
                translate2ItemValueType(locationCommandItem.getCity()),
                translate2ItemValueType(locationCommandItem.getDistrict()),
                locationCommandItem.getAddress()
        );
    }

    private ItemValueType translate2ItemValueType(ItemValueCommandItem item) {
        if (item == null) {
            return ItemValueType.create(null, null);
        }
        if (StringUtils.isBlank(item.getCode())) {
            return ItemValueType.create(null, null);
        }
        String name = item.getName();
        if (StringUtils.isBlank(name)) {
            Optional<RegionEntity> region = regionRepository.findById(item.getCode());
            if (region.isPresent()) {
                name = region.get().getRegionName();
            }
        }
        return ItemValueType.create(item.getCode(), name);
    }
}
