package com.bcxin.tenant.domain.entities;

import com.bcxin.Infrastructures.components.JsonProvider;
import com.bcxin.Infrastructures.entities.EntityAbstract;
import com.bcxin.Infrastructures.entities.IAggregate;
import com.bcxin.Infrastructures.enums.ApprovedStatus;
import com.bcxin.Infrastructures.enums.MemberType;
import com.bcxin.Infrastructures.enums.ResourceReferenceType;
import com.bcxin.api.interfaces.enums.ExternalMemberInviteType;
import com.bcxin.tenant.domain.DomainConstraint;
import com.bcxin.tenant.domain.entities.valueTypes.ApprovedInformationValueType;
import com.bcxin.tenant.domain.utils.BusinessUtil;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.*;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Getter
@Setter(AccessLevel.PROTECTED)
@Table(name = "external_members", uniqueConstraints =
@UniqueConstraint(name = DomainConstraint.FK_UNIQUE_DATA_EX_MEMBERS_TENANT_USER_ID_REF_NUM_TYPE, columnNames = {
        "tenant_user_id", "reference_number","reference_type"}))
@Entity
public class ExternalMemberEntity extends EntityAbstract implements IAggregate {

    @Id
    @Column(length = 50)
    private String id;

    @Column(length = 200, nullable = true)
    private String note;

    @Column(length = 50, nullable = false,name = "invite_code")
    private String inviteCode;

    @Column(length = 50, nullable = false,name = "invite_type")
    private ExternalMemberInviteType inviteType;

    @Column(length = 50, nullable = false,name = "invite_group_id")
    private String inviteGroupId;

    @Column(length = 200, nullable = false,name = "invite_group_name")
    private String inviteGroupName;

    /**
     * 所在分组的Json信息
     */
    @Column(length = 500, nullable = true,name = "group_ids_json")
    private String groupIdsJson;

    @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinColumn(name = "tenant_user_id", referencedColumnName = "id", nullable = false,
            foreignKey = @ForeignKey(name = "", value = ConstraintMode.CONSTRAINT))
    private TenantUserEntity tenantUser;

    /**
     * 审批状态
     */
    @Embedded
    @Column(name = "approved_information")
    @AttributeOverrides({
            @AttributeOverride(name = "status",
                    column = @Column(name = "approved_information_status")),
            @AttributeOverride(name = "lastUpdatedTime",
                    column = @Column(name = "approved_information_last_updated_time")),
            @AttributeOverride(name = "note",
                    column = @Column(name = "approved_information_note")),
    })
    private ApprovedInformationValueType approvedInformationValueType;

    /**
     * 参与时间
     */
    @Column(nullable = false, name = "join_time")
    private Timestamp joinTime;

    @Column(nullable = true, name = "deleted_time")
    private Timestamp deletedTime;

    @Column(nullable = true, name = "operator_employee_id")
    private String operatorEmployeeId;

    @Column(nullable = true, name = "operator_employee_name")
    private String operatorEmployeeName;

    @Column(nullable = false, name = "created_time")
    private Timestamp createdTime;

    @Column(nullable = true, name = "last_updated_time")
    private Timestamp lastUpdatedTime;

    @Column(nullable = false,name = "reference_type")
    private ResourceReferenceType referenceType;

    /**
     *  目前均指企业的Id
     */
    @Column(nullable = false,name = "reference_number",length = 200)
    private String referenceNumber;

    @Column(name = "member_type", nullable = false)
    @Enumerated(EnumType.ORDINAL)
    private MemberType memberType;

    public ExternalMemberEntity() {
        this.setCreatedTime(Timestamp.from(Instant.now()));
        this.setApprovedInformationValueType(ApprovedInformationValueType.create(ApprovedStatus.Init, ""));
    }

