package m9zApi

import (
	"fmt"
	"github.com/gogf/gf/v2/errors/gerror"
	"math"
	"sort"
	"strings"
	"sync"
	"tgk-touch/internal/library/m9z"
	"time"

	"github.com/towgo/towgo/towgo"
)

func getDeviceId(rpc towgo.JsonRpcConnection) (string, []uint) {
	var p struct {
		CommUid string `json:"comm_uid"`
		Idxs    []uint `json:"idxs"`
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}
	return p.CommUid, p.Idxs
}

// // 获取设备输出状态参数
func getOutStatus(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	read, err := m9z.DeviceOutStatusRead(cuid)
	if err != nil {
		panic(err)

	}
	rpc.WriteResult(read)
}

// todo 有问题 不管写入什么模式返回都是FF 获取设备编程模式  1-3 对应模式01~模式03 编程模式0~2
func getDeviceMode(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	read, err := m9z.ModeRead(cuid)
	if err != nil {
		panic(err)
	}
	rpc.WriteResult(read)
}

// 读取手动控制信息
func getDeviceManualInfo(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	manual, err := m9z.ManualRead(cuid)
	if err != nil {
		panic(err)
	}
	if !manual.Able {
		// 如果当前非手动控制模式就读取设备状态后赋值给手动控制对象
		//读取设备状态
		time.Sleep(time.Second)
		read, err := m9z.DeviceStatusRead(cuid)
		if err != nil {
			panic(err)
		}
		for i, _ := range manual.Loops {
			manual.Loops[i].IsOpen = read.Relays[i]
			manual.Loops[i].Pwm = int(read.PWMOutputs[i])
		}
	}
	rpc.WriteResult(manual)
}

// 获取设备上的时间
func getDeviceTime(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	deviceTime, timestampSec, err := m9z.DeviceTimeRead(cuid)
	if err != nil {
		panic(err)
	}
	result := make(map[string]interface{})
	result["deviceTime"] = deviceTime.Format("2006-01-02 15:04:05")
	result["timestampSec"] = timestampSec
	rpc.WriteResult(result)

}

// 获取设备经纬度信息
func getLgnLatData(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	manual, err := m9z.LgnLatRead(cuid)
	if err != nil {
		panic(err)
	}
	r := map[string]interface{}{}
	r["lgn"] = manual.Lgn
	r["lat"] = manual.Lat
	r["loc"] = manual.Loc.String()
	rpc.WriteResult(r)
}

// 获取日出日落时间偏差
func getSunRiseSet(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)
	read, err := m9z.SunRiseSetRead(cuid)
	if err != nil {
		panic(err)
	}
	rpc.WriteResult(read)
}

// 获取设备的状态
func getDeviceStatus(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	read, err := m9z.DeviceStatusRead(cuid)
	if err != nil {
		panic(err)
	}
	rpc.WriteResult(read)
}

type DeviceStatusLoop struct {
	m9z.Loop
	V         float32
	A         float32
	Kw        float32
	HasFault  bool            `json:"has_fault"`
	FaultMsg  string          `json:"fault_msg"`
	FaultCode string          `json:"fault_code"`
	Faults    []m9z.LoopFault `json:"faults"`
}
type DeviceStatus struct {
	m9z.DeviceStatus
	ScanDeviceCount int                `json:"scan_device_count"`
	Loops           []DeviceStatusLoop `json:"Loops"`
}

// 实时监控数据
func getDeviceStatus2(rpc towgo.JsonRpcConnection) {
	// 临时演示模式：原串口读取实现已保留在 getDeviceStatus2Real。
	getDeviceStatus2Real(rpc)
	//rpc.WriteResult(buildDemoDeviceStatus2())
}

