package com.bcxin.tenant.open.domains.entities;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.bcxin.tenant.open.domains.pojo.OrganizationPoJo;
import com.bcxin.tenant.open.domains.pojo.RollCallRulePoJo;
import com.bcxin.tenant.open.domains.pojo.SecurityDepartPoJo;
import com.bcxin.tenant.open.domains.pojo.SecurityStationPoJo;
import com.bcxin.tenant.open.infrastructures.TenantEmployeeContext;
import com.bcxin.tenant.open.infrastructures.components.JsonProvider;
import com.bcxin.tenant.open.infrastructures.entities.Aggregate;
import com.bcxin.tenant.open.infrastructures.entities.EntityAbstract;
import com.bcxin.tenant.open.infrastructures.enums.RollCallStatus;
import com.bcxin.tenant.open.infrastructures.enums.RollCallType;
import com.bcxin.tenant.open.infrastructures.exceptions.BadTenantException;
import com.bcxin.tenant.open.infrastructures.exceptions.IgnoreTenantException;
import com.bcxin.tenant.open.infrastructures.utils.StringUtil;
import lombok.Data;
import org.apache.ibatis.type.EnumOrdinalTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 点名的人员信息
 */
@Data
@TableName(value = "vga_roll_call_employees",autoResultMap = true)
public class RollCallEmployeeEntity  extends EntityAbstract implements Aggregate {
    @TableId("id")
    private String id;

    @TableField("roll_call_id")
    private Long rollCallId;

    @TableField("roll_call_plan_id")
    private Long rollCallPlanId;

    /**
     * 被调度-驻勤点信息
     * {"id":"","name":"xx","stationTypes":[]}
     */
    @TableField("security_station_json")
    private String securityStationJson;

    /**
     * 被调度-辖区信息:
     * {"id":"","name":"dddddd","employeeName":""}
     */
    @TableField("security_depart_json")
    private String securityDepartJson;

    /**
     * 发起点名的时间
     */
    @TableField("created_time")
    private Timestamp createdTime;

    /**
     * 被点名的人员Id
     */
    @TableField("called_employee_id")
    private String calledEmployeeId;

    /**
     * 被点名人员的Cid
     */
    @TableField("called_tenant_user_cid")
    private String calledTenantUserCid;

    @TableField("last_updated_tenant_user_cid_time")
    private Timestamp lastUpdatedTenantUserCIdTime;

    /**
     * 被点名的人员Id
     */
    @TableField("called_tenant_user_id")
    private String calledTenantUserId;

    /**
     * 调度过程中使用的唯一Id
     */
    @TableField("called_tencent_user_id")
    private String calledTencentUserId;

    /**
     * 被点名的组织Id
     */
    @TableField("called_organization_id")
    private String calledOrganizationId;

    /**
     * 被点名的组织名称
     */
    @TableField("called_organization_name")
    private String calledOrganizationName;

    /**
     * 被点名的人员姓名
     */
    @TableField("called_employee_name")
    private String calledEmployeeName;

    @TableField(value = "call_type")
    private int callType;

    /**
     * 被点名的状态
     */
    @TableField(value = "status")
    private int status;

    /**
     * 被点名状态发现变更的时间
     */
    @TableField("last_roll_call_status_changed_time")
    private Timestamp lastRollCallStatusTime;


    @TableField("last_roll_call_status_changed_note")
    private String lastRollCallStatusNote;

    /**
     * 发起.点名轮换的组织Id
     */
    @TableField("action_organization_id")
    private String actionOrganizationId;

    /**
     * 发起.点名轮换的组织名称
     */
    @TableField("action_organization_name")
    private String actionOrganizationName;

    /**
     * 发起.点名轮换的人员Id
     */
    @TableField("action_employee_id")
    private String actionEmployeeId;

    /**
     * 发起.点名轮换的人员名字
     */
    @TableField("action_employee_name")
    private String actionEmployeeName;

    /**
     * 最后的发起的时间(点名的话; 该值等于CreatedTime; 否则等于当前时间)
     */
    @TableField("last_action_time")
    private Timestamp lastActionTime;

    /**
     * 最后督导点名的房间号
     */
    @TableField("last_room_id")
    private String lastRoomId;

    /**
     * 最后督导的时间
     */
    @TableField("last_room_time")
    private Timestamp lastRoomTime;

    /**
     * 账单支付的公司
     */
    @TableField("payer_organization_id")
    private String payerOrgId;

    public RollCallEmployeeEntity() {
        this.setCreatedTime(java.sql.Timestamp.from(Instant.now()));
        this.setLastActionTime(this.getCreatedTime());
        this.setStatus(RollCallStatus.calculate(Collections.singleton(RollCallStatus.RollCallInit)));
    }

