package listener

import (
	"context"
	"encoding/json"
	"fmt"
	"math"
	"time"

	"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport/proto"

	"gorm.io/datatypes"
)

// loraListener 是一个统一的LoRa上行业务处理器，实现了 transport.UpstreamHandler 接口。
// 它包含了处理业务所需的所有依赖，是项目中唯一处理LoRa上行业务的地方。
type loraListener struct {
	selfCtx               context.Context
	areaControllerRepo    repository.AreaControllerRepository
	pendingCollectionRepo repository.PendingCollectionRepository
	deviceRepo            repository.DeviceRepository
	sensorDataRepo        repository.SensorDataRepository
	deviceCommandLogRepo  repository.DeviceCommandLogRepository
}

// NewLoRaListener 创建一个新的 loraListener 实例。
// 注意：返回的是 transport.UpstreamHandler 接口，向上层隐藏具体实现。
func NewLoRaListener(
	ctx context.Context,
	areaControllerRepo repository.AreaControllerRepository,
	pendingCollectionRepo repository.PendingCollectionRepository,
	deviceRepo repository.DeviceRepository,
	sensorDataRepo repository.SensorDataRepository,
	deviceCommandLogRepo repository.DeviceCommandLogRepository,
) transport.UpstreamHandler {
	return &loraListener{
		selfCtx:               logs.AddCompName(ctx, "LoRaListener"),
		areaControllerRepo:    areaControllerRepo,
		pendingCollectionRepo: pendingCollectionRepo,
		deviceRepo:            deviceRepo,
		sensorDataRepo:        sensorDataRepo,
		deviceCommandLogRepo:  deviceCommandLogRepo,
	}
}

// HandleInstruction 处理来自设备的、已解析为Instruction的业务指令。
func (l *loraListener) HandleInstruction(upstreamCtx context.Context, sourceAddr string, instruction *proto.Instruction) error {
	ctx, logger := logs.Trace(upstreamCtx, l.selfCtx, "HandleInstruction")
	logger.Infow("接收到设备指令", "来源地址", sourceAddr)

	switch p := instruction.Payload.(type) {
	case *proto.Instruction_CollectResult:
		return l.handleCollectResult(ctx, sourceAddr, p.CollectResult)

	case *proto.Instruction_OtaUpgradeStatus:
		return l.handleOtaStatus(ctx, sourceAddr, p.OtaUpgradeStatus)

	case *proto.Instruction_LogUploadRequest:
		logger.Infow("收到设备日志上传请求，暂未实现处理逻辑", "来源地址", sourceAddr, "日志条数", len(p.LogUploadRequest.Entries))
		// TODO: 在这里实现设备日志的处理逻辑
		return nil

	default:
		logger.Warnw("收到一个当前未处理的上行指令类型", "来源地址", sourceAddr, "类型", fmt.Sprintf("%T", p))
		return nil
	}
}

// HandleStatus 处理非业务指令的设备状态更新，例如信号强度、电量等。
func (l *loraListener) HandleStatus(upstreamCtx context.Context, sourceAddr string, status map[string]interface{}) error {
	ctx, logger := logs.Trace(upstreamCtx, l.selfCtx, "HandleStatus")
	logger.Infow("接收到设备状态更新", "来源地址", sourceAddr, "状态", status)

	areaController, err := l.areaControllerRepo.FindByNetworkID(ctx, sourceAddr)
	if err != nil {
		return fmt.Errorf("处理 'status' 事件失败：无法通过源地址 '%s' 找到区域主控设备: %w", sourceAddr, err)
	}

	eventTime := time.Now() // 状态事件通常是实时的，使用当前时间

	// 尝试记录信号强度
	if rssi, ok := status["rssi"].(float64); ok {
		if snr, ok := status["snr"].(float64); ok {
			signalMetrics := models.SignalMetrics{
				RssiDbm: int(rssi),
				SnrDb:   float32(snr),
			}
			if margin, ok := status["margin"].(float64); ok {
				signalMetrics.MarginDb = int(margin)
			}
			l.recordSensorData(ctx, areaController.ID, areaController.ID, eventTime, models.SensorTypeSignalMetrics, signalMetrics)
			logger.Infow("已记录区域主控的信号强度", "主控ID", areaController.ID, "指标", signalMetrics)
		}
	}

	// 尝试记录电池电量
	if batteryLevel, ok := status["batteryLevel"].(float64); ok {
		batteryData := models.BatteryLevel{
			BatteryLevelRatio: float32(batteryLevel),
		}
		if unavailable, ok := status["batteryLevelUnavailable"].(bool); ok {
			batteryData.BatteryLevelUnavailable = unavailable
		}
		if externalPower, ok := status["externalPower"].(bool); ok {
			batteryData.ExternalPower = externalPower
		}
		l.recordSensorData(ctx, areaController.ID, areaController.ID, eventTime, models.SensorTypeBatteryLevel, batteryData)
		logger.Infow("已记录区域主控的电池状态", "主控ID", areaController.ID, "状态", batteryData)
	}

	return nil
}