    public void join(JsonProvider jsonProvider, Collection<ExternalGroupEntity> groups) {
        if (!CollectionUtils.isEmpty(groups)) {
            Collection<String> groupIds = jsonProvider.toObjects(String.class, this.getGroupIdsJson());
            if (groupIds == null) {
                groupIds = new HashSet<>();
            }

            groupIds.addAll(groups.stream().map(ii -> ii.getId()).collect(Collectors.toList()));

            this.setGroupIdsJson(jsonProvider.getJson(groupIds.stream().distinct().collect(Collectors.toList())));
        } else {
            this.resetGroups();
           //this.setGroupIdsJson("[]");
        }

        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
    }

    public void leave(JsonProvider jsonProvider,Collection<ExternalGroupEntity> groups) {
        if (groups != null) {
            if (!CollectionUtils.isEmpty(groups)) {
                Collection<String> groupIds = jsonProvider.toObjects(String.class, this.getGroupIdsJson());
                if (groupIds == null) {
                    groupIds =  new HashSet<>();
                }

                groupIds.removeAll(groups.stream().map(ii -> ii.getId()).collect(Collectors.toList()));

                this.setGroupIdsJson(jsonProvider.getJson(groupIds.stream().distinct().collect(Collectors.toList())));
                this.setLastUpdatedTime(Timestamp.from(Instant.now()));
            }
        }
    }

    public void resetGroups() {
        this.setGroupIdsJson("[]");
    }

    public void delete(String operatorId,String operatorName) {
        this.setApprovedInformationValueType(ApprovedInformationValueType.create(
                ApprovedStatus.Deleted,
                "删除"
        ));

        this.markOperateLog(operatorId, operatorName);
    }

    public void changeStatus(ApprovedStatus status,String note,String operatorId,String operatorName) {
        this.setApprovedInformationValueType(ApprovedInformationValueType.create(
                status,
                note
        ));

        this.markOperateLog(operatorId, operatorName);
    }

    public void markOperateLog(String operatorId,String operatorName)
    {
        this.setOperatorEmployeeId(operatorId);
        this.setOperatorEmployeeName(operatorName);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
    }

    public static ExternalMemberEntity create(
            JsonProvider jsonProvider,
            String existEmpId,
            ExternalMemberInviteType inviteType,
            String inviteCode,
            TenantUserEntity tenantUser,
            ExternalGroupEntity group, OrganizationEntity organization) {
        ExternalMemberEntity externalMember = new ExternalMemberEntity();
        if (StringUtils.hasLength(existEmpId)) {
            externalMember.setId(existEmpId);
        } else {
            externalMember.setId(BusinessUtil.generateEmpMemberId(organization.getId(), tenantUser.getId()));
        }

        externalMember.setInviteType(inviteType);
        externalMember.setInviteCode(inviteCode);
        externalMember.setTenantUser(tenantUser);
        externalMember.setReferenceNumber(organization.getId());
        externalMember.setReferenceType(ResourceReferenceType.Organization);
        if (group != null) {
            externalMember.join(jsonProvider, Stream.of(group).collect(Collectors.toList()));
            externalMember.setInviteGroupId(group.getId());
            externalMember.setInviteGroupName(group.getName());
        } else {
            externalMember.join(jsonProvider, Collections.EMPTY_LIST);
        }

        externalMember.setMemberType(MemberType.Normal);
        return externalMember;
    }

    public static ExternalMemberEntity create(
            JsonProvider jsonProvider,
            TenantUserEntity tenantUser,OrganizationEntity organization) {
        ExternalMemberEntity externalMember = new ExternalMemberEntity();

        externalMember.setId(BusinessUtil.generateEmpMemberId(organization.getId(), tenantUser.getId()));
        externalMember.setInviteType(ExternalMemberInviteType.DirectlyJoin);
        externalMember.setInviteCode("");
        externalMember.setTenantUser(tenantUser);
        externalMember.setApprovedInformationValueType(
                ApprovedInformationValueType.create(ApprovedStatus.Passed, "直接通过")
        );
        externalMember.setReferenceNumber(organization.getId());
        externalMember.setReferenceType(ResourceReferenceType.Organization);
        externalMember.join(jsonProvider, Collections.EMPTY_LIST);

        externalMember.setMemberType(MemberType.Master);

        return externalMember;
    }
}
