package store

import (
	"context"
	"encoding/json"
	"net"
	"primesys_producer/src/logger"
	"primesys_producer/src/redis"
	"primesys_producer/src/types"
	"strconv"
	"sync"
	"time"
)

const deviceStatusKey = "device_status"
const deviceStatusChannel = "device_status_live"

var (
	mu                  sync.RWMutex
	deviceConnectionMap = make(map[int64]net.Conn)
)

// once connected then create the connection
func StoreDeviceConnectionMap(imei int64, conn net.Conn) {
	mu.Lock()
	deviceConnectionMap[imei] = conn
	mu.Unlock()

	data, err := json.Marshal(types.DeviceConnectionMetric{
		Imei:      imei,
		Status:    types.CONNECTED,
		TimeStamp: time.Now().Unix(),
	})
	if err != nil {
		logger.Error("device status marshal imei=%d failed: %v", imei, err)
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	pipe := redis.RedisClient.Pipeline()
	pipe.HSet(ctx, deviceStatusKey, strconv.FormatInt(imei, 10), data)
	pipe.Publish(ctx, deviceStatusChannel, data)
	if _, err := pipe.Exec(ctx); err != nil {
		logger.Error("redis store imei=%d failed: %v", imei, err)
	}
}

// get device conn by imei
func GetDeviceConn(imei int64) (net.Conn, bool) {
	logger.Info("device connection check going on")
	mu.RLock()
	defer mu.RUnlock()
	conn, ok := deviceConnectionMap[imei]
	return conn, ok
}

func FetchDeviceExists(imei int64) bool {
	mu.RLock()
	defer mu.RUnlock()
	_, ok := deviceConnectionMap[imei]
	return ok
}

func GetAllConnectedDevice() []int64 {
	mu.RLock()
	defer mu.RUnlock()
	var deviceImeis []int64
	for key := range deviceConnectionMap {
		deviceImeis = append(deviceImeis, key)
	}
	return deviceImeis
}

// Destory Imei Connection
func DestoryImeiConn(imei int64, conn net.Conn) {
	mu.Lock()
	removed := false
	if current, ok := deviceConnectionMap[imei]; ok && current == conn {
		delete(deviceConnectionMap, imei)
		removed = true
	}
	mu.Unlock()

	if !removed {
		return
	}

	data, err := json.Marshal(types.DeviceConnectionMetric{
		Imei:      imei,
		Status:    types.DISCONNECTED,
		TimeStamp: time.Now().Unix(),
	})
	if err != nil {
		logger.Error("device status marshal imei=%d failed: %v", imei, err)
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	pipe := redis.RedisClient.Pipeline()
	pipe.HDel(ctx, deviceStatusKey, strconv.FormatInt(imei, 10))
	pipe.Publish(ctx, deviceStatusChannel, data)
	if _, err := pipe.Exec(ctx); err != nil {
		logger.Error("redis remove imei=%d failed: %v", imei, err)
	}
}
