package models

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

	"gorm.io/datatypes"
)

// --- Properties 结构体定义 ---

// Bus485Properties 定义了总线设备的特有属性
type Bus485Properties struct {
	BusNumber  uint8 `json:"bus_number"`  // 485 总线号
	BusAddress uint8 `json:"bus_address"` // 485 总线地址
}

// AreaControllerProperties 定义了区域主控的特有属性
type AreaControllerProperties struct {
	FirmwareVersion string `json:"firmware_version,omitempty"` // 主控程序版本
}

// AreaController 是一个LoRa转总线(如485)的通信网关
type AreaController struct {
	Model

	// Name 是主控的业务名称，例如 "1号猪舍主控"
	Name string `gorm:"not null" json:"name"`

	// NetworkID 是主控在通信网络中的唯一标识，例如 LoRaWAN 的 DevEUI。
	// 这是 transport 层用来寻址的关键。
	NetworkID string `gorm:"not null;index" json:"network_id"`

	// Location 描述了主控的物理安装位置。
	Location string `gorm:"index" json:"location"`

	// Status 表示主控的在线状态等，可以后续扩展。
	Status string `gorm:"default:'unknown'" json:"status"`

	// Properties 用于存储其他与主控相关的属性，例如硬件版本、固件版本等。
	Properties datatypes.JSON `json:"properties"`
}

// SelfCheck 对 AreaController 的关键字段进行业务逻辑验证。
func (ac *AreaController) SelfCheck() error {
	if strings.TrimSpace(ac.NetworkID) == "" {
		return errors.New("区域主控的 NetworkID 不能为空")
	}
	return nil
}

// ParseProperties 解析 JSON 属性到一个具体的结构体中。
// 调用方需要传入一个指向目标结构体实例的指针。
func (ac *AreaController) ParseProperties(v interface{}) error {
	if ac.Properties == nil {
		return errors.New("区域主控属性为空，无法解析")
	}
	return json.Unmarshal(ac.Properties, v)
}

// SetProperties 将一个结构体编码为 JSON 并设置到 Properties 字段。
func (ac *AreaController) SetProperties(v interface{}) error {
	if v == nil {
		ac.Properties = nil
		return nil
	}
	jsonBytes, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("无法编码区域主控的属性 (Properties): %w", err)
	}
	ac.Properties = jsonBytes
	return nil
}

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

// Device 代表系统中的所有普通设备
type Device struct {
	// Model 内嵌了标准模型字段 (ID, CreatedAt, UpdatedAt, DeletedAt)
	Model

	// Name 是设备的业务名称，应清晰可读，例如 "1号猪舍温度传感器"
	Name string `gorm:"not null" json:"name"`

	// DeviceTemplateID 是设备模板的外键
	DeviceTemplateID uint32 `gorm:"not null;index" json:"device_template_id"`

	// DeviceTemplate 是设备的模板，包含了设备的通用信息
	DeviceTemplate DeviceTemplate `json:"device_template"`

	// AreaControllerID 是区域主控的外键
	AreaControllerID uint32 `gorm:"not null;index" json:"area_controller_id"`

	// AreaController 是设备所属的区域主控
	AreaController AreaController `json:"area_controller"`

	// Location 描述了设备的物理安装位置，例如 "1号猪舍东侧"，方便运维。建立索引以优化按位置查询。
	Location string `gorm:"index" json:"location"`

	// Properties 用于存储特定类型设备的独有属性，采用JSON格式。
	// 建议在应用层为不同子类型的设备定义专用的属性结构体，以保证数据一致性。
	Properties datatypes.JSON `json:"properties"`

	// Tasks 是与此设备关联的任务列表，通过 DeviceTask 关联表实现多对多关系
	Tasks []Task `gorm:"many2many:device_tasks;" json:"tasks"`
}

// SelfCheck 对 Device 的关键字段和属性进行业务逻辑验证。
func (d *Device) SelfCheck() error {
	if d.AreaControllerID == 0 {
		return errors.New("设备必须关联一个区域主控 (AreaControllerID不能为0)")
	}
	if d.DeviceTemplateID == 0 {
		return errors.New("设备必须关联一个设备模板 (DeviceTemplateID不能为0)")
	}

	// 验证 Properties 是否包含必要的总线地址信息
	if d.Properties == nil {
		return errors.New("设备属性 (Properties) 不能为空")
	}

	var props Bus485Properties
	if err := json.Unmarshal(d.Properties, &props); err != nil {
		return errors.New("无法解析设备属性 (Properties)")
	}

	if props.BusAddress == 0 {
		return errors.New("设备属性 (Properties) 中缺少总线地址 (bus_address)")
	}

	return nil
}

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

// ParseProperties 解析 JSON 属性到一个具体的结构体中。
// 调用方需要传入一个指向目标结构体实例的指针。
// 示例：
//
//	var props LoraProperties
//	if err := device.ParseProperties(&props); err != nil { ... }
func (d *Device) ParseProperties(v interface{}) error {
	if d.Properties == nil {
		return errors.New("设备属性为空，无法解析")
	}
	return json.Unmarshal(d.Properties, v)
}
