package com.bcxin.tenant.domain.entities;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdcardUtil;
import com.alibaba.excel.util.DateUtils;
import com.bcxin.Infrastructures.TenantContext;
import com.bcxin.Infrastructures.components.EventDispatcher;
import com.bcxin.Infrastructures.entities.EntityAbstract;
import com.bcxin.Infrastructures.entities.IAggregate;
import com.bcxin.Infrastructures.entities.OperatorValueType;
import com.bcxin.Infrastructures.enums.*;
import com.bcxin.Infrastructures.exceptions.ArgumentTenantException;
import com.bcxin.Infrastructures.exceptions.BadTenantException;
import com.bcxin.Infrastructures.exceptions.NotAllowedTenantException;
import com.bcxin.Infrastructures.utils.AuthUtil;
import com.bcxin.Infrastructures.utils.RetirementAgeUtil;
import com.bcxin.Infrastructures.utils.UUIDUtil;
import com.bcxin.Infrastructures.validations.DateValidator;
import com.bcxin.tenant.domain.DomainConstraint;
import com.bcxin.tenant.domain.enums.EmployeeEventType;
import com.bcxin.tenant.domain.events.*;
import com.bcxin.tenant.domain.utils.BusinessUtil;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.StringUtils;

import javax.persistence.*;
import java.sql.Timestamp;
import java.text.ParseException;
import java.time.Instant;
import java.util.*;

@Getter
@Setter(AccessLevel.PROTECTED)
@Table(name = "tenant_employees", uniqueConstraints =
@UniqueConstraint(name = DomainConstraint.UNIQUE_ORGANIZATION_EMPLOYEE_STATUS, columnNames = {
        "organization_id", "tenant_user_id"}))
@Entity
public class EmployeeEntity extends EntityAbstract implements IAggregate {
    @Id
    @Column(length = 100)
    private String id;

    /**
     * 工号
     */
    @Column(name = "job_number", length = 50)
    private String jobNumber;