    public Collection<RollCallEmployeeHistoryEntity> reply(
            RollCallType rollCallType,
            String roomId,
            RollCallStatus expectedStatus) {
        if (!StringUtils.hasLength(roomId) && rollCallType == RollCallType.Dispatch) {
            throw new BadTenantException("回复督导点名的时候, 房间(RoomId)信息不能为空");
        }

        Collection<RollCallEmployeeHistoryEntity> employeeHistories = new ArrayList<>();
        Collection<RollCallType> currentRollCallTypes = RollCallType.getSelectedRollCallTypes(this.getCallType());
        if (!currentRollCallTypes.contains(rollCallType)) {

            if (rollCallType == RollCallType.Dispatch) {
                currentRollCallTypes.add(RollCallType.Dispatch);
                this.setCallType(RollCallType.calculate(currentRollCallTypes));
                RollCallEmployeeHistoryEntity unexpectedHistory = RollCallEmployeeHistoryEntity.create(
                        rollCallType,
                        roomId,
                        this,
                        RollCallStatus.DispatchInit,
                        "非预期的督导点名的初始化-在确认的时候执行"
                );

                employeeHistories.add(unexpectedHistory);
            }
            /*
            为了性能的原因; 我们允许在用户接受督导点名确认的时候; RollCallEmployee表的类型还没有发生变化
            if (rollCallType == RollCallType.Dispatch) {
                throw new BadTenantException("参数类型无效, 该点名未发起督导点名信息;");
            }
             */

            if (rollCallType == RollCallType.RollCall) {
                throw new BadTenantException("系统异常; 未找到点名信息记录;");
            }
        }

        Collection<RollCallStatus> selectedStatuses = RollCallStatus.getSelectedRollCallStatuses(this.getStatus());

        if (selectedStatuses.contains(expectedStatus)) {
            throw new IgnoreTenantException("该操作已确认; 不允许重复提交");
        }

        if (expectedStatus == RollCallStatus.RollCallSuccess || expectedStatus == RollCallStatus.DispatchSuccess) {
            if (rollCallType == RollCallType.RollCall) {
                if (selectedStatuses.contains(RollCallStatus.RollCallFailed)) {
                    throw new BadTenantException("该轮点名已经结束, 无法进行确认");
                }
            } else if (rollCallType == RollCallType.Dispatch) {
                if (selectedStatuses.contains(RollCallStatus.DispatchFailed)) {
                    throw new BadTenantException("该督导点名已经结束, 无法进行确认");
                }
            }
        }

        Collection<RollCallStatus> remainStatuses = RollCallStatus.calculateRollCallStatus(selectedStatuses, expectedStatus);
        remainStatuses.add(expectedStatus);

        this.setStatus(RollCallStatus.calculate(remainStatuses));

        Timestamp now = Timestamp.from(Instant.now());
        this.setLastRoomId(roomId);
        this.setLastRoomTime(now);
        this.setLastRollCallStatusTime(now);
        String label = "已确认";
        if (expectedStatus == RollCallStatus.RollCallFailed) {
            label = "督导点名失败";
        }

        this.setLastRollCallStatusNote(label);
        RollCallEmployeeHistoryEntity history = RollCallEmployeeHistoryEntity.create(
                rollCallType,
                roomId,
                this,
                expectedStatus,
                label
        );
        employeeHistories.add(history);

        return employeeHistories;
    }

    public RollCallEmployeeHistoryEntity assignFailed2Call(
            RollCallType rollCallType,
            RollCallStatus status,String message) {
        Collection<RollCallStatus> selectedStatuses = RollCallStatus.getSelectedRollCallStatuses(this.getStatus());

        if (status == RollCallStatus.RollCallFailed && selectedStatuses.contains(RollCallStatus.RollCallSuccess)) {
            return null;
        }

        if (status == RollCallStatus.DispatchFailed && selectedStatuses.contains(RollCallStatus.DispatchSuccess)) {
            return null;
        }

        Collection<RollCallStatus> remainStatuses = RollCallStatus.calculateRollCallStatus(selectedStatuses, status);
        this.setStatus(RollCallStatus.calculate(remainStatuses));
        this.setLastRollCallStatusTime(Timestamp.from(Instant.now()));
        this.setLastRollCallStatusNote(message);

        return RollCallEmployeeHistoryEntity.create(
                rollCallType,
                this, status, message);
    }

