package main

import (
	"runtime/debug"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/go-redis/redis/v7"
	"github.com/sirupsen/logrus"

	"lc/common/models"
	"lc/common/mqtt"
	"lc/common/protocol"
	"lc/common/util"
)

var _LampStrategyMgrOnce sync.Once
var _LampStrategyMgrSingle *LampStrategyMgr

func GetLampStrategyMgr() *LampStrategyMgr {
	_LampStrategyMgrOnce.Do(func() {
		_LampStrategyMgrSingle = &LampStrategyMgr{
			mapLampOOT:   make(map[string][]protocol.CHZB_OnOffTime),
			mapLampHlOOT: make(map[string][]protocol.HLWLZB_OnOffTime),
		}
	})
	return _LampStrategyMgrSingle
}

// LampStrategyQueue redis缓存
var LampStrategyQueue = "lamp_strategy_queue" //已修改的灯控编码列表，队列方式，先进先出

// LampStrategyMgr 灯控时间策略管理
type LampStrategyMgr struct {
	mapLampOOT   map[string][]protocol.CHZB_OnOffTime   //灯控策略时间，开灯时间段
	mapLampHlOOT map[string][]protocol.HLWLZB_OnOffTime //灯控策略时间，开灯时间段
}

func (o *LampStrategyMgr) Handler(args ...interface{}) interface{} {
	defer func() {
		if err := recover(); err != nil {
			gopool.Add(o.Handler, args)
			logrus.Errorf("LampStrategyMgr.Handler发生异常:%s", string(debug.Stack()))
		}
	}()
	IDList := make([]string, 0, 100)
	for {
		strID, err := redisCltRawData.RPop(LampStrategyQueue).Result() //灯控id
		if err != nil {
			if err != redis.Nil { //排除掉无数据的情况
				logrus.Errorf("读取redis队列lamp_strategy_queue发生错误:%s", err.Error())
			}
		} else {
			id := strings.Trim(strID, " ")
			if len(id) > 0 {
				IDList = append(IDList, id)
			}
		}
		//队列无数据并且IDList有数据，或IDList数据达到封顶值100，则进行处理
		if (err == redis.Nil && len(IDList) > 0) || len(IDList) == 100 {
			idlist := RemoveReplicaString(IDList)
			o.HandleZigbeeLampStrategy(idlist)   //处理zigbee灯控
			o.HandleHLZigbeeLampStrategy(idlist) //处理海蓝zigbee灯控
			o.HandleYm485LampStrategy(idlist)    //处理485灯控

			IDList = make([]string, 0, 100)
		}
		time.Sleep(1 * time.Second)
	}
}

type ConcentratorInfo struct {
	DID     string
	Tenant  string
	GID     string
	LampIDs []uint32
}

