package m9zApi

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/towgo/towgo/lib/system"

	g "tgk-touch/internal/global"
	"tgk-touch/internal/library/m9z"
)

var l sync.Mutex
var historyLock sync.Mutex
var filePath = system.GetPathOfProgram() + "data/data.json"

const (
	maxItems             = 50
	faultDuplicateWindow = 12 * time.Hour
)

func ScanM9z() {
	if !l.TryLock() {
		return
	}
	defer l.Unlock()

	cuid := g.Config().DeviceInfo.DeviceId
	for i := 0; i < configuredLoopCount(); i++ {
		read, err := m9z.SubLoopParametersRead(cuid, uint(i))
		if err != nil {
			g.Log().Error("sub loop parameters read error:", err)
			continue
		}
		if read == nil {
			continue
		}
		faults := m9z.BuildLoopFaults(uint(i+1), read.AlarmStatus, read.AlarmCode)
		recordLoopFaults(cuid, uint(i+1), read.DCVoltage, faults)
	}
}

func recordLoopFaults(commUid string, loopIdx uint, voltage float32, faults []m9z.LoopFault) {
	for _, fault := range faults {
		item := M9zFaultHistory{
			CommUid:   commUid,
			LoopIdx:   loopIdx,
			Voltage:   voltage,
			FaultMsg:  fault.Message,
			FaultCode: fault.Code,
			FaultTime: time.Now(),
		}
		if err := addData(item); err != nil {
			g.Log().Error(gerror.Wrap(err, "create error"))
		}
	}
}

func readData() ([]M9zFaultHistory, error) {
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		file, err := createFileWithPath(filePath)
		if err != nil {
			return nil, gerror.Wrap(err, "create file error")
		}
		_ = file.Close()
		return []M9zFaultHistory{}, nil
	}

	data, err := os.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("读取文件失败: %v", err)
	}

	var items []M9zFaultHistory
	if len(data) > 0 {
		if err := json.Unmarshal(data, &items); err != nil {
			return nil, fmt.Errorf("解析 JSON 失败: %v", err)
		}
	}
	for i := range items {
		items[i].ID = int64(i + 1)
	}
	return items, nil
}

func writeData(items []M9zFaultHistory) error {
	data, err := json.MarshalIndent(items, "", "  ")
	if err != nil {
		return fmt.Errorf("序列化 JSON 失败: %v", err)
	}
	return os.WriteFile(filePath, data, 0644)
}

func addData(newItem M9zFaultHistory) error {
	historyLock.Lock()
	defer historyLock.Unlock()

	items, err := readData()
	if err != nil {
		return gerror.Wrap(err, "readData")
	}

	if newItem.FaultTime.IsZero() {
		newItem.FaultTime = time.Now()
	}
	for _, item := range items {
		if isDuplicateFaultWithinWindow(item, newItem) {
			return nil
		}
	}

	items = append(items, newItem)

	if len(items) > maxItems {
		sort.Slice(items, func(i, j int) bool {
			return items[i].FaultTime.Before(items[j].FaultTime)
		})
		items = items[len(items)-maxItems:]
	}
	for i := range items {
		items[i].ID = int64(i + 1)
	}

	if err := writeData(items); err != nil {
		return gerror.Wrap(err, "writeData")
	}
	return nil
}

func isDuplicateFaultWithinWindow(item, newItem M9zFaultHistory) bool {
	if item.CommUid != newItem.CommUid ||
		item.LoopIdx != newItem.LoopIdx ||
		item.FaultCode != newItem.FaultCode ||
		item.FaultMsg != newItem.FaultMsg {
		return false
	}
	if item.FaultTime.IsZero() || newItem.FaultTime.IsZero() {
		return false
	}
	diff := newItem.FaultTime.Sub(item.FaultTime)
	if diff < 0 {
		diff = -diff
	}
	return diff < faultDuplicateWindow
}

func deleteById(id int) error {
	historyLock.Lock()
	defer historyLock.Unlock()

	items, err := readData()
	if err != nil {
		return gerror.Wrap(err, "readData")
	}
	if id < 1 || id > len(items) {
		return fmt.Errorf("ID %d 超出范围", id)
	}

	items = append(items[:id-1], items[id:]...)
	for i := id - 1; i < len(items); i++ {
		items[i].ID = int64(i + 1)
	}
	return writeData(items)
}

func createFileWithPath(filePath string) (*os.File, error) {
	if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
		return nil, err
	}
	return os.Create(filePath)
}
