package maincontrollerClient

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/towgo/towgo/towgo"
	"go.uber.org/zap"
	"sync"
	g "tgk-touch/internal/global"
)

var controllerClient *MainControllerClient

type Device struct {
	ID              int64  `json:"id" xorm:"pk"`
	SerialNumber    string `json:"serial_number" xorm:"index unique"`
	Name            string `json:"name"`
	Classify        string `json:"classify"`
	IsOnline        bool   `json:"is_online" xorm:"-"`
	LastSubsistTime int64  `json:"last_subsist_time"`
	UpdatedAt       int64  `xorm:"updated"`
	CreatedAt       int64  `xorm:"created"`
}

func ControllerClient() *MainControllerClient {
	if controllerClient == nil {
		controllerClient = NewMainControllerClient(MainControllerClientConfig{
			ServerHost: g.Config().MainControllerClient.ServerHost,
			SecretKey:  g.Config().MainControllerClient.SecretKey,
		})
	}
	return controllerClient
}

type MainControllerClientConfig struct {
	ServerHost string `json:"server_host"`
	SecretKey  string `json:"secret_key"`
}

type MainControllerClient struct {
	MainControllerClientConfig
	*towgo.WebScoketClient
}

func NewMainControllerClient(config MainControllerClientConfig) *MainControllerClient {
	g.Log().Info("初始化主控客户端")
	ws := towgo.NewWebsocketClient(config.ServerHost, config.ServerHost)
	m := &MainControllerClient{}
	m.MainControllerClientConfig = config
	m.WebScoketClient = ws
	m.CallTimeOut = 10
	m.OnConnect = func(wsc *towgo.WebScoketClient) {
		m.mainControllerClientReg(wsc)
	}
	m.OnClose = func(wsc *towgo.WebScoketClient) {}
	m.Connect()
	return m
}

// 服务注册
func (m *MainControllerClient) mainControllerClientReg(wsc *towgo.WebScoketClient) {
	g.Log().Info("注册主控客户端到服务端")
	request := towgo.NewJsonrpcrequest()
	request.Method = "/iotserver/maincontroller/reg"
	request.Params = m.MainControllerClientConfig
	wsc.Call(request, func(jrc towgo.JsonRpcConnection) {
		if jrc.GetRpcResponse().Error.Code != 200 {
			g.Log().Errorf("mainControllerClientReg call err {%v}", jrc.GetRpcResponse().Error.Message)
		}
	})
	request.Await()
}
func getParamsType(params any) string {
	switch params.(type) {
	case []byte:
		return "[]byte"
	}
	return ""
}

// 定义日志格式化回调类型，由调用者自定义友好显示内容
type LogFormatter func(deviceID, method string, params, result interface{}) (deviceType, cmdDetail, cmdDisplay, resultDisplay string)

// 带日志记录的设备调用函数，新增formatter参数用于自定义日志格式
func DeviceCallByDeviceID(deviceID, method, token string, params, destResult any, formatter LogFormatter) error {
	var err error

	err = deviceCallByDeviceID(deviceID, method, token, params, destResult)
	// 构建日志基础信息
	logEntry := New(deviceID, params, destResult)

	if err != nil {
		zap.S().Errorf("%+v", err)
		logEntry.Error = err.Error()
	}

	// 应用自定义格式化（如果提供）
	if formatter != nil {
		devType, cmdDetail, cmdDisp, resultDisp := formatter(deviceID, method, params, destResult)
		if cmdDisp == "" {
			cmdDisp = formatDefault(params)
		}
		if resultDisp == "" {
			resultDisp = formatDefault(destResult)
		}
		logEntry.CmdDisply = cmdDisp
		logEntry.ResultDisply = resultDisp
		logEntry.DeviceType = devType
		logEntry.CmdDetail = cmdDetail
	} else {
		// 默认格式化（处理字节切片等特殊类型）
		logEntry.CmdDisply = formatDefault(params)
		logEntry.ResultDisply = formatDefault(destResult)
	}
	zap.S().Debugf("%+v", logEntry)
	return err
}