// HandleZigbeeLampStrategy 处理Zigbee灯控策略
func (o *LampStrategyMgr) HandleZigbeeLampStrategy(IDs []string) {
	zlsArr, err := models.GetZigbeeLampStrategyByIDs(IDs)
	if err != nil {
		oList := make([]interface{}, 0, len(IDs))
		for _, v := range IDs {
			oList = append(oList, v)
		}
		redisCltRawData.LPush(LampStrategyQueue, oList...)
	}
	if len(zlsArr) == 0 {
		return
	}
	//处理照明策略时间
	mapTime := make(map[string][]protocol.CHZB_OnOffTime) //策略编号->时间和亮度信息
	for _, v := range zlsArr {
		if _, ok := mapTime[v.Strategy]; ok {
			o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...)
			continue
		}
		var ls []models.LampStrategy //time_info字段
		if err := json.UnmarshalFromString(v.TimeInfo, &ls); err != nil {
			logrus.Errorf("HandleZigbeeLampStrategy:策略时间字段解析错误,字段内容:%s,错误原因:%s", v.TimeInfo, err.Error())
			continue
		}
		if len(ls) == 0 {
			continue
		}
		//日出日落
		if v.Sunset == 1 {
			if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil {
				onOffTimes := make([]protocol.CHZB_OnOffTime, 1)
				onOffTimes[0].Brightness = uint8(ls[0].Brightness)
				onHour, _ := strconv.Atoi(strings.Split(set, ":")[0])
				onMinute, _ := strconv.Atoi(strings.Split(set, ":")[1])
				offHour, _ := strconv.Atoi(strings.Split(rise, ":")[0])
				offMinute, _ := strconv.Atoi(strings.Split(rise, ":")[1])
				onOffTimes[0].OnHour = uint8(onHour)
				onOffTimes[0].OnMinite = uint8(onMinute)
				onOffTimes[0].OffHour = uint8(offHour)
				onOffTimes[0].OffMinite = uint8(offMinute)
				mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...)
				o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...)
			}
		} else {
			onOffTimes := make([]protocol.CHZB_OnOffTime, len(ls), len(ls))
			for i, v := range ls {
				onOffTimes[i].Brightness = uint8(v.Brightness)
				onHour, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[0])
				onMinute, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[1])
				offHour, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[0])
				offMinute, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[1])
				onOffTimes[i].OnHour = uint8(onHour)
				onOffTimes[i].OnMinite = uint8(onMinute)
				onOffTimes[i].OffHour = uint8(offHour)
				onOffTimes[i].OffMinite = uint8(offMinute)
			}
			mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...)
			o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...)
		}
	}

	//处理照明策略，集控器及末端编号
	mapStrategyToMapConcentratorToNumbers := make(map[string]map[string]*ConcentratorInfo)
	for _, v := range zlsArr {
		if _, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy]; !ok {
			mapStrategyToMapConcentratorToNumbers[v.Strategy] = make(map[string]*ConcentratorInfo)
		}
		if pConcentratorInfo, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator]; !ok {
			c := ConcentratorInfo{DID: v.Concentrator, Tenant: v.Tenant, GID: v.GID}
			c.LampIDs = append(c.LampIDs, uint32(v.Number))
			mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator] = &c
		} else {
			pConcentratorInfo.LampIDs = append(pConcentratorInfo.LampIDs, uint32(v.Number))
		}
	}
	for k0, v0 := range mapStrategyToMapConcentratorToNumbers {
		oots, ok := mapTime[k0]
		if !ok {
			continue
		}
		for k1, v1 := range v0 {
			var obj protocol.Pack_SetOnOffTime
			seq := GetNextSeq()
			str, err := obj.EnCode(k1, v1.GID, seq, v1.LampIDs, oots)
			if err != nil {
				continue
			}
			topic := GetTopic(v1.Tenant, protocol.DT_CONCENTRATOR, k1, protocol.TP_CHZB_SET_ONOFFTIME)
			var msg string
			if msg0, errmsg := json.MarshalIndent(obj, "", " "); errmsg == nil {
				msg = string(msg0)
			} else {
				msg = str
			}
			odb := models.DeviceCmdRecord{
				ID:      seq,
				GID:     v1.GID,
				DID:     k1,
				Topic:   topic,
				Message: msg,
				State:   0,
			}
			if err := models.G_db.Create(&odb).Error; err != nil {
				logrus.Errorf("HandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库错误:%s", k1, v1.LampIDs, err.Error())
			} else {
				logrus.Errorf("HandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库成功", k1, v1.LampIDs)
			}
			err = GetMQTTMgr().Publish(topic, str, mqtt.AtLeastOnce)
			if err != nil {
				logrus.Errorf("HandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落消息错误:%s", k1, v1.LampIDs, err.Error())
			}
		}
	}
}

