package api

import (
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	"sensor-server/internal/cache"
	"sensor-server/internal/database"
	"sensor-server/internal/models"
	"sensor-server/internal/websocket"
)

// Handler API 핸들러 구조체
type Handler struct {
	db  *gorm.DB
	hub *websocket.Hub
}

// NewHandler 새로운 핸들러 생성
func NewHandler(hub *websocket.Hub) *Handler {
	return &Handler{
		db:  database.GetDB(),
		hub: hub,
	}
}

// HealthCheck 헬스체크 엔드포인트
func (h *Handler) HealthCheck(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status":    "ok",
		"timestamp": time.Now(),
		"service":   "sensor-server",
	})
}

// ReceiveSensorData 센서 데이터 수신
func (h *Handler) ReceiveSensorData(c *gin.Context) {
	var request models.SensorDataRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"message": "Invalid request data: " + err.Error(),
		})
		return
	}

	// recorded_time 문자열을 time.Time으로 파싱
	recordedTime, err := time.Parse(time.RFC3339, request.RecordedTime)
	if err != nil {
		// ISO_LOCAL_DATE_TIME 형식도 시도
		recordedTime, err = time.Parse("2006-01-02T15:04:05", request.RecordedTime)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"success": false,
				"message": "Invalid recorded_time format: " + err.Error(),
			})
			return
		}
	}

	// 센서 데이터 생성
	reading := &models.SensorReading{
		DeviceID:     request.DeviceID,
		NodeID:       request.NodeID,
		Temperature:  request.Temperature,
		Humidity:     request.Humidity,
		Longitude:    request.Longitude,
		Latitude:     request.Latitude,
		// 추가 센서 데이터 필드
		FloatValue:        request.FloatValue,
		SignedInt32Value:  request.SignedInt32Value,
		UnsignedInt32Value: request.UnsignedInt32Value,
		// 원시 데이터 (디버깅용)
		RawTem:            request.RawTem,
		RawHum:            request.RawHum,
		RecordedTime:      recordedTime,
		ReceivedTime:      time.Now(),
	}

	// 데이터베이스에 저장
	if err := h.db.Create(reading).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"message": "Failed to save sensor data: " + err.Error(),
		})
		return
	}

	// Redis 캐시 업데이트
	if err := cache.CacheLatestReading(request.DeviceID, reading); err != nil {
		// 캐시 실패는 로그만 남기고 계속 진행
		c.Error(err)
	}

	// WebSocket 브로드캐스트
	h.hub.BroadcastSensorData(reading)

	// 디바이스 정보 업데이트
	h.updateDeviceInfo(request.DeviceID)

	c.JSON(http.StatusOK, models.SensorDataResponse{
		Success: true,
		Message: "Sensor data received successfully",
		Data:    reading,
	})
}

// ReceiveExtendedSensorData 확장된 센서 데이터 수신
func (h *Handler) ReceiveExtendedSensorData(c *gin.Context) {
	var request models.ExtendedSensorDataRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"message": "Invalid request data: " + err.Error(),
		})
		return
	}

	// recorded_time 문자열을 time.Time으로 파싱
	recordedTime, err := time.Parse(time.RFC3339, request.RecordedTime)
	if err != nil {
		// ISO_LOCAL_DATE_TIME 형식도 시도
		recordedTime, err = time.Parse("2006-01-02T15:04:05", request.RecordedTime)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"success": false,
				"message": "Invalid recorded_time format: " + err.Error(),
			})
			return
		}
	}

	// 확장된 센서 데이터 생성
	reading := &models.SensorReading{
		DeviceID:     request.DeviceID,
		NodeID:       request.NodeID,
		Temperature:  request.Temperature,
		Humidity:     request.Humidity,
		Longitude:    request.Longitude,
		Latitude:     request.Latitude,
		// 추가 센서 데이터 필드
		FloatValue:        request.FloatValue,
		SignedInt32Value:  request.SignedInt32Value,
		UnsignedInt32Value: request.UnsignedInt32Value,
		// 원시 데이터 (디버깅용)
		RawTem:            request.RawTem,
		RawHum:            request.RawHum,
		// 환경 센서 데이터
		PM10:             request.PM10,
		PM25:             request.PM25,
		Pressure:         request.Pressure,
		Illumination:     request.Illumination,
		TVOC:             request.TVOC,
		CO2:              request.CO2,
		O2:               request.O2,
		CO:               request.CO,
		RecordedTime:      recordedTime,
		ReceivedTime:      time.Now(),
	}

	// 데이터베이스에 저장
	if err := h.db.Create(reading).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"message": "Failed to save extended sensor data: " + err.Error(),
		})
		return
	}

	// Redis 캐시 업데이트
	if err := cache.CacheLatestReading(request.DeviceID, reading); err != nil {
		// 캐시 실패는 로그만 남기고 계속 진행
		c.Error(err)
	}

	// WebSocket 브로드캐스트
	h.hub.BroadcastSensorData(reading)

	// 디바이스 정보 업데이트
	h.updateDeviceInfo(request.DeviceID)

	c.JSON(http.StatusOK, models.SensorDataResponse{
		Success: true,
		Message: "Extended sensor data received successfully",
		Data:    reading,
	})
}

