package m9zApi

import (
	"encoding/json"
	"os"
	"path/filepath"
	"sync"

	g "tgk-touch/internal/global"

	"github.com/towgo/towgo/lib/system"
	"github.com/towgo/towgo/towgo"
)

const defaultAlarmDisplayEnabled = true

type ScanConfigManager struct {
	mu                  sync.RWMutex
	loopCount           int
	alarmDisplayEnabled bool
}

type ScanConfigData struct {
	LoopCount           int   `json:"loop_count"`
	AlarmDisplayEnabled *bool `json:"alarm_display_enabled,omitempty"`
}

var scanConfigManager = &ScanConfigManager{alarmDisplayEnabled: defaultAlarmDisplayEnabled}

func getScanConfigFilePath() string {
	return filepath.Join(system.GetPathOfProgram(), "data", "scanConfig.json")
}

func (m *ScanConfigManager) GetLoopCount() int {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return normalizeLoopCount(m.loopCount)
}

func (m *ScanConfigManager) GetAlarmDisplayEnabled() bool {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.alarmDisplayEnabled
}

func (m *ScanConfigManager) SetLoopCount(loopCount int) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.loopCount = normalizeLoopCount(loopCount)
	if err := m.saveToFile(); err != nil {
		return err
	}
	g.GVA_Config.DeviceInfo.LoopCount = m.loopCount
	return nil
}

func (m *ScanConfigManager) SetConfig(loopCount int, alarmDisplayEnabled *bool) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if loopCount > 0 {
		m.loopCount = normalizeLoopCount(loopCount)
		g.GVA_Config.DeviceInfo.LoopCount = m.loopCount
	}
	if alarmDisplayEnabled != nil {
		m.alarmDisplayEnabled = *alarmDisplayEnabled
	}
	if err := m.saveToFile(); err != nil {
		return err
	}
	return nil
}

func (m *ScanConfigManager) loadFromFile() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	filePath := getScanConfigFilePath()
	jsonData, err := os.ReadFile(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			m.loopCount = normalizeLoopCount(g.Config().DeviceInfo.LoopCount)
			m.alarmDisplayEnabled = defaultAlarmDisplayEnabled
			return m.saveToFile()
		}
		return err
	}

	var data ScanConfigData
	if err := json.Unmarshal(jsonData, &data); err != nil {
		return err
	}
	m.loopCount = normalizeLoopCount(data.LoopCount)
	if data.AlarmDisplayEnabled == nil {
		m.alarmDisplayEnabled = defaultAlarmDisplayEnabled
	} else {
		m.alarmDisplayEnabled = *data.AlarmDisplayEnabled
	}
	g.GVA_Config.DeviceInfo.LoopCount = m.loopCount
	return nil
}

func (m *ScanConfigManager) saveToFile() error {
	filePath := getScanConfigFilePath()
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	jsonData, err := json.Marshal(ScanConfigData{
		LoopCount:           normalizeLoopCount(m.loopCount),
		AlarmDisplayEnabled: boolPtr(m.alarmDisplayEnabled),
	})
	if err != nil {
		return err
	}
	return os.WriteFile(filePath, jsonData, 0600)
}

func getScanConfig(rpcConn towgo.JsonRpcConnection) {
	rpcConn.WriteResult(map[string]interface{}{
		"loop_count":            scanConfigManager.GetLoopCount(),
		"alarm_display_enabled": scanConfigManager.GetAlarmDisplayEnabled(),
		"max_loop_count":        maxLoopCount,
		"min_loop_count":        1,
	})
}

type SetScanConfigReq struct {
	LoopCount           int   `json:"loop_count"`
	AlarmDisplayEnabled *bool `json:"alarm_display_enabled"`
}

func setScanConfig(rpcConn towgo.JsonRpcConnection) {
	var req SetScanConfigReq
	rpcConn.ReadParams(&req)

	if req.LoopCount != 0 && (req.LoopCount < 1 || req.LoopCount > maxLoopCount) {
		rpcConn.WriteError(400, "loop_count must be between 1 and 10")
		return
	}
	if req.LoopCount == 0 && req.AlarmDisplayEnabled == nil {
		rpcConn.WriteError(400, "loop_count or alarm_display_enabled is required")
		return
	}
	if err := scanConfigManager.SetConfig(req.LoopCount, req.AlarmDisplayEnabled); err != nil {
		rpcConn.WriteError(500, "save scan config failed: "+err.Error())
		return
	}
	rpcConn.WriteResult(map[string]interface{}{
		"success":               true,
		"loop_count":            scanConfigManager.GetLoopCount(),
		"alarm_display_enabled": scanConfigManager.GetAlarmDisplayEnabled(),
		"max_loop_count":        maxLoopCount,
		"min_loop_count":        1,
	})
}

func boolPtr(value bool) *bool {
	return &value
}