// HandleHLZigbeeLampStrategy 处理海蓝Zigbee灯控策略
func (o *LampStrategyMgr) HandleHLZigbeeLampStrategy(IDs []string) {
	zlsArr, err := models.GetHLZigbeeLampStrategyByIDs(IDs) ////海蓝根据灯控编码列表，获取所有hlzigbee灯控
	if err != nil {
		oList := make([]interface{}, 0, len(IDs))
		for _, v := range IDs {
			oList = append(oList, v)
		}
		redisCltRawData.LPush(LampStrategyQueue, oList...) //灯控ID入队列中,待处理的ids
	}
	if len(zlsArr) == 0 {
		return
	}
	//处理照明策略时间-从存入到数据库中的策略处理
	mapTime := make(map[string][]protocol.HLWLZB_OnOffTime) //策略编号->时间和亮度信息
	for _, v := range zlsArr {
		if _, ok := mapTime[v.Strategy]; ok {
			o.mapLampHlOOT[v.ID] = append(o.mapLampHlOOT[v.ID], mapTime[v.Strategy]...)
			continue
		}
		var ls []models.LampStrategy //time_info字段
		if err := json.UnmarshalFromString(v.TimeInfo, &ls); err != nil {
			logrus.Errorf("HandleZigbeeLampStrategy:策略时间字段解析错误,字段内容:%s,错误原因:%s", v.TimeInfo, err.Error())
			continue
		}
		if len(ls) == 0 {
			continue
		}
		//日出日落
		if v.Sunset == 1 {
			if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil {
				oots := make([]protocol.HLWLZB_OnOffTime, 1)
				oots[0].Brightness = uint8(ls[0].Brightness)
				onhour, _ := strconv.Atoi(strings.Split(set, ":")[0])
				onminite, _ := strconv.Atoi(strings.Split(set, ":")[1])
				offhour, _ := strconv.Atoi(strings.Split(rise, ":")[0])
				offminite, _ := strconv.Atoi(strings.Split(rise, ":")[1])
				oots[0].OnHour = uint8(onhour)
				oots[0].OnMinite = uint8(onminite)
				oots[0].OffHour = uint8(offhour)
				oots[0].OffMinite = uint8(offminite)
				mapTime[v.Strategy] = append(mapTime[v.Strategy], oots...)
				o.mapLampHlOOT[v.ID] = append(o.mapLampHlOOT[v.ID], mapTime[v.Strategy]...)
			}
		} else {
			onOffTimes := make([]protocol.HLWLZB_OnOffTime, len(ls), len(ls))
			for i, v := range ls {
				onOffTimes[i].Brightness = uint8(v.Brightness)
				onHour, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[0])
				onMinute, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[1])
				offHour, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[0])
				offMinute, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[1])
				onOffTimes[i].OnHour = uint8(onHour)
				onOffTimes[i].OnMinite = uint8(onMinute)
				onOffTimes[i].OffHour = uint8(offHour)
				onOffTimes[i].OffMinite = uint8(offMinute)
			}
			mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...)
			o.mapLampHlOOT[v.ID] = append(o.mapLampHlOOT[v.ID], mapTime[v.Strategy]...)
		}
	}

	//处理照明策略，集控器及末端编号
	mapStrategyToMapConcentratorToNumbers := make(map[string]map[string]*ConcentratorInfo)
	for _, v := range zlsArr {
		if _, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy]; !ok {
			mapStrategyToMapConcentratorToNumbers[v.Strategy] = make(map[string]*ConcentratorInfo) //key为Strategy 没有就创建
		}
		if pConcentratorInfo, ok := mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator]; !ok { //没有就创建
			c := ConcentratorInfo{DID: v.Concentrator, Tenant: v.Tenant, GID: v.GID}
			c.LampIDs = append(c.LampIDs, uint32(v.Number)) //灯加入进去
			mapStrategyToMapConcentratorToNumbers[v.Strategy][v.Concentrator] = &c
		} else {
			pConcentratorInfo.LampIDs = append(pConcentratorInfo.LampIDs, uint32(v.Number)) //加灯
		}
	}
	for k0, v0 := range mapStrategyToMapConcentratorToNumbers {
		oots, ok := mapTime[k0]
		if !ok {
			continue
		}
		for k1, v1 := range v0 {

			if len(oots) == 1 && (oots[0].OffHour == 0 && oots[0].OffMinite == 0 && oots[0].OnHour == 0 && oots[0].OnMinite == 0) { //清除策略
				var obj protocol.Pack_HLClearStrategy

				seq := GetNextSeq()
				str, err := obj.EnCode(k1)
				logrus.Debugf("%s", str)

				s, err := json.MarshalToString(oots)
				logrus.Debugf("%s", s)
				if err != nil {
					logrus.Errorf("HLHandleZigbeeLampStrategy:集控器---[%s]对灯控[%v]发布清除策略指令入库错误:%s", k1, v1.LampIDs, err.Error())
					continue
				}
				topic := GetHLTopicDown(v1.Tenant, protocol.DT_CONCENTRATOR, k1, protocol.TP_CHZB_SET_ONOFFTIME)
				var msg string
				if msg0, errmsg := json.MarshalIndent(obj, "", " "); errmsg == nil {
					msg = string(msg0)
				} else {
					msg = str
				}
				odb := models.DeviceCmdRecord{ //记录入库
					ID:      seq,
					GID:     v1.GID,
					DID:     k1,
					Topic:   topic,
					Message: msg,
					State:   0,
				}
				if err := models.G_db.Create(&odb).Error; err != nil {
					logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布清除策略时指令入库错误:%s", k1, v1.LampIDs, err.Error())
				} else {
					logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布清除策略时指令入库成功", k1, v1.LampIDs)
				}
				err = GetHlMqttMgr().Publish(topic, str, mqtt.AtLeastOnce)
				if err != nil {
					logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布清除策略消息错误:%s", k1, v1.LampIDs, err.Error())
				}
				return
			}

			var obj protocol.Pack_HLSetOnOffTime

			seq := GetNextSeq()
			str, err := obj.EnCode(k1, v1.GID, seq, v1.LampIDs, oots)
			logrus.Debugf("%s", str)

			s, err := json.MarshalToString(oots)
			logrus.Debugf("%s", s)
			if err != nil {
				logrus.Errorf("HLHandleZigbeeLampStrategy:集控器---[%s]对灯控[%v]发布日出日落时间时指令入库错误:%s", k1, v1.LampIDs, err.Error())
				continue
			}
			topic := GetHLTopicDown(v1.Tenant, protocol.DT_CONCENTRATOR, k1, protocol.TP_CHZB_SET_ONOFFTIME)
			var msg string
			if msg0, errMsg := json.MarshalIndent(obj, "", " "); errMsg == nil {
				msg = string(msg0)
			} else {
				msg = str
			}
			odb := models.DeviceCmdRecord{ //记录入库
				ID:      seq,
				GID:     v1.GID,
				DID:     k1,
				Topic:   topic,
				Message: msg,
				State:   0,
			}
			if err := models.G_db.Create(&odb).Error; err != nil {
				logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库错误:%s", k1, v1.LampIDs, err.Error())
			} else {
				logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落时间时指令入库成功", k1, v1.LampIDs)
			}
			err = GetHlMqttMgr().Publish(topic, str, mqtt.AtLeastOnce)
			if err != nil {
				logrus.Errorf("HLHandleZigbeeLampStrategy:集控器[%s]对灯控[%v]发布日出日落消息错误:%s", k1, v1.LampIDs, err.Error())
			}
		}
	}
}