// formatDefault 默认格式化：优先用%q处理字符串类，特殊类型单独适配
func formatDefault(data interface{}) string {
	if data == nil {
		return "%q" // 明确标记nil，比"null"更符合%q的语义
	}

	// 1. 处理字符串：直接用%q，自动转义特殊字符
	if s, ok := data.(string); ok {
		return fmt.Sprintf("%q", s)
	}

	// 2. 处理字节切片：先转十六进制，再用%q包裹（避免乱码，且显式标记类型）
	if b, ok := data.([]byte); ok {
		// 若字节切片是可打印ASCII，优先转ASCII+%q；否则转十六进制
		if isPrintableASCII(b) {
			return fmt.Sprintf("[]byte(ascii): %q", string(b))
		}
		return fmt.Sprintf("[]byte(hex): %q", hex.EncodeToString(b))
	}

	// 3. 处理符文（rune）：用%q显示字符+Unicode码点
	if r, ok := data.(rune); ok {
		return fmt.Sprintf("rune: %q (U+%04X)", r, r)
	}

	// 4. 复杂类型（结构体/切片/映射）：用JSON序列化，字符串字段自动带%q效果
	// （JSON序列化会自动转义特殊字符，等价于%q的最小化转义）
	jsonBytes, err := json.MarshalIndent(data, "", "  ")
	if err == nil {
		if string(jsonBytes) != "null" && string(jsonBytes) != "" {
			return fmt.Sprintf("json: %q", string(jsonBytes)) // 用%q包裹JSON字符串，显式转义双引号
		}
	}

	// 5. 其他基础类型（int/bool/float等）：用%+v（%q会多一层引号，冗余）
	return fmt.Sprintf("%+v", data)
}

// 辅助函数：判断字节切片是否全是可打印ASCII（32-126）
func isPrintableASCII(b []byte) bool {
	for _, c := range b {
		if c < 32 || c > 126 {
			return false
		}
	}
	return true
}

var pushLock sync.Mutex = sync.Mutex{}

func deviceCallByDeviceID(deviceID, method, token string, params, destResult any) error {
	if serialPortMode {
		pushLock.Lock()
		defer pushLock.Unlock()
		b, err := ToBytes(params)
		if err != nil {
			return err
		}
		result, err := serialPort.BIO(b)
		if err != nil {
			return gerror.Wrap(err, "serialPort.BIO result err")
		}
		b, err = json.Marshal(result)
		if err != nil {
			return gerror.Wrap(err, "json.Marshal(result) err")
		}

		return json.Unmarshal(b, destResult)
	}
	deviceRequest := towgo.NewJsonrpcrequest()
	deviceRequest.Method = method
	deviceRequest.Session = token
	deviceRequest.Params = params
	deviceRequest.DataType = getParamsType(params)

	mainControllerRequest := towgo.NewJsonrpcrequest()

	mainControllerRequest.Method = "/iotserver/maincontroller/device/call"
	mainControllerRequest.Params = deviceRequest
	mainControllerRequest.Route.DestAddr = deviceID

	var err error
	ControllerClient().
		Call(mainControllerRequest, func(jrc towgo.JsonRpcConnection) {
			//g.Log().Debug(jrc.GetRpcResponse())
			if jrc.GetRpcResponse().Error.Code != 200 {
				err = gerror.Newf("deviceId [%s],msg %s", deviceID, jrc.GetRpcResponse().Error.Message)
				return
			}
			jrc.ReadResult(destResult)
		})
	mainControllerRequest.Await()

	return err
}

type DeviceControllerLog struct {
	DeviceId   string `json:"device_id"  xorm:"comment('网关Id')"  `
	DeviceType string `json:"device_type,omitempty" xorm:"comment('设备类型')"`

	CmdDetail string `json:"cmd_detail" xorm:"text comment('命令描述')"`
	Cmd       string `json:"cmd" xorm:"text notnull  comment('指令%q字符串')"  `

	Result string `json:"result" xorm:"text notnull  comment('结果%q字符串')" `

	CmdDisply    string `json:"cmd_disply" xorm:"text comment('命令友好显示')"`
	ResultDisply string `json:"result_disply" xorm:"text comment('结果友好显示')"`

	Error string `json:"error" xorm:" varchar(255)    comment('指令执行异常')" `
}

func (DeviceControllerLog) TableName() string {
	return "device_controller_log"
}
func New(deviceId string, cmd, result interface{}) *DeviceControllerLog {
	return &DeviceControllerLog{
		DeviceId: deviceId,
		Cmd:      formatDefault(cmd),
		Result:   formatDefault(result),
	}
}
