package com.qaup.collision.rule.event;

import com.qaup.collision.rule.model.entity.SpatialRule;
import com.qaup.collision.rule.model.enums.RuleStatus;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

import java.time.LocalDateTime;
import java.util.Map;

/**
 * 规则状态变更事件
 * 当规则状态发生变化时发布此事件
 */
@Getter
public class RuleStateChangeEvent extends ApplicationEvent {

    /**
     * 相关规则
     */
    private final SpatialRule rule;

    /**
     * 变更前状态
     */
    private final RuleStatus previousStatus;

    /**
     * 变更后状态
     */
    private final RuleStatus newStatus;

    /**
     * 状态变更时间
     */
    private final LocalDateTime changeTimestamp;

    /**
     * 变更原因
     */
    private final String changeReason;

    /**
     * 变更操作人
     */
    private final String changedBy;

    /**
     * 事件源标识
     */
    private final String eventSource;

    /**
     * 变更上下文信息
     */
    private final Map<String, Object> changeContext;

    /**
     * 状态变更类型
     */
    private final StateChangeType changeType;

    /**
     * 是否影响运行时规则
     */
    private final boolean affectsRuntime;

    /**
     * 状态变更类型枚举
     */
    public enum StateChangeType {
        ACTIVATION("激活", "规则被激活"),
        DEACTIVATION("停用", "规则被停用"),
        SUSPENSION("暂停", "规则被暂停"),
        RESUMPTION("恢复", "规则从暂停状态恢复"),
        CONFIGURATION_UPDATE("配置更新", "规则配置被更新"),
        EXPIRATION("过期", "规则因时间限制而过期"),
        MANUAL_CHANGE("手动变更", "管理员手动变更状态"),
        SYSTEM_CHANGE("系统变更", "系统自动变更状态"),
        ERROR_TRIGGERED("错误触发", "因错误导致的状态变更");

        private final String displayName;
        private final String description;

        StateChangeType(String displayName, String description) {
            this.displayName = displayName;
            this.description = description;
        }

        public String getDisplayName() {
            return displayName;
        }

        public String getDescription() {
            return description;
        }
    }

    /**
     * 完整构造函数
     */
    public RuleStateChangeEvent(Object source, SpatialRule rule, RuleStatus previousStatus, RuleStatus newStatus,
                              String changeReason, String changedBy, String eventSource,
                              Map<String, Object> changeContext, StateChangeType changeType) {
        super(source);
        this.rule = rule;
        this.previousStatus = previousStatus;
        this.newStatus = newStatus;
        this.changeTimestamp = LocalDateTime.now();
        this.changeReason = changeReason;
        this.changedBy = changedBy;
        this.eventSource = eventSource;
        this.changeContext = changeContext;
        this.changeType = changeType;
        this.affectsRuntime = determineRuntimeImpact(previousStatus, newStatus);
    }

    /**
     * 简化构造函数
     */
    public RuleStateChangeEvent(Object source, SpatialRule rule, RuleStatus previousStatus, RuleStatus newStatus,
                              String changeReason, String changedBy) {
        this(source, rule, previousStatus, newStatus, changeReason, changedBy, "RuleManager", 
             null, determineChangeType(previousStatus, newStatus));
    }

    /**
     * 规则激活事件工厂方法
     */
    public static RuleStateChangeEvent ruleActivated(Object source, SpatialRule rule, String changedBy, String reason) {
        return new RuleStateChangeEvent(source, rule, rule.getStatus(), RuleStatus.ACTIVE,
                reason, changedBy, "RuleManager", null, StateChangeType.ACTIVATION);
    }

    /**
     * 规则停用事件工厂方法
     */
    public static RuleStateChangeEvent ruleDeactivated(Object source, SpatialRule rule, String changedBy, String reason) {
        return new RuleStateChangeEvent(source, rule, rule.getStatus(), RuleStatus.SUSPENDED,
                reason, changedBy, "RuleManager", null, StateChangeType.DEACTIVATION);
    }

    /**
     * 规则暂停事件工厂方法
     */
    public static RuleStateChangeEvent ruleSuspended(Object source, SpatialRule rule, String changedBy, String reason) {
        return new RuleStateChangeEvent(source, rule, rule.getStatus(), RuleStatus.SUSPENDED,
                reason, changedBy, "RuleManager", null, StateChangeType.SUSPENSION);
    }

