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

import com.bcxin.Infrastructures.IdWorker;
import com.bcxin.Infrastructures.UnitWork;
import com.bcxin.Infrastructures.enums.ResourceReferenceType;
import com.bcxin.Infrastructures.exceptions.*;
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.repositories.ExternalGroupRepository;
import com.bcxin.tenant.domain.repositories.ExternalMemberRepository;
import com.bcxin.tenant.domain.repositories.OrganizationRepository;
import com.bcxin.tenant.domain.services.ExternalGroupService;
import com.bcxin.tenant.domain.services.commands.externalGroups.CreateExternalGroupCommand;
import com.bcxin.tenant.domain.services.commands.externalGroups.DeleteExternalGroupCommand;
import com.bcxin.tenant.domain.services.commands.externalGroups.UpdateExternalGroupCommand;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Service
public class ExternalGroupServiceImpl implements ExternalGroupService {
    private final UnitWork unitWork;

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

    private final ExternalMemberRepository externalMemberRepository;

    public ExternalGroupServiceImpl(UnitWork unitWork,
                                    IdWorker idWorker,
                                    ExternalGroupRepository externalGroupRepository,
                                    OrganizationRepository organizationRepository,
                                    ExternalMemberRepository externalMemberRepository) {
        this.unitWork = unitWork;
        this.idWorker = idWorker;
        this.externalGroupRepository = externalGroupRepository;
        this.organizationRepository = organizationRepository;
        this.externalMemberRepository = externalMemberRepository;
    }

    @Override
    public void update(UpdateExternalGroupCommand command) {
        ExternalGroupEntity externalGroup = this.externalGroupRepository.findById(command.getId()).orElse(null);
        if (externalGroup == null) {
            throw new NotFoundTenantException();
        }

        if (!externalGroup.getReferenceNumber().equalsIgnoreCase(command.getReferenceNumber())) {
            throw new ForbidTenantException("禁止修改该分组信息");
        }

        assignExternalGroupInfo(externalGroup,command.getPrincipalId(),command.getParentId());
        externalGroup.change(command.getName(), command.getDisplayOrder());

        this.unitWork.executeTran(() -> {
            this.externalGroupRepository.save(externalGroup);
        });
    }

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

        String id = String.valueOf(this.idWorker.nextId());

        ExternalGroupEntity externalGroup = ExternalGroupEntity.create(ResourceReferenceType.Organization,
                organization.getId(),
                id,
                command.getName(), command.getDisplayOrder()
        );

        this.assignExternalGroupInfo(externalGroup,command.getPrincipalId(),command.getParentId());

        this.unitWork.executeTran(() -> {
            this.externalGroupRepository.save(externalGroup);
        });
    }

    @Override
    public void delete(DeleteExternalGroupCommand command) {
        ExternalGroupEntity externalGroup = this.externalGroupRepository.findById(command.getId()).orElse(null);
        if (externalGroup == null) {
            throw new NotFoundTenantException();
        }

        if (!externalGroup.getReferenceNumber().equalsIgnoreCase(command.getReferenceNumber())) {
            throw new ForbidTenantException("无权限访问该分组信息");
        }

        try
        {
            this.unitWork.executeTran(()->{
                this.externalGroupRepository.delete(externalGroup);
            });
        }
        catch (Exception ex) {
            if (DomainConstraint.isRefForeignIssue(ex)) {
                throw new BadTenantException("删除该分组之前、请先删除子分组或者分组成员信息");
            }

            throw ex;
        }
    }

    private void assignExternalGroupInfo(ExternalGroupEntity externalGroup, String principalId, String parentId) {
        ExternalMemberEntity externalMember = null;
        if (StringUtils.hasLength(principalId)) {
            externalMember = this.externalMemberRepository.findById(principalId).orElse(null);
        }

        externalGroup.assignPrincipal(externalMember);

        ExternalGroupEntity parent = null;
        if (StringUtils.hasLength(parentId)) {
            parent = this.externalGroupRepository.findById(parentId).orElse(null);

            if (parent != null && !externalGroup.getReferenceNumber().equalsIgnoreCase(parent.getReferenceNumber())) {
                throw new ArgumentTenantException(String.format("数据(%s)无效(所属组织不一致)", parentId));
            }

            externalGroup.assign(parent);
        } else {
            externalGroup.assign(null);
        }
    }
}