// HandleYm485LampStrategy 处理裕明485灯控
func (o *LampStrategyMgr) HandleYm485LampStrategy(IDs []string) {
	lsArr, err := models.GetYm485Lampstrategy(IDs)
	if err != nil {
		oList := make([]interface{}, 0, len(IDs))
		for _, v := range IDs {
			oList = append(oList, v)
		}
		redisCltRawData.LPush(LampStrategyQueue, oList...)
	}
	if len(lsArr) == 0 {
		return
	}
	//处理照明策略时间
	mapTime := make(map[string][]protocol.CHZB_OnOffTime) //策略编号->时间和亮度信息
	for _, v := range lsArr {
		if _, ok := mapTime[v.Strategy]; ok {
			o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...)
			continue
		}
		var ls []models.LampStrategy //time_info字段
		if err := json.UnmarshalFromString(v.TimeInfo, &ls); err != nil {
			logrus.Errorf("HandleYm485LampStrategy:策略时间字段解析错误,字段内容:%s,错误原因:%s", v.TimeInfo, err.Error())
			continue
		}
		if len(ls) == 0 {
			continue
		}
		//日出日落
		if v.Sunset == 1 {
			if rise, set, err := util.SunriseSunsetForChina(v.Latitude, v.Longitude); err == nil {
				onOffTimes := make([]protocol.CHZB_OnOffTime, 1)
				onOffTimes[0].Brightness = uint8(ls[0].Brightness)
				onHour, _ := strconv.Atoi(strings.Split(set, ":")[0])
				onMinute, _ := strconv.Atoi(strings.Split(set, ":")[1])
				offHour, _ := strconv.Atoi(strings.Split(rise, ":")[0])
				offMinute, _ := strconv.Atoi(strings.Split(rise, ":")[1])
				onOffTimes[0].OnHour = uint8(onHour)
				onOffTimes[0].OnMinite = uint8(onMinute)
				onOffTimes[0].OffHour = uint8(offHour)
				onOffTimes[0].OffMinite = uint8(offMinute)
				mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...)
				o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...)
			}
		} else {
			onOffTimes := make([]protocol.CHZB_OnOffTime, len(ls), len(ls))
			for i, v := range ls {
				onOffTimes[i].Brightness = uint8(v.Brightness)
				onHour, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[0])
				onMinute, _ := strconv.Atoi(strings.Split(v.Ontime, ":")[1])
				offHour, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[0])
				offMinute, _ := strconv.Atoi(strings.Split(v.Offtime, ":")[1])
				onOffTimes[i].OnHour = uint8(onHour)
				onOffTimes[i].OnMinite = uint8(onMinute)
				onOffTimes[i].OffHour = uint8(offHour)
				onOffTimes[i].OffMinite = uint8(offMinute)
			}
			mapTime[v.Strategy] = append(mapTime[v.Strategy], onOffTimes...)
			o.mapLampOOT[v.ID] = append(o.mapLampOOT[v.ID], mapTime[v.Strategy]...)
		}
	}

	//处理照明策略，集控器及末端编号
	for _, v := range lsArr {
		oots, ok := mapTime[v.Strategy]
		if !ok {
			continue
		}
		var obj protocol.Pack_SetOnOffTime
		seq := GetNextSeq()
		str, err := obj.EnCode(v.ID, v.GID, seq, nil, oots)
		if err != nil {
			continue
		}
		topic := GetTopic(v.Tenant, protocol.DT_LAMPCONTROLLER, v.ID, protocol.TP_YM_SET_ONOFFTIME)
		var msg string
		if msg0, errMsg := json.MarshalIndent(obj, "", " "); errMsg == nil {
			msg = string(msg0)
		} else {
			msg = str
		}
		odb := models.DeviceCmdRecord{
			ID:      seq,
			GID:     v.GID,
			DID:     v.ID,
			Topic:   topic,
			Message: msg,
			State:   0,
		}
		if err := models.G_db.Create(&odb).Error; err != nil {
			logrus.Errorf("HandleYm485LampStrategy:对灯控[%s]发布日出日落时间时指令入库错误:%s", v.ID, err.Error())
		} else {
			logrus.Errorf("HandleYm485LampStrategy:对灯控[%s]发布日出日落时间时指令入库成功", v.ID)
		}
		err = GetMQTTMgr().Publish(topic, str, mqtt.AtLeastOnce)
		if err != nil {
			logrus.Errorf("HandleYm485LampStrategy:对灯控[%s]发布日出日落消息错误:%s", v.ID, err.Error())
		}
	}
}

// RemoveReplicaString 字符串去重
func RemoveReplicaString(slc []string) []string {
	result := make([]string, 0)
	tempMap := make(map[string]bool, len(slc))
	for _, e := range slc {
		if _, ok := tempMap[e]; !ok {
			tempMap[e] = true
			result = append(result, e)
		}
	}
	return result
}