    public Collection<RollCallEmployeeHistoryEntity> autoFailed(String message) {
        Collection<RollCallType> selectedRolLCallType = RollCallType.getSelectedRollCallTypes(this.getCallType());

        Collection<RollCallEmployeeHistoryEntity> histories = new ArrayList<>();
        if (selectedRolLCallType.contains(RollCallType.Dispatch)) {
            Calendar currentCalendar = Calendar.getInstance();
            currentCalendar.add(Calendar.MINUTE, -3);
            Date shouldBeExpiredTime = currentCalendar.getTime();

            /**
             * 点名超过3分钟; 但是督导才刚刚开始的时候; 添加此判断是为了避免这种极端问题
             */
            if (this.getLastActionTime()!=null && this.getLastActionTime().before(shouldBeExpiredTime)) {
                RollCallEmployeeHistoryEntity history =
                        this.assignFailed2Call(RollCallType.Dispatch, RollCallStatus.DispatchFailed, message);

                if (history != null) {
                    histories.add(history);
                }
            }
        }

        if (selectedRolLCallType.contains(RollCallType.RollCall)) {
            RollCallEmployeeHistoryEntity history =
                    this.assignFailed2Call(RollCallType.RollCall, RollCallStatus.RollCallFailed, message);

            if (history != null) {
                histories.add(history);
            }
        }

        return histories;
    }


    public void assignCalledTenantCid(String cid) {
        this.setCalledTenantUserCid(cid);
        this.setLastUpdatedTenantUserCIdTime(Timestamp.from(Instant.now()));
        this.setLastRollCallStatusNote("推送给手机CID");
    }


    /**
     * 当督导点名的kafka的队列（TOPIC_DISPATCH_ROLL_CALL_EMPLOYEE_ROOM） 消费较慢(慢于TOPIC_DISPATCH_ROOM_EMPLOYEE_ACTION)的时候；
     * 默认以回复的为主；因此，这边直接返回null
     * @param roomId
     * @return
     */
    public RollCallEmployeeHistoryEntity doDispatch(
            String roomId) {
        if (StringUtils.hasLength(this.getLastRoomId()) && StringUtil.isEqual(this.getLastRoomId(), roomId)) {
            return null;
        }

        this.setLastRoomId(roomId);
        Timestamp now = Timestamp.from(Instant.now());
        this.setLastRoomTime(now);
        this.setLastRollCallStatusTime(now);
        this.setLastActionTime(now);

        Collection<RollCallType> selectedCallTypes = RollCallType.getSelectedRollCallTypes(this.getCallType());
        selectedCallTypes.add(RollCallType.Dispatch);
        this.setCallType(RollCallType.calculate(selectedCallTypes));

        RollCallStatus expectedStatus = RollCallStatus.DispatchInit;
        Collection<RollCallStatus> selectedStatuses = RollCallStatus.getSelectedRollCallStatuses(this.getStatus());
        Collection<RollCallStatus> remainStatuses = RollCallStatus.calculateRollCallStatus(selectedStatuses, expectedStatus);
        this.setStatus(RollCallStatus.calculate(remainStatuses));
        this.setLastRollCallStatusNote("发起督导点名请求");

        return RollCallEmployeeHistoryEntity.create(
                RollCallType.Dispatch,
                roomId,
                this,
                expectedStatus,
                "发起督导点名请求"
        );
    }

    public static RollCallEmployeeEntity create(
            String id,
            JsonProvider jsonProvider,
            RollCallEntity rollCall,
            String calledEmployeeId,
            String calledTenantUserId,
            String calledTencentUserId,
            String calledEmployeeName,
            SecurityDepartPoJo securityDepartPoJo,
            SecurityStationPoJo securityStationPoJo,
            OrganizationPoJo organizationPoJo,
            String payerOrgId) {
        RollCallEmployeeEntity entity = new RollCallEmployeeEntity();
        entity.setActionEmployeeId(rollCall.getActionEmployeeId());
        entity.setActionEmployeeName(rollCall.getActionEmployeeName());
        entity.setActionOrganizationId(rollCall.getActionOrganizationId());
        entity.setActionOrganizationName(rollCall.getActionOrganizationName());
        entity.setRollCallId(rollCall.getId());
        RollCallRulePoJo rulePoJo = jsonProvider.toObject(RollCallRulePoJo.class,rollCall.getRollCallRuleJson());
        if(rulePoJo!=null) {
            entity.setRollCallPlanId(rulePoJo.getPlanId());
        }

        entity.setId(id);
        entity.setCallType(RollCallType.calculate(Collections.singleton(RollCallType.RollCall)));

        entity.setCalledEmployeeId(calledEmployeeId);
        entity.setCalledTenantUserId(calledTenantUserId);
        entity.setCalledTencentUserId(calledTencentUserId);
        entity.setCalledEmployeeName(calledEmployeeName);

        entity.setSecurityDepartJson(jsonProvider.getJson(securityDepartPoJo));
        entity.setSecurityStationJson(jsonProvider.getJson(securityStationPoJo));
        entity.setCalledOrganizationId(organizationPoJo.getId());
        entity.setCalledOrganizationName(organizationPoJo.getName());
        entity.setCreatedTime(rollCall.getCreatedTime());
        entity.setPayerOrgId(payerOrgId);

        return entity;
    }
}