package models

import (
	"encoding/json"
	"errors"
	"fmt"

	"gorm.io/datatypes"
)

// ModbusFunctionCode 定义Modbus功能码的枚举类型
type ModbusFunctionCode byte

// 定义常用的Modbus功能码常量及其应用场景
const (
	// ReadCoils 读取线圈状态 (0x01)
	// 场景: 用于读取数字量输出（DO）或内部标志位的当前状态，这些状态通常是开关量。
	ReadCoils ModbusFunctionCode = 0x01
	// ReadDiscreteInputs 读取离散输入状态 (0x02)
	// 场景: 用于读取数字量输入（DI）的当前状态，这些状态通常是外部传感器的开关量信号。
	ReadDiscreteInputs ModbusFunctionCode = 0x02
	// ReadHoldingRegisters 读取保持寄存器 (0x03)
	// 场景: 用于读取设备内部可读写的参数或数据，例如温度设定值、电机速度等模拟量或配置数据。
	ReadHoldingRegisters ModbusFunctionCode = 0x03
	// ReadInputRegisters 读取输入寄存器 (0x04)
	// 场景: 用于读取设备的模拟量输入（AI）数据，这些数据通常是只读的，例如当前温度、压力、电压等实时测量值。
	ReadInputRegisters ModbusFunctionCode = 0x04
	// WriteSingleCoil 写入单个线圈 (0x05)
	// 场景: 用于控制单个数字量输出（DO），例如打开或关闭一个继电器、指示灯等。
	WriteSingleCoil ModbusFunctionCode = 0x05
	// WriteSingleRegister 写入单个保持寄存器 (0x06)
	// 场景: 用于修改设备内部的单个可写参数，例如设置一个温度控制器的目标温度、调整一个阀门的开度等。
	WriteSingleRegister ModbusFunctionCode = 0x06
	// WriteMultipleCoils 写入多个线圈 (0x0F)
	// 场景: 用于批量控制多个数字量输出（DO），例如同时打开或关闭一组继电器。
	WriteMultipleCoils ModbusFunctionCode = 0x0F
	// WriteMultipleRegisters 写入多个保持寄存器 (0x10)
	// 场景: 用于批量修改设备内部的多个可写参数，例如一次性更新多个配置参数或模拟量输出值。
	WriteMultipleRegisters ModbusFunctionCode = 0x10
)

// DeviceCategory 定义了设备模板的宽泛类别
type DeviceCategory string

const (
	// CategoryActuator 代表一个执行器，可以被控制（例如：风机、阀门）
	CategoryActuator DeviceCategory = "执行器"
	// CategorySensor 代表一个传感器，用于报告测量值（例如：温度计）
	CategorySensor DeviceCategory = "传感器"
)

// ValueDescriptor 描述了传感器可以报告的单个数值。
// 它提供了必要的元数据，以便应用程序能够正确解释从设备读取的原始数据。
type ValueDescriptor struct {
	Type       SensorType `json:"type"`
	Multiplier float32    `json:"multiplier"` // 乘数，用于原始数据转换
	Offset     float32    `json:"offset"`     // 偏移量，用于原始数据转换
}

// --- 指令结构体 (Command Structs) ---

// SwitchCommands 定义了开关类指令所需的Modbus参数
type SwitchCommands struct {
	// ModbusStartAddress 记录Modbus寄存器的起始地址，用于生成指令。(一般是第三到四字节)
	ModbusStartAddress uint16 `json:"modbus_start_address"`
	// ModbusQuantity 记录Modbus寄存器的数量，对于开关通常为1。(一般是五到六字节)
	ModbusQuantity uint16 `json:"modbus_quantity"`
}

// SelfCheck 校验开关指令参数的有效性
func (sc *SwitchCommands) SelfCheck() error {
	// 对于开关，数量通常为1
	if sc.ModbusQuantity != 1 {
		return errors.New("'switch' 指令集 ModbusQuantity 必须为1")
	}
	return nil
}

// SensorCommands 定义了传感器读取指令所需的Modbus参数
type SensorCommands struct {
	// ModbusFunctionCode 记录Modbus功能码，例如 ReadHoldingRegisters。(一般是第二字节)
	ModbusFunctionCode ModbusFunctionCode `json:"modbus_function_code"`
	// ModbusStartAddress 记录Modbus寄存器的起始地址，用于生成指令。(一般是第三到四字节)
	ModbusStartAddress uint16 `json:"modbus_start_address"`
	// ModbusQuantity 记录Modbus寄存器的数量，用于生成指令。(一般是五到六字节)
	ModbusQuantity uint16 `json:"modbus_quantity"`
}