    /**
     * 职业: 保安员, 其他
     */
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "occupation_type", nullable = false)
    private OccupationType occupationType;

    /**
     * 待入职，入职, 离职申请中，离职
     */
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "status", nullable = false)
    private EmploymentStatus status;

    /**
     * 入职操作人
     */
    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "id",
                    column = @Column(name = "hired_operator_id", nullable = true, length = 150)),
            @AttributeOverride(name = "name",
                    column = @Column(name = "hired_operator_name", nullable = true, length = 200)),
            @AttributeOverride(name = "createdTime",
                    column = @Column(name = "hired_operator_created_time", nullable = true))
    })
    private OperatorValueType hiredOperator;

    /**
     * 离职时间
     */
    @Column(name = "leave_time", nullable = true)
    private Timestamp leaveTime;

    @Column(name = "leave_note", length = 2000)
    private String leaveNote;

    @Embedded
    @AttributeOverrides({
            @AttributeOverride(name = "id",
                    column = @Column(name = "leave_operator_id", nullable = true, length = 150)),
            @AttributeOverride(name = "name",
                    column = @Column(name = "leave_operator_name", nullable = true, length = 200)),
            @AttributeOverride(name = "createdTime",
                    column = @Column(name = "leave_operator_created_time", nullable = true))
    })
    private OperatorValueType leaveOperator;

    /**
     * 入职时间
     */
    @Column(nullable = true, name = "hired_date")
    @Temporal(TemporalType.TIMESTAMP)
    private Date hiredDate;

    /**
     * 转正时间
     */
    @Column(nullable = true, name = "positive_date")
    @Temporal(TemporalType.DATE)
    private Date positiveDate;


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

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

    /**
     * 最后同步时间
     */
    @Column(name = "last_sync_time")
    private Timestamp lastSyncTime;

    /**
     * 薪资
     */
    @Column(length = 50)
    private String salary;


    /**
     * 岗位
     */
    @Column(length = 200)
    private String position;

    /**
     * 工作地点
     */
    @Column(length = 1000)
    private String workPlace;

    /**
     * 工资卡卡号
     */
    @Column(length = 100)
    private String salaryBankNumber;

    /**
     * 直接上级
     */
    @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinColumn(name = "superior_id", referencedColumnName = "id", nullable = true, foreignKey = @ForeignKey(name = "fk_employee_self_superior_id"))
    private EmployeeEntity superior;

    /**
     * 企业邮箱
     */
    @Column(length = 50)
    private String email;

    @Column(name = "unique_offset", length = 100)
    private String uniqueOffset;

    /**
     * 是否投保
     */
    @Column(name = "insure", nullable = false)
    private TrueFalseStatus insure;

    @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
    private Collection<DepartmentEmployeeRelationEntity> departmentEmployeeRelations;

    /**
     * 设置为All, 又无法进行离职操作
     */
    @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinColumn(name = "organization_id", referencedColumnName = "id")
    private OrganizationEntity organization;

    /**
     * 是否为企业负责人/组织管理员
     */
    @Column(name = "master_slave_type", nullable = false)
    private MasterSlaveType masterSlaveType;

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

    @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Collection<EmployeeEventEntity> events;

    @Column(name = "is_domain_admin", nullable = false)
    private TrueFalseStatus domainAdmin;


    @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id", referencedColumnName = "id",
            foreignKey = @ForeignKey(name = "fk_default_department_id", value = ConstraintMode.CONSTRAINT))
    private DepartmentEntity defaultDepartment;

    /**
     * 面谈记录
     */
    @Column(name = "interview")
    private String interview;

    /**
     * 员工状态（选填，单选：全职/兼职/试用/实习）
     */
    @Column(name = "person_status")
    private PersonStatus personStatus;

    /**
     * 试用期 数字
     */
    @Column(name = "probation")
    private String probation;


    /**
     * 原定转正日期 年月日
     */
    @Column(nullable = true, name = "plan_positive_date")
    @Temporal(TemporalType.DATE)
    private Date planPositiveDate;

    protected EmployeeEntity() {
        this.createdTime = Timestamp.from(Instant.now());
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    protected EmployeeEntity(
            OrganizationEntity organization,
            OccupationType occupationType,
            CredentialType credentialType,
            String credentialNumber) {
        this();
        this.changeOccupationType(occupationType, credentialType, credentialNumber,organization.getSuperviseRegionCode(), organization.getInstitutionalCode());

        /**
         * changed by lhc: 根据tenant_users.id+org.id来组合生成emp,id
         * 更好地解决：先成为团队成员在作为职员的情况
         */
        //this.setId(UUIDUtil.getShortUuid());
        this.setOrganization(organization);
        this.markAsDomainAdmin(false, true);
    }

    /**
     * 只适用于新员工加入部门
     *
     * @param department
     * @param masterSlaveType
     */
    public void joinDepartment(DepartmentEntity department, MasterSlaveType masterSlaveType) {
        Collection<DepartmentEmployeeRelationEntity> departmentEmployeeRelationEntities = this.getDepartmentEmployeeRelations();
        if (departmentEmployeeRelationEntities == null) {
            departmentEmployeeRelationEntities = new ArrayList<>();
        }
        /**
         * 先退出之前的部门，避免一个人入职多部门的异常
         */
        Optional<DepartmentEmployeeRelationEntity> departmentEmployeeRelationOptional
                = departmentEmployeeRelationEntities.stream().findFirst();
        DepartmentEmployeeRelationEntity selectedDepartmentEmployee = null;
        if (departmentEmployeeRelationOptional.isPresent()) {
            selectedDepartmentEmployee = departmentEmployeeRelationOptional.get();
        } else {
            selectedDepartmentEmployee = DepartmentEmployeeRelationEntity.create(department, this, masterSlaveType, true);
            departmentEmployeeRelationEntities.add(selectedDepartmentEmployee);
        }

        department.incrEmployee();
        selectedDepartmentEmployee.setDepartment(department);
        selectedDepartmentEmployee.setDepartmentIndexTree(department.getIndexTree());
        selectedDepartmentEmployee.setMasterSlaveType(masterSlaveType);

        this.setDepartmentEmployeeRelations(departmentEmployeeRelationEntities);
        this.markDefaultDepartment(department);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    /**
     * 加入部门
     *
     * @param departments
     * @param masterSlaveType
     */
    public void joinDepartments(Collection<DepartmentEntity> departments, MasterSlaveType masterSlaveType) {
        Collection<DepartmentEmployeeRelationEntity> dbDepartmentEmployeeRelationEntities = this.getDepartmentEmployeeRelations();
        Collection<DepartmentEmployeeRelationEntity> departmentEmployeeRelationEntities = new ArrayList<>();
        Map<String, DepartmentEmployeeRelationEntity> departMap = new HashMap<>();
        if (dbDepartmentEmployeeRelationEntities != null) {
            for (DepartmentEmployeeRelationEntity dbDepartmentEmployeeRelationEntity : dbDepartmentEmployeeRelationEntities) {
                departMap.put(dbDepartmentEmployeeRelationEntity.getDepartment().getId(), dbDepartmentEmployeeRelationEntity);
            }
        }

        for (DepartmentEntity department : departments) {
            if (departMap.get(department.getId()) != null) {
                departmentEmployeeRelationEntities.add(departMap.get(department.getId()));
            } else {
                departmentEmployeeRelationEntities.add(DepartmentEmployeeRelationEntity.create(department, this, masterSlaveType, true));
            }
            department.incrEmployee();
            this.markDefaultDepartment(department);
        }
        this.setDepartmentEmployeeRelations(departmentEmployeeRelationEntities);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    /**
     * 转移部门
     *
     * @param destDepartment
     */
    public void move2Department(DepartmentEntity destDepartment) {
        Collection<DepartmentEmployeeRelationEntity> departmentEmployeeRelationEntities =
                this.getDepartmentEmployeeRelations();
        for (DepartmentEmployeeRelationEntity relation : departmentEmployeeRelationEntities) {
            relation.gotoDepartment(destDepartment);
        }
        this.setDepartmentEmployeeRelations(departmentEmployeeRelationEntities);


        this.markDefaultDepartment(destDepartment);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    public void markDefaultDepartment(DepartmentEntity department) {
        this.setDefaultDepartment(department);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    /**
     * 是否设置为组织管理员
     */
    public void markAsAdmin(MasterSlaveType masterSlaveType) {
        this.setMasterSlaveType(masterSlaveType);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    /**
     * 修改邮箱
     *
     * @param email
     * @param hiredDate
     */
    public void change(String email, Date hiredDate) {
        this.setEmail(email);
        this.setHiredDate(hiredDate);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
        Timestamp hiredDateTimestamp = Timestamp.valueOf(DateUtil.toLocalDateTime(hiredDate));
        //修改职员事件表时间
        this.recordEvent(EmployeeHiredDateChangeEvent.create(this, EmployeeEventType.Other, hiredDateTimestamp, ""));

    }

    /**
     * 修改入职时间，职位，转正时间，上级
     *
     * @param hiredDate
     * @param position
     * @param superior
     */
    public void change(Date hiredDate, String position, Date positiveDate, EmployeeEntity superior,
                       String interview,
                       PersonStatus personStatus,
                       String probation,
                       Date planPositiveDate,
                       String salary) {
        if (AuthUtil.isBeiJingCompany()) {
            this.setHiredDate(hiredDate);
        }
        this.setPosition(position);
        this.changeSuperior(superior);
        this.setPositiveDate(positiveDate);
        this.setInterview(interview);
        this.setPersonStatus(personStatus);
        this.setProbation(probation);
        this.setPlanPositiveDate(planPositiveDate);
        this.setSalary(salary);

        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
        Timestamp hiredDateTimestamp = Timestamp.valueOf(DateUtil.toLocalDateTime(hiredDate));
        //修改职员事件表时间
        this.recordEvent(EmployeeHiredDateChangeEvent.create(this, EmployeeEventType.Other, hiredDateTimestamp, ""));
    }

    public void leave(Date expectedDate, String note, OperatorValueType leaveOperator) {
        if (this.getStatus() == EmploymentStatus.OffJob) {
            throw new NotAllowedTenantException(String.format("该职员(%s)已经处于离职状态", this.getId()));
        }

        if (this.getDomainAdmin() == TrueFalseStatus.True) {
            throw new NotAllowedTenantException(String.format("该职员(%s)为组织管理员，不允许离职，请转移后重试", this.getId()));
        }

        Timestamp leaveTimeStamp = Timestamp.from(expectedDate.toInstant());

        this.setStatus(EmploymentStatus.OffJob);
        this.setLeaveTime(leaveTimeStamp);
        this.setLeaveNote(note);
        this.setLeaveOperator(leaveOperator);
        this.setUniqueOffset(String.format("Lv%s", Instant.now()));
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));

        this.recordEvent(EmployeeUpdatedEvent.create(this, EmployeeEventType.LeaveJob, leaveTime, note));
    }

    public void back(
            EventDispatcher eventDispatcher,
            DepartmentEntity department,
            OccupationType occupationType,
            Date hiredDate,
            OperatorValueType hiredOperator,
            boolean isDuplicateValidate
    ) {
        if (this.getStatus() != EmploymentStatus.OffJob) {
            return;
        }

        TenantUserEntity tenantUser = this.getTenantUser();
        TenantUserCredentialsEntity userCredential
                = tenantUser.getSelectedCredential();

        CredentialType credentialType = null;
        String credentialNumber = null;
        //todo 默认现在只有一种证件类型而已
        //todo 后续有多种的时候, 必须进行循环验证, 主要有即可.
        if (userCredential != null) {
            credentialType = userCredential.getCredentialType();
            credentialNumber = userCredential.getNumber();
        }

        this.changeOccupationType(occupationType, credentialType, credentialNumber,this.getOrganization().getSuperviseRegionCode(),this.getOrganization().getInstitutionalCode());

        this.setStatus(EmploymentStatus.OnJob);
        this.setHiredDate(Timestamp.from(Instant.now()));
        this.setHiredOperator(hiredOperator);
        this.setLeaveTime(null);
        this.setLeaveNote("复职");

        //通过转移部门的方式来实现复职时候的部门变更
        /**
         * 回来之后需要
         */
        Collection<DepartmentEmployeeRelationEntity> newDepartRelations = new ArrayList<>();
        newDepartRelations.add(
                DepartmentEmployeeRelationEntity.create(department, this, MasterSlaveType.Normal, true));
        this.setDepartmentEmployeeRelations(newDepartRelations);

        this.move2Department(department);
        this.setHiredDate(hiredDate);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));

        /**
         * 针对新增的情况; 由于我们在新增的时候已经验证过数据, 因此, 这边无需重复验证
         * dispatch(CreateEmployeeRequestCommand command)
         */
        if (!isDuplicateValidate) {
            this.doValidateBeforeBackOrCreateEvent(eventDispatcher, true);
        }

        this.recordEvent(
                EmployeeUpdatedEvent.create(this,
                        EmployeeEventType.EntryJob,
                        Timestamp.valueOf(DateUtil.toLocalDateTime(hiredDate)), "复职"));
    }

    public void changeSuperior(EmployeeEntity superior) {
        if (superior != null && superior.getId().equals(this.getId())) {
            throw new NotAllowedTenantException("无法设置自己为自己的上级");
        }

        this.setSuperior(superior);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    public String getSelectIdNum() {
        return this.getTenantUser().getSelectIdNum();
    }

    public void changeOccupationType(OccupationType occupationType, CredentialType credentialType, String credentialNumber,String areaCode, String institutionalCode) {
        if (occupationType == null) {
            throw new ArgumentTenantException("职业信息不能为空");
        }

        if (occupationType == OccupationType.SecurityGuard) {
            if (credentialType != CredentialType.IdCard) {
                throw new ArgumentTenantException("保安员必须上传身份证信息");
            }
            if (!StringUtils.hasLength(credentialNumber)) {
                throw new ArgumentTenantException("身份证号不能为空");
            }
            Date d=new Date();
            d.setHours(0);
            d.setMinutes(0);
            d.setSeconds(0);
            Date birthDate =IdcardUtil.getBirthDate(credentialNumber);
            if (institutionalCode == null || !institutionalCode.startsWith("02")) {
                if (DateValidator.dateAddYear(birthDate,18).after(d)) {
                    throw new ArgumentTenantException("保安员年龄必须大于等于18岁");
                }
            }
            if (institutionalCode == null || !institutionalCode.startsWith("02")) {
                //不是大活行业，才需要限制年龄
                if (AuthUtil.isBeijingCompany(areaCode)) {
                    if (IdcardUtil.getGenderByIdCard(credentialNumber) == 0) {
                        if (RetirementAgeUtil.isExceedRetirementAge(birthDate, 50)) {
                            throw new ArgumentTenantException("根据最新人社部规定，该人员已超龄");
                        }
                    } else if (RetirementAgeUtil.isExceedRetirementAge(birthDate, 60)) {
                        throw new ArgumentTenantException("根据最新人社部规定，该人员已超龄");
                    }
                } else if (AuthUtil.isHunanCompany(areaCode)) {
                    if (IdcardUtil.getGenderByIdCard(credentialNumber) == 0) {
                        if (DateValidator.dateAddYear(birthDate, 60).before(d)) {
                            throw new ArgumentTenantException("入职女保安员, 年龄必须小于等于60岁");
                        }
                    } else if (DateValidator.dateAddYear(birthDate, 60).before(d)) {
                        throw new ArgumentTenantException("入职男保安员, 年龄必须小于等于60岁");
                    }
                } else if (AuthUtil.isNingxiaCompany(areaCode)) {
                    if (IdcardUtil.getGenderByIdCard(credentialNumber) == 0) {
                        if (DateValidator.dateAddYear(birthDate, 55).before(d)) {
                            throw new ArgumentTenantException("入职女保安员, 年龄必须小于等于55岁");
                        }
                    } else if (DateValidator.dateAddYear(birthDate, 60).before(d)) {
                        throw new ArgumentTenantException("入职男保安员, 年龄必须小于等于60岁");
                    }
                } else if (AuthUtil.isShanxiCompany(areaCode)) {
                    if (IdcardUtil.getGenderByIdCard(credentialNumber) == 0) {
                        if (DateValidator.dateAddYear(birthDate, 58).before(d)) {
                            throw new ArgumentTenantException("入职女保安员, 年龄必须小于等于58岁");
                        }
                    } else if (DateValidator.dateAddYear(birthDate, 63).before(d)) {
                        throw new ArgumentTenantException("入职男保安员, 年龄必须小于等于63岁");
                    }
                }
            }
        }
        this.setOccupationType(occupationType);
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    public static EmployeeEntity create(
            EventDispatcher eventDispatcher,
            OrganizationEntity organization,
            DepartmentEntity defaultDepartment,
            String name, String telephone,
            String password, String position,
            Date hiredDate,
            MasterSlaveType masterSlaveType,
            CredentialType credentialType,
            String credentialNumber,
            OccupationType occupationType,
            boolean ensureExistsUser,
            OperatorValueType hiredOperator) {
        EmployeeEntity employee =
                new EmployeeEntity(organization, occupationType, credentialType, credentialNumber);
        employee.setStatus(EmploymentStatus.OnJob);
        employee.markAsAdmin(masterSlaveType);
        employee.setPosition(position);
        employee.setHiredDate(hiredDate);
        employee.setInsure(TrueFalseStatus.False);
        employee.setStatus(EmploymentStatus.OnJob);
        employee.joinDepartment(defaultDepartment, masterSlaveType);
        employee.setHiredOperator(hiredOperator);

        Sex sex = getSexByCredential(credentialType, credentialNumber);
        Date birthdate = getBirthdateByCredential(credentialType, credentialNumber);

        /**
         * 职员创建前通过ensureExistsUser来创建其他域的用户信息
         */
        EmployeeBeforeCreatedEvent employeeBeforeCreatedEvent = EmployeeBeforeCreatedEvent.create(
                employee, name, telephone, password, credentialType, credentialNumber, masterSlaveType, ensureExistsUser, sex, birthdate);
        employee.recordEvent(employeeBeforeCreatedEvent);

        /**
         * 从如上的事件EmployeeCreatedEvent产生的结果中获取租户信息
         */
        TenantUserEntity tenantUser = TenantContext.getInstance().getValueAndRemove(employee);
        if (tenantUser == null) {
            throw new BadTenantException("租户信息无效,无法创建员工, 请联系管理员");
        }
        //employee.setTenantUser(tenantUser);
        employee.assignTenantUser(tenantUser,organization);

        /**
         * 创建之前, 发布进行验证
         */
        employee.doValidateBeforeBackOrCreateEvent(eventDispatcher,false);

        return employee;
    }


    /**
     * description: 批量导入创建
     * author: linchunpeng
     * date:  2023-05-10 15:58
     */
    public static EmployeeEntity create(
            EventDispatcher eventDispatcher,
            OrganizationEntity organization,
            DepartmentEntity defaultDepartment,
            String name, String telephone,
            String password,
            String position,
            Date hiredDate,
            MasterSlaveType masterSlaveType,
            CredentialType credentialType,
            String credentialNumber,
            OccupationType occupationType,
            String nation,
            String education,
            String politicsStatus,
            String militaryStatus,
            String maritalStatus,
            String emergencyContact,
            String emergencyPhone,
            String address,
            String householdType,
            boolean ensureExistsUser,
            OperatorValueType hiredOperator) {
        EmployeeEntity employee = new EmployeeEntity(organization, occupationType, credentialType, credentialNumber);
        employee.setStatus(EmploymentStatus.OnJob);
        employee.markAsAdmin(masterSlaveType);
        employee.setPosition(position);
        employee.setHiredDate(hiredDate);
        employee.setInsure(TrueFalseStatus.False);
        employee.setStatus(EmploymentStatus.OnJob);
        employee.joinDepartment(defaultDepartment, masterSlaveType);
        employee.setHiredOperator(hiredOperator);

        Sex sex = getSexByCredential(credentialType, credentialNumber);
        Date birthdate = getBirthdateByCredential(credentialType, credentialNumber);

        EmployeeBeforeCreatedEvent employeeBeforeCreatedEvent =
                EmployeeBeforeCreatedEvent.create(
                employee, name, telephone, password, credentialType, credentialNumber, masterSlaveType, ensureExistsUser,
                sex, birthdate, nation, education, politicsStatus, militaryStatus, maritalStatus, emergencyContact,
                emergencyPhone, address, householdType);
        employee.recordEvent(employeeBeforeCreatedEvent);

        /**
         * 从如上的事件EmployeeCreatedEvent产生的结果中获取租户信息
         */
        TenantUserEntity tenantUser = TenantContext.getInstance().getValueAndRemove(employee);
        if (tenantUser == null) {
            throw new BadTenantException("租户信息无效,无法创建员工, 请联系管理员");
        }
        //employee.setTenantUser(tenantUser);
        employee.assignTenantUser(tenantUser,organization);

        /**
         * 创建之前, 发布进行验证
         */
        employee.doValidateBeforeBackOrCreateEvent(eventDispatcher,false);

        return employee;
    }

    /**
     * 创建雇员
     * 如果为保安员的话, 那么必须验证上传的证件类型和身份证信息
     *
     * @param organization
     * @param tenantUser
     * @return
     */
    public static EmployeeEntity create(
            EventDispatcher eventDispatcher,
            OrganizationEntity organization,
            DepartmentEntity defaultDepartment,
            TenantUserEntity tenantUser,
            MasterSlaveType masterSlaveType,
            String position,
            OccupationType occupationType,
            CredentialType credentialType,
            String credentialNumber,
            boolean isDomainAdmin,
            OperatorValueType hiredOperator) {
        EmployeeEntity employee = new EmployeeEntity(organization, occupationType, credentialType, credentialNumber);
        employee.setStatus(EmploymentStatus.OnJob);
        employee.setHiredDate(new Date());
        employee.markAsAdmin(masterSlaveType);
        employee.setPosition(position);
        employee.setInsure(TrueFalseStatus.False);
        employee.markAsDomainAdmin(isDomainAdmin, true);
        employee.setHiredOperator(hiredOperator);
        //通过身份证加上生日信息

        //employee.setTenantUser(tenantUser);
        employee.assignTenantUser(tenantUser,organization);
        employee.joinDepartment(defaultDepartment, masterSlaveType);

        employee.recordEvent(
                EmployeeBeforeCreatedEvent.create(
                        employee, tenantUser.getName(), tenantUser.getTelephone(), null,
                        tenantUser.getSelectedCredential() == null ? null : tenantUser.getSelectedCredential().getCredentialType(),
                        tenantUser.getSelectedCredential() == null ? null : tenantUser.getSelectedCredential().getNumber(),
                        masterSlaveType, false, null, null)
        );

        employee.doValidateBeforeBackOrCreateEvent(eventDispatcher,false);

        return employee;
    }

    public void markAsDomainAdmin(boolean domainAdmin, boolean isFromNew) {
        this.setDomainAdmin(domainAdmin ? TrueFalseStatus.True : TrueFalseStatus.False);
        if (!isFromNew) {
            this.recordEvent(
                    EmployeeUpdatedEvent.create(
                            this, domainAdmin ? EmployeeEventType.AsAdmin : EmployeeEventType.AsUser, Timestamp.from(Instant.now()),
                            domainAdmin ? "设置为组织管理员" : "移除组织管理员")
            );
        }
        this.setLastUpdatedTime(Timestamp.from(Instant.now()));
        this.setLastSyncTime(Timestamp.from(Instant.now()));
    }

    public void dispatchAfterCreatedEvent(boolean isRequiredPhoneAsLoginName) {
        EmployeeAfterCreatedEvent employeeAfterCreatedEvent =
                EmployeeAfterCreatedEvent.create(this, tenantUser.getTelephone(), isRequiredPhoneAsLoginName);
        this.recordEvent(employeeAfterCreatedEvent);
    }


    private static boolean isEvenNumber(int num) {
        if (num == 0)
            return true;
        else if (num % 2 == 0)
            return true;
        else
            return false;
    }


    private static Sex getSexByCredential(CredentialType credentialType, String credentialNumber) {
        Sex sex = null;
        if (credentialType.ordinal() == CredentialType.IdCard.ordinal() && StringUtils.hasLength(credentialNumber)) {
            //证件类型是身份证，需要根据身份证号码，获取出生日期、性别
            String sexText = "";
            if (credentialNumber.length() == 15) {
                //15位身份证
                sexText = credentialNumber.substring(14);
            } else if (credentialNumber.length() == 18) {
                //18位身份证
                sexText = credentialNumber.substring(16, 17);
            } else {
                throw new ArgumentTenantException("身份证号码格式错误");
            }
            sex = isEvenNumber(Integer.parseInt(sexText)) ? Sex.Female : Sex.Male;
        }
        return sex;
    }

    private static Date getBirthdateByCredential(CredentialType credentialType, String credentialNumber) {
        Date birthdate = null;
        if (credentialType.ordinal() == CredentialType.IdCard.ordinal() && StringUtils.hasLength(credentialNumber)) {
            //证件类型是身份证，需要根据身份证号码，获取出生日期、性别
            String birthdateText = "";
            if (credentialNumber.length() == 15) {
                //15位身份证
                birthdateText = "19" + credentialNumber.substring(6, 12);
            } else if (credentialNumber.length() == 18) {
                //18位身份证
                birthdateText = credentialNumber.substring(6, 14);
            } else {
                throw new ArgumentTenantException("身份证号码格式错误");
            }

            try {
                birthdate = DateUtils.parseDate(birthdateText, "yyyyMMdd");
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return birthdate;
    }

    private void doValidateBeforeBackOrCreateEvent(EventDispatcher eventDispatcher,boolean isBack) {
        TenantUserEntity tenantUser = this.getTenantUser();
        TenantUserCredentialsEntity userCredential
                = tenantUser.getSelectedCredential();

        CredentialType credentialType = null;
        String credentialNumber = null;
        //todo 默认现在只有一种证件类型而已
        //todo 后续有多种的时候, 必须进行循环验证, 主要有即可.
        if (userCredential != null) {
            credentialType = userCredential.getCredentialType();
            credentialNumber = userCredential.getNumber();
        }

        String tenantUserId = tenantUser.getId();
        EmployeeBeforeEntryValidationEvent employeeBeforeEntryValidationEvent =
                EmployeeBeforeEntryValidationEvent.create(
                        this.getOrganization().getSuperviseRegionCode(),
                        this.getOrganization().getId(),
                        Collections.singleton(
                                EmployeeBeforeEntryValidationEvent.TenantUserOccupationTypeInfo.create(
                                        tenantUserId,
                                        tenantUser.getName(),
                                        occupationType,
                                        credentialType,
                                        credentialNumber,
                                        tenantUser.getTelephone(),
                                        isBack
                                )
                        )
                );
        /**
         * 广播进行职业类型, 区域和年龄的限制
         */
        eventDispatcher.dispatch(employeeBeforeEntryValidationEvent);
    }

    public void assignTenantUser(TenantUserEntity user,OrganizationEntity organization) {
        this.setId(BusinessUtil.generateEmpMemberId(organization.getId(), user.getId()));

        this.setTenantUser(user);
    }
}