// HandleAck 处理对下行指令的确认（ACK）事件。
func (l *loraListener) HandleAck(upstreamCtx context.Context, sourceAddr string, deduplicationID string, acknowledged bool, eventTime time.Time) error {
	ctx, logger := logs.Trace(upstreamCtx, l.selfCtx, "HandleAck")

	err := l.deviceCommandLogRepo.UpdateAcknowledgedAt(ctx, deduplicationID, eventTime, acknowledged)
	if err != nil {
		logger.Errorw("更新下行任务记录的确认状态失败",
			"MessageID", deduplicationID,
			"DevEui", sourceAddr,
			"Acknowledged", acknowledged,
			"error", err,
		)
		return fmt.Errorf("更新下行任务记录失败: %w", err)
	}

	logger.Infow("成功更新下行任务记录确认状态",
		"MessageID", deduplicationID,
		"DevEui", sourceAddr,
		"Acknowledged", acknowledged,
		"AcknowledgedAt", eventTime.Format(time.RFC3339),
	)
	return nil
}

// handleCollectResult 是处理采集结果的核心业务逻辑
func (l *loraListener) handleCollectResult(ctx context.Context, sourceAddr string, collectResp *proto.CollectResult) error {
	if collectResp == nil {
		return fmt.Errorf("传入的CollectResult为nil")
	}

	correlationID := collectResp.CorrelationId
	logger := logs.GetLogger(ctx).With("correlationID", correlationID, "来源地址", sourceAddr)
	logger.Infow("开始处理采集响应", "数据点数量", len(collectResp.Values))

	// 1. 查找区域主控
	areaController, err := l.areaControllerRepo.FindByNetworkID(ctx, sourceAddr)
	if err != nil {
		return fmt.Errorf("处理采集响应失败：无法通过源地址 '%s' 找到区域主控设备: %w", sourceAddr, err)
	}
	if err := areaController.SelfCheck(); err != nil {
		return fmt.Errorf("处理采集响应失败：区域主控 %v(ID: %d) 未通过自检: %w", areaController.Name, areaController.ID, err)
	}

	// 2. 根据 CorrelationID 查找待处理请求
	pendingReq, err := l.pendingCollectionRepo.FindByCorrelationID(ctx, correlationID)
	if err != nil {
		return fmt.Errorf("处理采集响应失败：无法找到待处理请求: %w", err)
	}

	// 3. 检查状态，防止重复处理
	if pendingReq.Status != models.PendingStatusPending && pendingReq.Status != models.PendingStatusTimedOut {
		logger.Warnw("收到一个已处理过的采集响应，将忽略。", "状态", string(pendingReq.Status))
		return nil // 返回 nil，因为这不是一个错误，只是一个重复的请求
	}

	// 4. 匹配数据并存入数据库
	deviceIDs := pendingReq.CommandMetadata
	values := collectResp.Values
	if len(deviceIDs) != len(values) {
		err := fmt.Errorf("数据不匹配：下行指令要求采集 %d 个设备，但上行响应包含 %d 个值", len(deviceIDs), len(values))
		// 即使数量不匹配，也尝试更新状态为完成，以防止请求永远 pending
		if updateErr := l.pendingCollectionRepo.UpdateStatusToFulfilled(ctx, correlationID, time.Now()); updateErr != nil {
			logger.Errorw("更新待采集请求状态为 'fulfilled' 失败", "error", updateErr)
		}
		return err
	}

	eventTime := time.Now() // 对整个采集批次使用统一的时间戳

	for i, deviceID := range deviceIDs {
		rawSensorValue := values[i]
		devLogger := logger.With("设备ID", deviceID)

		if math.IsNaN(float64(rawSensorValue)) {
			devLogger.Warnw("设备上报了一个无效的 NaN 值，已跳过当前值的记录。")
			continue
		}

		dev, err := l.deviceRepo.FindByID(ctx, deviceID)
		if err != nil {
			devLogger.Errorw("处理采集数据失败：无法找到设备", "error", err)
			continue
		}
		if err := dev.SelfCheck(); err != nil {
			devLogger.Warnw("跳过设备，因其未通过自检或设备模板无效", "error", err)
			continue
		}

		var valueDescriptors []*models.ValueDescriptor
		if err := dev.DeviceTemplate.ParseValues(&valueDescriptors); err != nil {
			devLogger.Warnw("跳过设备，因其设备模板的 Values 属性解析失败", "error", err)
			continue
		}
		if len(valueDescriptors) == 0 {
			devLogger.Warnw("跳过设备，因其设备模板缺少 ValueDescriptor 定义")
			continue
		}
		valueDescriptor := valueDescriptors[0]

		parsedValue := rawSensorValue*valueDescriptor.Multiplier + valueDescriptor.Offset

		var dataToRecord interface{}
		switch valueDescriptor.Type {
		case models.SensorTypeTemperature:
			dataToRecord = models.TemperatureData{TemperatureCelsius: parsedValue}
		case models.SensorTypeHumidity:
			dataToRecord = models.HumidityData{HumidityPercent: parsedValue}
		case models.SensorTypeWeight:
			dataToRecord = models.WeightData{WeightKilograms: parsedValue}
		default:
			devLogger.Warnw("未知的传感器类型，将使用通用格式记录", "传感器类型", string(valueDescriptor.Type))
			dataToRecord = map[string]float32{"value": parsedValue}
		}

		l.recordSensorData(ctx, areaController.ID, dev.ID, eventTime, valueDescriptor.Type, dataToRecord)
		devLogger.Infow("成功记录传感器数据",
			"类型", string(valueDescriptor.Type),
			"原始值", rawSensorValue,
			"解析值", parsedValue,
		)
	}

	// 5. 更新请求状态为“已完成”
	if err := l.pendingCollectionRepo.UpdateStatusToFulfilled(ctx, correlationID, eventTime); err != nil {
		logger.Errorw("更新待采集请求状态为 'fulfilled' 失败", "error", err)
		return fmt.Errorf("更新待采集请求状态失败: %w", err)
	}

	logger.Infow("成功完成并关闭采集请求")
	return nil
}