// SelfCheck 校验读取指令参数的有效性
func (sc *SensorCommands) SelfCheck() error {
	// 校验ModbusFunctionCode是否为读取类型
	switch sc.ModbusFunctionCode {
	case ReadCoils, ReadDiscreteInputs, ReadHoldingRegisters, ReadInputRegisters:
		// 支持的读取功能码
	default:
		return fmt.Errorf("'sensor' 指令集 ModbusFunctionCode %X 无效或不是读取类型", sc.ModbusFunctionCode)
	}

	// 校验ModbusQuantity的合理性，例如不能为0，且在常见Modbus读取数量限制内
	if sc.ModbusQuantity == 0 || sc.ModbusQuantity > 125 {
		return fmt.Errorf("'sensor' 指令集 ModbusQuantity 无效: %d, 必须在1-125之间", sc.ModbusQuantity)
	}
	return nil
}

// DeviceTemplate 代表一种物理设备的类型。
type DeviceTemplate struct {
	Model

	// Name 是此模板的唯一名称, 例如 "FanModel-XYZ-2000" 或 "TempSensor-T1"
	Name string `gorm:"not null" json:"name"`

	// Manufacturer 是设备的制造商。
	Manufacturer string `json:"manufacturer"`

	// Description 提供了关于此设备类型的更多详细信息。
	Description string `json:"description"`

	// Category 将模板分类为传感器、执行器
	Category DeviceCategory `gorm:"not null;index" json:"category"`

	// Commands 存储了生成Modbus指令所需的参数，而不是原始指令字符串。
	// 使用 JSON 格式，具有良好的可扩展性。
	// 例如，对于执行器 (开关): {"modbus_start_address": 0, "modbus_quantity": 1}
	// 例如，对于传感器: {"modbus_function_code": 3, "modbus_start_address": 0, "modbus_quantity": 1}
	Commands datatypes.JSON `json:"commands"`

	// Values 描述了传感器模板所能提供的数据点。
	// 当 Category 是 "sensor" 时，此字段尤为重要。
	// 它是一个 ValueDescriptor 对象的 JSON 数组。
	Values datatypes.JSON `json:"values"`
}

// TableName 自定义 GORM 使用的数据库表名
func (DeviceTemplate) TableName() string {
	return "device_templates"
}

// ParseCommands ...
func (dt *DeviceTemplate) ParseCommands(v interface{}) error {
	if dt.Commands == nil {
		return errors.New("设备模板的 Commands 属性为空，无法解析")
	}
	return json.Unmarshal(dt.Commands, v)
}

// ParseValues ...
func (dt *DeviceTemplate) ParseValues(v interface{}) error {
	if dt.Values == nil {
		return errors.New("设备模板的 Values 属性为空，无法解析")
	}
	return json.Unmarshal(dt.Values, v)
}

// SelfCheck 对 DeviceTemplate 进行彻底的、基于角色的校验
func (dt *DeviceTemplate) SelfCheck() error {
	if dt.Commands == nil {
		return errors.New("所有设备模板都必须有 Commands 定义")
	}

	switch dt.Category {
	case CategoryActuator:
		var cmd SwitchCommands
		if err := dt.ParseCommands(&cmd); err != nil {
			return errors.New("执行器模板的 Commands 无法被解析为 'switch' 指令集: " + err.Error())
		}
		if err := cmd.SelfCheck(); err != nil {
			return err
		}

	case CategorySensor:
		var cmd SensorCommands
		if err := dt.ParseCommands(&cmd); err != nil {
			return errors.New("传感器模板的 Commands 无法被解析为 'sensor' 指令集: " + err.Error())
		}
		if err := cmd.SelfCheck(); err != nil {
			return err
		}

		if dt.Values == nil {
			return errors.New("传感器类型的设备模板缺少 Values 定义")
		}
		// 这里应该解析为 ValueDescriptor 的切片，因为传感器可能提供多个数据点
		var values []ValueDescriptor
		if err := dt.ParseValues(&values); err != nil {
			return errors.New("无法解析传感器模板的 Values 属性: " + err.Error())
		}
		if len(values) == 0 {
			return errors.New("传感器类型的设备模板 Values 属性不能为空")
		}
	default:
		return errors.New("未知的设备模板类别")
	}

	return nil
}