// GetLatestData 최신 센서 데이터 조회
func (h *Handler) GetLatestData(c *gin.Context) {
	deviceID := c.Param("deviceId")
	if deviceID == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"message": "Device ID is required",
		})
		return
	}

	// 먼저 캐시에서 조회
	reading, err := cache.GetLatestReading(deviceID)
	if err == nil {
		c.JSON(http.StatusOK, models.SensorDataResponse{
			Success: true,
			Data:    reading,
		})
		return
	}

	// 캐시에 없으면 데이터베이스에서 조회
	var latestReading models.SensorReading
	if err := h.db.Where("device_id = ?", deviceID).
		Order("recorded_time DESC").
		First(&latestReading).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			c.JSON(http.StatusNotFound, gin.H{
				"success": false,
				"message": "No data found for device: " + deviceID,
			})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"message": "Database error: " + err.Error(),
		})
		return
	}

	// 캐시에 저장
	cache.CacheLatestReading(deviceID, &latestReading)

	c.JSON(http.StatusOK, models.SensorDataResponse{
		Success: true,
		Data:    &latestReading,
	})
}

// GetHistory 히스토리 데이터 조회
func (h *Handler) GetHistory(c *gin.Context) {
	deviceID := c.Param("deviceId")
	if deviceID == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"message": "Device ID is required",
		})
		return
	}

	// 쿼리 파라미터 파싱
	limitStr := c.DefaultQuery("limit", "100")
	offsetStr := c.DefaultQuery("offset", "0")
	startTime := c.Query("start_time")
	endTime := c.Query("end_time")

	limit, err := strconv.Atoi(limitStr)
	if err != nil || limit <= 0 || limit > 1000 {
		limit = 100
	}

	offset, err := strconv.Atoi(offsetStr)
	if err != nil || offset < 0 {
		offset = 0
	}

	// 쿼리 빌드
	query := h.db.Where("device_id = ?", deviceID)

	if startTime != "" {
		if start, err := time.Parse(time.RFC3339, startTime); err == nil {
			query = query.Where("recorded_time >= ?", start)
		}
	}

	if endTime != "" {
		if end, err := time.Parse(time.RFC3339, endTime); err == nil {
			query = query.Where("recorded_time <= ?", end)
		}
	}

	// 총 개수 조회
	var total int64
	query.Model(&models.SensorReading{}).Count(&total)

	// 데이터 조회
	var readings []models.SensorReading
	if err := query.Order("recorded_time DESC").
		Limit(limit).
		Offset(offset).
		Find(&readings).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"message": "Database error: " + err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, models.HistoryResponse{
		Success: true,
		Data:    readings,
		Total:   total,
	})
}

// GetDevices 디바이스 목록 조회
func (h *Handler) GetDevices(c *gin.Context) {
	var devices []models.Device
	if err := h.db.Find(&devices).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"message": "Database error: " + err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, models.DeviceListResponse{
		Success: true,
		Devices: devices,
	})
}

// updateDeviceInfo 디바이스 정보 업데이트
func (h *Handler) updateDeviceInfo(deviceID string) {
	var device models.Device
	if err := h.db.Where("device_id = ?", deviceID).First(&device).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			// 새 디바이스 생성
			device = models.Device{
				DeviceID: deviceID,
				Name:     "Device " + deviceID,
				Status:   "active",
				LastSeen: time.Now(),
			}
			h.db.Create(&device)
		}
	} else {
		// 기존 디바이스 업데이트
		device.LastSeen = time.Now()
		device.Status = "active"
		h.db.Save(&device)
	}

	// 캐시 업데이트
	cache.CacheDeviceInfo(&device)
} 