func getDeviceStatus2Real(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	read, err := m9z.DeviceStatusRead(cuid)
	if err != nil {
		panic(err)
	}
	d := new(DeviceStatus)
	d.DeviceStatus = *read
	scanLoopCount := configuredLoopCount()
	displayLoopCount := monitorDisplayLoopCount()
	d.ScanDeviceCount = scanLoopCount
	loops := make([]DeviceStatusLoop, displayLoopCount)
	var wg sync.WaitGroup
	for i := 0; i < displayLoopCount; i++ {
		idx := i
		wg.Add(1)
		go func() {
			defer wg.Done()
			var loop DeviceStatusLoop
			loop.Name = fmt.Sprintf("#%+v", idx+1)
			loop.Index = idx
			loop.IsOpen = read.Relays[idx]
			loop.Pwm = int(read.PWMOutputs[idx])
			parametersRead, _ := m9z.SubLoopParametersRead(cuid, uint(idx))
			if parametersRead != nil {
				loop.V = parametersRead.DCVoltage
				loop.A = parametersRead.DCCurrent
				kw := float64(loop.V*loop.A) / 1000
				loop.Kw = float32(math.Round(kw*1000) / 1000)
				if idx < scanLoopCount {
					loop.Faults = m9z.BuildLoopFaults(uint(idx+1), parametersRead.AlarmStatus, parametersRead.AlarmCode)
					loop.HasFault = len(loop.Faults) > 0
					loop.FaultMsg = joinLoopFaultMessages(loop.Faults)
					loop.FaultCode = joinLoopFaultCodes(loop.Faults)
				}
			}
			loops[idx] = loop
		}()

	}
	wg.Wait()
	d.Loops = loops
	// 按 Timestamp 排序（最早的在前）
	sort.Slice(d.Loops, func(i, j int) bool {
		return d.Loops[i].Index < d.Loops[j].Index
	})
	for _, loop := range d.Loops {
		if loop.HasFault && loop.Index < scanLoopCount {
			recordLoopFaults(cuid, uint(loop.Index+1), loop.V, loop.Faults)
		}
	}
	rpc.WriteResult(d)

}

func buildDemoDeviceStatus2() *DeviceStatus {
	displayLoopCount := monitorDisplayLoopCount()
	scanLoopCount := configuredLoopCount()
	d := &DeviceStatus{
		ScanDeviceCount: scanLoopCount,
		Loops:           make([]DeviceStatusLoop, displayLoopCount),
	}
	d.Mode = 0
	d.DeviceTime = time.Now().UTC()
	for i := 0; i < displayLoopCount; i++ {
		relayOpen := i%2 == 0
		pwm := uint8(35 + i*5)
		d.Relays[i] = relayOpen
		d.PWMOutputs[i] = pwm

		loop := DeviceStatusLoop{
			Loop: m9z.Loop{
				Index:  i,
				Name:   fmt.Sprintf("#%d", i+1),
				IsOpen: relayOpen,
				Pwm:    int(pwm),
			},
			V: float32(218 + i*3),
			A: float32(0.32 + float64(i)*0.08),
		}
		kw := float64(loop.V*loop.A) / 1000
		loop.Kw = float32(math.Round(kw*1000) / 1000)

		if i < scanLoopCount {
			switch i {
			case 0:
				loop.Faults = m9z.BuildLoopFaults(uint(i+1), 0x01, "01 00")
			case 1:
				loop.Faults = m9z.BuildLoopFaults(uint(i+1), 0x02, "02 00")
			case 6:
				loop.Faults = m9z.BuildLoopFaults(uint(i+1), 0x04, "04 03")
			case 8:
				loop.Faults = m9z.BuildLoopFaults(uint(i+1), 0x03, "03 00")
			default:
				loop.Faults = m9z.BuildLoopFaults(uint(i+1), 0x02, "02 00")
			}
		}
		loop.HasFault = len(loop.Faults) > 0
		loop.FaultMsg = joinLoopFaultMessages(loop.Faults)
		loop.FaultCode = joinLoopFaultCodes(loop.Faults)
		d.Loops[i] = loop
	}
	d.Optocoupler[0] = true
	d.Optocoupler[3] = true
	return d
}

func joinLoopFaultMessages(faults []m9z.LoopFault) string {
	messages := make([]string, 0, len(faults))
	for _, fault := range faults {
		if fault.Message != "" {
			messages = append(messages, fault.Message)
		}
	}
	return strings.Join(messages, "；")
}

func joinLoopFaultCodes(faults []m9z.LoopFault) string {
	codes := make([]string, 0, len(faults))
	for _, fault := range faults {
		if fault.Code != "" {
			codes = append(codes, fault.Code)
		}
	}
	return strings.Join(codes, ",")
}

// 获取设备的配置
func getDeviceConfig(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	read, err := m9z.DeviceConfigRead(cuid)
	if err != nil {
		panic(err)
	}
	rpc.WriteResult(read)
}

// 获取日出日落时间
func getDeviceSunRiseTime(rpc towgo.JsonRpcConnection) {
	cuid, _ := getDeviceId(rpc)

	read, err := m9z.SunRiseTimeRead(cuid)
	if err != nil {
		panic(err)
	}
	rpc.WriteResult(read)

}