    /**
     * 规则配置更新事件工厂方法
     */
    public static RuleStateChangeEvent ruleConfigUpdated(Object source, SpatialRule rule, String changedBy, 
                                                        Map<String, Object> updateContext) {
        return new RuleStateChangeEvent(source, rule, rule.getStatus(), rule.getStatus(),
                "规则配置已更新", changedBy, "RuleManager", updateContext, StateChangeType.CONFIGURATION_UPDATE);
    }

    /**
     * 确定状态变更类型
     */
    private static StateChangeType determineChangeType(RuleStatus previousStatus, RuleStatus newStatus) {
        if (previousStatus == newStatus) {
            return StateChangeType.CONFIGURATION_UPDATE;
        }

        if (newStatus == RuleStatus.ACTIVE) {
            return StateChangeType.ACTIVATION;
        } else if (newStatus == RuleStatus.SUSPENDED) {
            return StateChangeType.SUSPENSION;
        } else if (newStatus == RuleStatus.EXPIRED || newStatus == RuleStatus.DELETED) {
            return StateChangeType.DEACTIVATION;
        } else if (previousStatus == RuleStatus.SUSPENDED && newStatus == RuleStatus.ACTIVE) {
            return StateChangeType.RESUMPTION;
        } else {
            return StateChangeType.MANUAL_CHANGE;
        }
    }

    /**
     * 确定是否影响运行时
     */
    private boolean determineRuntimeImpact(RuleStatus previousStatus, RuleStatus newStatus) {
        // 如果状态从ACTIVE变为非ACTIVE，或从非ACTIVE变为ACTIVE，都影响运行时
        if (previousStatus == null || newStatus == null) {
            return false;
        }

        boolean wasActive = (previousStatus == RuleStatus.ACTIVE);
        boolean isActive = (newStatus == RuleStatus.ACTIVE);
        
        return wasActive != isActive;
    }

    /**
     * 判断是否为激活状态变更
     */
    public boolean isActivationChange() {
        return changeType == StateChangeType.ACTIVATION || changeType == StateChangeType.RESUMPTION;
    }

    /**
     * 判断是否为停用状态变更
     */
    public boolean isDeactivationChange() {
        return changeType == StateChangeType.DEACTIVATION || changeType == StateChangeType.SUSPENSION;
    }

    /**
     * 判断是否为配置变更
     */
    public boolean isConfigurationChange() {
        return changeType == StateChangeType.CONFIGURATION_UPDATE;
    }

    /**
     * 判断是否为系统自动变更
     */
    public boolean isSystemTriggered() {
        return changeType == StateChangeType.SYSTEM_CHANGE || 
               changeType == StateChangeType.EXPIRATION ||
               changeType == StateChangeType.ERROR_TRIGGERED;
    }

    /**
     * 获取变更严重程度
     */
    public int getChangeSeverity() {
        switch (changeType) {
            case ERROR_TRIGGERED:
                return 5; // 最高
            case EXPIRATION:
                return 4;
            case DEACTIVATION:
            case SUSPENSION:
                return 3;
            case ACTIVATION:
            case RESUMPTION:
                return 2;
            case CONFIGURATION_UPDATE:
            case MANUAL_CHANGE:
            case SYSTEM_CHANGE:
            default:
                return 1; // 最低
        }
    }

    /**
     * 获取事件描述
     */
    public String getEventDescription() {
        return String.format("规则状态变更 - 规则ID: %s, %s -> %s, 变更类型: %s, 操作人: %s",
                rule.getRuleId(),
                previousStatus != null ? previousStatus.name() : "NULL",
                newStatus != null ? newStatus.name() : "NULL",
                changeType.getDisplayName(),
                changedBy != null ? changedBy : "SYSTEM");
    }

    /**
     * 判断是否需要通知
     */
    public boolean requiresNotification() {
        return affectsRuntime || getChangeSeverity() >= 3;
    }

    @Override
    public String toString() {
        return String.format("RuleStateChangeEvent{" +
                "ruleId='%s', " +
                "previousStatus=%s, " +
                "newStatus=%s, " +
                "changeType=%s, " +
                "changedBy='%s', " +
                "affectsRuntime=%s, " +
                "timestamp=%s" +
                "}",
                rule.getRuleId(),
                previousStatus,
                newStatus,
                changeType.name(),
                changedBy,
                affectsRuntime,
                changeTimestamp);
    }
} 