// recordSensorData 是一个通用方法，用于将传感器数据存入数据库。
func (l *loraListener) recordSensorData(ctx context.Context, areaControllerID uint32, sensorDeviceID uint32, eventTime time.Time, sensorType models.SensorType, data interface{}) {
	logger := logs.GetLogger(ctx).With("方法", "recordSensorData")

	jsonData, err := json.Marshal(data)
	if err != nil {
		logger.Errorw("记录传感器数据失败：序列化数据为 JSON 时出错", "error", err)
		return
	}

	sensorData := &models.SensorData{
		Time:             eventTime,
		DeviceID:         sensorDeviceID,
		AreaControllerID: areaControllerID,
		SensorType:       sensorType,
		Data:             datatypes.JSON(jsonData),
	}

	if err := l.sensorDataRepo.Create(ctx, sensorData); err != nil {
		logger.Errorw("记录传感器数据失败：存入数据库时出错",
			"设备ID", sensorDeviceID,
			"传感器类型", string(sensorType),
			"error", err,
		)
	}
}

// handleOtaStatus 处理设备上报的OTA升级状态。
func (l *loraListener) handleOtaStatus(ctx context.Context, sourceAddr string, status *proto.OtaUpgradeStatus) error {
	reqCtx, logger := logs.Trace(ctx, l.selfCtx, "handleOtaStatus")
	logger.Infow("开始处理OTA升级状态",
		"来源地址", sourceAddr,
		"状态码", status.StatusCode,
		"处理结果", status.StatusCode == 0,
		"当前版本", status.CurrentFirmwareVersion,
	)

	// 1. 查找区域主控
	areaController, err := l.areaControllerRepo.FindByNetworkID(reqCtx, sourceAddr)
	if err != nil {
		return fmt.Errorf("处理OTA状态失败：无法找到区域主控: %w", err)
	}

	// 2. 更新固件版本号
	// 我们信任设备上报的版本号，无论成功失败都进行更新
	if status.CurrentFirmwareVersion != "" {
		err = l.areaControllerRepo.UpdateFirmwareVersion(reqCtx, areaController.ID, status.CurrentFirmwareVersion)
		if err != nil {
			logger.Errorw("更新区域主控固件版本号失败", "主控ID", areaController.ID, "error", err)
			return fmt.Errorf("更新固件版本号失败: %w", err)
		}
		logger.Infow("成功更新区域主控固件版本号", "主控ID", areaController.ID, "新版本", status.CurrentFirmwareVersion)
	}

	// TODO: 后续可以在这里增加逻辑，比如记录一条操作日志，或者发送一个通知

	return nil
}