// 读取开始任务 (优化指令 CMD=0x13)
func getStartTask(rpc towgo.JsonRpcConnection) {
	var p struct {
		CommUid string `json:"comm_uid"`
		Mode    uint8  `json:"mode"` // 0x00-0x02
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	read, err := m9z.StartTaskRead(p.CommUid, p.Mode)
	if err != nil {
		panic(err)
	}
	/*program, err := m9z.ControlTaskToTaskProgram(m9z.DefaultStopTask, p.Mode)
	if err != nil {
		panic(err)
	}*/
	/*err = m9z.StartTaskWriteByTaskCfg(p.CommUid, p.Mode, m9z.DefaultStartTask)
	if err != nil {
		panic(err)
	}*/

	rpc.WriteResult(read)
}

// 读取中间任务 (优化指令 CMD=0x14)
func getMiddleTaskRead(rpc towgo.JsonRpcConnection) {
	var p struct {
		CommUid string `json:"comm_uid"`
		Mode    uint8  `json:"mode"` // 0x00-0x02
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	read, err := m9z.MiddleTaskRead(p.CommUid, p.Mode)
	if err != nil {
		panic(err)
	}
	rpc.WriteResult(read)
}

// 读取结束任务 (优化指令 CMD=0x15)
func getStopTaskRead(rpc towgo.JsonRpcConnection) {
	var p struct {
		CommUid string `json:"comm_uid"`
		Mode    uint8  `json:"mode"` // 0x00-0x02
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	read, err := m9z.StopTaskRead(p.CommUid, p.Mode)
	if err != nil {
		panic(err)
	}

	rpc.WriteResult(read)
}
func getStartTask2(rpc towgo.JsonRpcConnection) {
	var p struct {
		CommUid string `json:"comm_uid"`
		Mode    uint8  `json:"mode"` // 0x00-0x02
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	read, err := m9z.StartTaskRead(p.CommUid, p.Mode)
	if err != nil {
		panic(gerror.Wrap(err, ""))
	}
	task, err := m9z.TaskProgramToControlTask(read)
	if err != nil {
		panic(err)

	}
	if task.TimeType == 1 {
		timeRead, err := m9z.SunRiseTimeRead(p.CommUid)
		if err != nil {
			panic(err)
		}
		task.ExecTime = timeRead.Sunrise
	}
	rpc.WriteResult(task)

}
func getStopTask2(rpc towgo.JsonRpcConnection) {
	var p struct {
		CommUid string `json:"comm_uid"`
		Mode    uint8  `json:"mode"` // 0x00-0x02
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	read, err := m9z.StopTaskRead(p.CommUid, p.Mode)
	if err != nil {
		panic(err)
	}
	task, err := m9z.TaskProgramToControlTask(read)
	if err != nil {
		panic(err)
	}
	if task.TimeType == 1 {
		timeRead, err := m9z.SunRiseTimeRead(p.CommUid)
		if err != nil {
			panic(err)
		}
		task.ExecTime = timeRead.Sunset
	}

	rpc.WriteResult(task)
}

func getMiddleTask2(rpc towgo.JsonRpcConnection) {

	var p struct {
		CommUid string `json:"comm_uid"`
		Mode    uint8  `json:"mode"` // 0x00-0x02
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	read, err := m9z.MiddleTaskRead(p.CommUid, p.Mode)
	if err != nil {
		panic(err)
	}
	/*program, err := m9z.MiddleControlTaskToTaskProgram(m9z.DefaultMiddleTask, p.Mode)
	if err != nil {
		panic(err)
	}*/
	task, err := m9z.TaskProgramToMiddleControlTask(read)
	if err != nil {
		panic(err)
	}

	rpc.WriteResult(task)
}

func getSubLoopParameters(rpc towgo.JsonRpcConnection) {
	var p struct {
		CommUid string `json:"comm_uid"`
		Idx     uint   `json:"idx"`
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	r, err := m9z.SubLoopParametersRead(p.CommUid, p.Idx)
	if err != nil {
		panic(err)
	}
	rpc.WriteResult(r)
}

func getAllSubLoopParameter(rpc towgo.JsonRpcConnection) {
	var p struct {
		CommUid string `json:"comm_uid"`
	}
	rpc.ReadParams(&p)
	if p.CommUid == "" {
		panic(gerror.New("comm_uid is empty"))
	}

	result := make(map[string]interface{})
	subLoopData := make([]*m9z.LoopFullParameters, 0)
	subLoopMap := make(map[string]*m9z.LoopFullParameters)
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			subLoop, err := m9z.SubLoopParametersRead(p.CommUid, uint(i))
			if err != nil || subLoop == nil {
				return
			}
			subLoopMap[subLoop.LoopAddress] = subLoop
		}()
	}

	wg.Wait()
	for _, v := range subLoopMap {
		subLoopData = append(subLoopData, v)
	}
	result["subLoopData"] = subLoopData
	totalP := 0.0

	for _, subLoop := range subLoopData {
		totalP = totalP + float64(subLoop.DCVoltage*subLoop.DCCurrent)
	}
	result["totalP"] = totalP
	rpc.WriteResult(result)
}
