package com.qaup.collision.rule.model.entity;

import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import com.qaup.collision.rule.model.enums.RuleAlertLevel;
import com.qaup.collision.rule.model.enums.RuleCategory;
import com.qaup.collision.rule.model.enums.RuleStatus;
import com.qaup.collision.rule.model.enums.SpatialObjectType;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.persistence.*;
import org.locationtech.jts.geom.Geometry;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;

/**
 * 空间规则实体类
 * 对应spatial_rules表，统一管理所有空间安全规则
 * 
 * @author AI Assistant
 * @version 1.0
 * @since 2025-01-17
 */
@Entity
@Table(name = "spatial_rules", indexes = {
    @Index(name = "idx_spatial_rules_status", columnList = "status"),
    @Index(name = "idx_spatial_rules_category", columnList = "rule_category"),
    @Index(name = "idx_spatial_rules_object_type", columnList = "spatial_object_type"),
    @Index(name = "idx_spatial_rules_priority", columnList = "priority")
})
@EntityListeners(AuditingEntityListener.class)
public class SpatialRule implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 规则唯一标识
     */
    @Id
    @Column(name = "rule_id", length = 50)
    private String ruleId;

    /**
     * 规则名称
     */
    @Column(name = "rule_name", nullable = false, length = 200)
    private String name;

    /**
     * 规则描述
     */
    @Column(name = "description", columnDefinition = "TEXT")
    private String description;

    /**
     * 规则分类
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "rule_category", nullable = false, length = 50)
    private RuleCategory category;

    /**
     * 规则状态
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "status", nullable = false, length = 20)
    private RuleStatus status;

    /**
     * 规则优先级（数值越小优先级越高）
     */
    @Column(name = "priority", nullable = false)
    private Integer priority;

    /**
     * 空间对象类型（区域、道路、路口或自定义几何形状）
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "spatial_object_type", nullable = false, length = 30)
    private SpatialObjectType spatialObjectType;

    /**
     * 关联的空间对象ID（通用字段，根据spatial_object_type确定具体含义）
     */
    @Column(name = "spatial_object_id", length = 50)
    private String spatialObjectId;

    /**
     * 允许的车辆类型列表
     */
    @Enumerated(EnumType.STRING)
    @ElementCollection(targetClass = MovingObjectType.class)
    @CollectionTable(
        name = "spatial_rule_vehicle_types",
        joinColumns = @JoinColumn(name = "rule_id")
    )
    @Column(name = "vehicle_type", length = 30)
    private List<MovingObjectType> allowedVehicleTypes;

    /**
     * 规则参数（JSONB格式存储灵活参数）
     */
    @Column(name = "rule_parameters", columnDefinition = "jsonb")
    private String parameters;

    /**
     * 告警级别
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "alert_level", nullable = false, length = 20)
    private RuleAlertLevel alertLevel;

    /**
     * 告警消息模板
     */
    @Column(name = "alert_message", length = 500)
    private String alertMessage;

    /**
     * 生效开始时间
     */
    @Column(name = "effective_start_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime effectiveStartTime;

    /**
     * 生效结束时间
     */
    @Column(name = "effective_end_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime effectiveEndTime;

    /**
     * 每日生效开始时间
     */
    @Column(name = "daily_start_time")
    @JsonFormat(pattern = "HH:mm:ss")
    private LocalTime dailyStartTime;

    /**
     * 每日生效结束时间
     */
    @Column(name = "daily_end_time")
    @JsonFormat(pattern = "HH:mm:ss")
    private LocalTime dailyEndTime;

    /**
     * 星期模式（位掩码：周一=1, 周二=2, 周三=4, ..., 周日=64）
     */
    @Column(name = "weekday_pattern")
    private Integer weekdayPattern;

    /**
     * 复杂时间模式（支持多种时间表达式）
     * 当此字段不为空时，优先使用此模式，忽略dailyStartTime、dailyEndTime、weekdayPattern
     * 格式：[{"days": ["MONDAY", "TUESDAY"], "time_ranges": ["08:00-18:00"]}]
     */
    @Column(name = "time_patterns", columnDefinition = "jsonb")
    private String timePatterns;

    /**
     * 创建时间
     */
    @CreatedDate
    @Column(name = "created_at", nullable = false, updatable = false)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdAt;

    /**
     * 更新时间
     */
    @LastModifiedDate
    @Column(name = "updated_at", nullable = false)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updatedAt;

    /**
     * 创建者
     */
    @Column(name = "created_by", length = 100)
    private String createdBy;

    /**
     * 更新者
     */
    @Column(name = "updated_by", length = 100)
    private String updatedBy;

    /**
     * 版本号（用于乐观锁）
     */
    @Version
    @Column(name = "version")
    private Long version;

    /**
     * 自定义几何形状（当spatial_object_type为CUSTOM_GEOMETRY时使用）
     * 注意：此字段不参与Redis序列化，以避免序列化问题
     */
    @Column(name = "custom_geometry", columnDefinition = "geometry")
    private transient Geometry customGeometry;

    // 默认构造函数
    public SpatialRule() {
    }

    // 全参构造函数
    public SpatialRule(String ruleId, String name, String description, 
                      RuleCategory category, RuleStatus status, Integer priority,
                      SpatialObjectType spatialObjectType, RuleAlertLevel alertLevel) {
        this.ruleId = ruleId;
        this.name = name;
        this.description = description;
        this.category = category;
        this.status = status;
        this.priority = priority;
        this.spatialObjectType = spatialObjectType;
        this.alertLevel = alertLevel;
    }

    // 业务方法

    /**
     * 检查规则是否当前有效
     * @return 如果规则当前有效返回true
     */
    public boolean isCurrentlyEffective() {
        LocalDateTime now = LocalDateTime.now();
        
        // 检查状态
        if (!status.isExecutable()) {
            return false;
        }
        
        // 检查整体时间范围
        if (effectiveStartTime != null && now.isBefore(effectiveStartTime)) {
            return false;
        }
        
        if (effectiveEndTime != null && now.isAfter(effectiveEndTime)) {
            return false;
        }
        
        // 检查每日时间范围
        if (dailyStartTime != null && dailyEndTime != null) {
            LocalTime currentTime = now.toLocalTime();
            if (currentTime.isBefore(dailyStartTime) || currentTime.isAfter(dailyEndTime)) {
                return false;
            }
        }
        
        // 检查星期模式
        if (weekdayPattern != null) {
            int todayBit = 1 << (now.getDayOfWeek().getValue() - 1);
            if ((weekdayPattern & todayBit) == 0) {
                return false;
            }
        }
        
        return true;
    }

    /**
     * 检查车辆类型是否被允许
     * @param vehicleType 车辆类型
     * @return 如果车辆类型被允许返回true
     */
    public boolean isVehicleTypeAllowed(MovingObjectType vehicleType) {
        if (allowedVehicleTypes == null || allowedVehicleTypes.isEmpty()) {
            // 如果没有指定车辆类型限制，则允许所有类型
            return true;
        }
        return allowedVehicleTypes.contains(vehicleType);
    }

    /**
     * 获取规则的关联对象ID
     * @return 根据spatial_object_type返回相应的ID
     */
    public String getRelatedObjectId() {
        switch (spatialObjectType) {
            case AREA:
                return spatialObjectId;
            case ROAD:
                return spatialObjectId;
            case INTERSECTION:
                return spatialObjectId;
            case CUSTOM_GEOMETRY:
            default:
                return null;
        }
    }

    /**
     * 设置规则的关联对象ID
     * @param objectId 对象ID
     */
    public void setRelatedObjectId(String objectId) {
        switch (spatialObjectType) {
            case AREA:
            case ROAD:
            case INTERSECTION:
                this.spatialObjectId = objectId;
                break;
            case CUSTOM_GEOMETRY:
                // 自定义几何不需要设置ID
                break;
        }
    }

    /**
     * 更新规则状态
     * @param newStatus 新状态
     * @throws IllegalStateException 如果状态变更不合法
     */
    public void updateStatus(RuleStatus newStatus) {
        if (!this.status.canTransitionTo(newStatus)) {
            throw new IllegalStateException(
                String.format("Cannot transition from %s to %s", this.status, newStatus)
            );
        }
        this.status = newStatus;
    }

    // Getters and Setters
    public String getRuleId() { return ruleId; }
    public void setRuleId(String ruleId) { this.ruleId = ruleId; }

    public String getName() { return name; }
    public void setName(String ruleName) { this.name = ruleName; }

    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }

    public RuleCategory getCategory() { return category; }
    public void setCategory(RuleCategory ruleCategory) { this.category = ruleCategory; }

    public RuleStatus getStatus() { return status; }
    public void setStatus(RuleStatus status) { this.status = status; }

    public Integer getPriority() { return priority; }
    public void setPriority(Integer priority) { this.priority = priority; }

    public SpatialObjectType getSpatialObjectType() { return spatialObjectType; }
    public void setSpatialObjectType(SpatialObjectType spatialObjectType) { this.spatialObjectType = spatialObjectType; }

    public String getSpatialObjectId() { return spatialObjectId; }
    public void setSpatialObjectId(String spatialObjectId) { this.spatialObjectId = spatialObjectId; }

    public List<MovingObjectType> getAllowedVehicleTypes() { return allowedVehicleTypes; }
    public void setAllowedVehicleTypes(List<MovingObjectType> allowedVehicleTypes) { this.allowedVehicleTypes = allowedVehicleTypes; }

    public String getParameters() { return parameters; }
    public void setParameters(String parameters) { this.parameters = parameters; }

    public RuleAlertLevel getAlertLevel() { return alertLevel; }
    public void setAlertLevel(RuleAlertLevel alertLevel) { this.alertLevel = alertLevel; }

    public String getAlertMessage() { return alertMessage; }
    public void setAlertMessage(String alertMessage) { this.alertMessage = alertMessage; }

    public LocalDateTime getEffectiveStartTime() { return effectiveStartTime; }
    public void setEffectiveStartTime(LocalDateTime effectiveStartTime) { this.effectiveStartTime = effectiveStartTime; }

    public LocalDateTime getEffectiveEndTime() { return effectiveEndTime; }
    public void setEffectiveEndTime(LocalDateTime effectiveEndTime) { this.effectiveEndTime = effectiveEndTime; }

    public LocalTime getDailyStartTime() { return dailyStartTime; }
    public void setDailyStartTime(LocalTime dailyStartTime) { this.dailyStartTime = dailyStartTime; }

    public LocalTime getDailyEndTime() { return dailyEndTime; }
    public void setDailyEndTime(LocalTime dailyEndTime) { this.dailyEndTime = dailyEndTime; }

    public Integer getWeekdayPattern() { return weekdayPattern; }
    public void setWeekdayPattern(Integer weekdayPattern) { this.weekdayPattern = weekdayPattern; }

    public String getTimePatterns() { return timePatterns; }
    public void setTimePatterns(String timePatterns) { this.timePatterns = timePatterns; }

    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }

    public LocalDateTime getUpdatedAt() { return updatedAt; }
    public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }

    public String getCreatedBy() { return createdBy; }
    public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }

    public String getUpdatedBy() { return updatedBy; }
    public void setUpdatedBy(String updatedBy) { this.updatedBy = updatedBy; }

    public Long getVersion() { return version; }
    public void setVersion(Long version) { this.version = version; }

    public Geometry getCustomGeometry() { return customGeometry; }
    public void setCustomGeometry(Geometry customGeometry) { this.customGeometry = customGeometry; }

    // equals, hashCode and toString methods
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SpatialRule that = (SpatialRule) o;
        return Objects.equals(ruleId, that.ruleId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(ruleId);
    }

    @Override
    public String toString() {
        return String.format("SpatialRule{ruleId='%s', ruleName='%s', category=%s, status=%s, priority=%d}", 
                           ruleId, name, category, status, priority);
    }


} 