package models

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"go.uber.org/zap/zapcore"
	"gorm.io/gorm"
)

type AIModel string

const (
	AI_MODEL_NONE   AIModel = "None"
	AI_MODEL_GEMINI AIModel = "Gemini"
)

// Model 用于代替gorm.Model, 使用uint32以节约空间
type Model struct {
	ID        uint32 `gorm:"primarykey"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt gorm.DeletedAt `gorm:"index"`
}

// GetAllModels 返回一个包含所有数据库模型实例的切片。
// 这个函数用于在数据库初始化时自动迁移所有的表结构。
func GetAllModels() []interface{} {
	return []interface{}{
		// Core Models
		&User{},
		&UserActionLog{},

		// Device Models
		&Device{},
		&AreaController{},
		&DeviceTemplate{},
		&SensorData{},
		&DeviceCommandLog{},
		&DeviceTask{},

		// Plan & Task Models
		&Plan{},
		&SubPlan{},
		&Task{},
		&PlanExecutionLog{},
		&TaskExecutionLog{},
		&PendingTask{},
		&PendingCollection{},

		// Farm Asset Models
		&PigHouse{},
		&Pen{},

		// Pig & Batch Models
		&PigBatch{},
		&PigBatchLog{},
		&WeighingBatch{},
		&WeighingRecord{},
		&PigTransferLog{},
		&PigSickLog{},
		&PigBreed{},
		&PigAgeStage{},
		&PigType{},
		&PigNutrientRequirement{},

		// Pig Buy & Sell
		&PigPurchase{},
		&PigSale{},

		// Feed Models
		&RawMaterial{},
		&Nutrient{},
		&RawMaterialNutrient{},
		&RawMaterialStockLog{},
		&Recipe{},
		&RecipeIngredient{},

		// Medication Models
		&Medication{},
		&MedicationLog{},

		// Alarm Models
		&ActiveAlarm{},
		&HistoricalAlarm{},

		// Notification Models
		&Notification{},
	}
}

// UintArray 是一个自定义类型，代表 uint32 的切片。
// 它实现了 gorm.Scanner 和 driver.Valuer 接口，
// 以便能与数据库的 bigint[] 类型进行原生映射。
type UintArray []uint32

// Value 实现了 driver.Valuer 接口。
// 它告诉 GORM 如何将 UintArray ([]) 转换为数据库能够理解的格式。
func (a UintArray) Value() (driver.Value, error) {
	if a == nil {
		return "{}", nil
	}

	var b strings.Builder
	b.WriteString("{")
	for i, v := range a {
		if i > 0 {
			b.WriteString(",")
		}
		b.WriteString(strconv.FormatUint(uint64(v), 10))
	}
	b.WriteString("}")
	return b.String(), nil
}

// Scan 实现了 gorm.Scanner 接口。
// 它告诉 GORM 如何将从数据库读取的数据转换为我们的 UintArray ([])。
func (a *UintArray) Scan(src interface{}) error {
	if src == nil {
		*a = nil
		return nil
	}

	var srcStr string
	switch v := src.(type) {
	case []byte:
		srcStr = string(v)
	case string:
		srcStr = v
	default:
		return errors.New("无法将值 %v (类型 %T) 扫描为 UintArray")
	}

	// 去掉花括号
	srcStr = strings.Trim(srcStr, "{}")
	if srcStr == "" {
		*a = []uint32{}
		return nil
	}

	// 按逗号分割
	parts := strings.Split(srcStr, ",")
	arr := make([]uint32, len(parts))
	for i, p := range parts {
		val, err := strconv.ParseUint(p, 10, 64)
		if err != nil {
			return fmt.Errorf("解析 UintArray 元素失败: %w", err)
		}
		arr[i] = uint32(val)
	}

	*a = arr
	return nil
}

// SeverityLevel 定义了系统中告警、通知、日志的统一级别枚举。
// 它以中文形式存储在数据库中，提高了可读性。
type SeverityLevel string

const (
	// DebugLevel 调试级别，用于开发和诊断问题。
	DebugLevel SeverityLevel = "debug"
	// InfoLevel 信息级别，用于记录常规操作。
	InfoLevel SeverityLevel = "info"
	// WarnLevel 警告级别，表示出现潜在问题，需要关注。
	WarnLevel SeverityLevel = "warn"
	// ErrorLevel 错误级别，表示发生了需要处理的错误。
	ErrorLevel SeverityLevel = "error"
	// DPanicLevel 开发时崩溃级别，在开发模式下会触发 panic。
	DPanicLevel SeverityLevel = "dpanic"
	// PanicLevel 崩溃级别，记录日志后会立即触发 panic。
	PanicLevel SeverityLevel = "panic"
	// FatalLevel 致命级别，记录日志后会调用 os.Exit(1) 退出程序。
	FatalLevel SeverityLevel = "fatal"
)

// ToZapLevel 将我们的自定义级别转换为 zapcore.Level，以便与日志记录器兼容。
func (al SeverityLevel) ToZapLevel() zapcore.Level {
	switch al {
	case DebugLevel:
		return zapcore.DebugLevel
	case InfoLevel:
		return zapcore.InfoLevel
	case WarnLevel:
		return zapcore.WarnLevel
	case ErrorLevel:
		return zapcore.ErrorLevel
	case DPanicLevel:
		return zapcore.DPanicLevel
	case PanicLevel:
		return zapcore.PanicLevel
	case FatalLevel:
		return zapcore.FatalLevel
	default:
		// 默认情况下返回 Info 级别，保证程序健壮性
		return zapcore.InfoLevel
	}
}

// Scan 实现了 sql.Scanner 接口，GORM 在从数据库读取数据时会调用此方法。
func (al *SeverityLevel) Scan(value interface{}) error {
	bytes, ok := value.([]byte)
	if !ok {
		// 尝试处理其他可能的类型，例如字符串
		s, ok := value.(string)
		if !ok {
			return fmt.Errorf("无法将值 %v (类型 %T) 扫描为 SeverityLevel", value, value)
		}
		*al = SeverityLevel(s)
		return nil
	}
	*al = SeverityLevel(bytes)
	return nil
}

// Value 实现了 driver.Valuer 接口，GORM 在将数据写入数据库时会调用此方法。
func (al SeverityLevel) Value() (driver.Value, error) {
	return string(al), nil
}
