package models

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

	"gorm.io/datatypes"
	"gorm.io/gorm"
)

type PlanName string

const (
	// PlanNamePeriodicSystemHealthCheck 是周期性系统健康检查计划的名称
	PlanNamePeriodicSystemHealthCheck PlanName = "周期性系统健康检查"
	// PlanNameAlarmNotification 是告警通知发送计划的名称
	PlanNameAlarmNotification PlanName = "告警通知发送"
)

// PlanExecutionType 定义了计划的执行类型
type PlanExecutionType string

const (
	PlanExecutionTypeAutomatic PlanExecutionType = "自动" // 自动执行 (包含定时和循环)
	PlanExecutionTypeManual    PlanExecutionType = "手动" // 手动执行
)

// PlanContentType 定义了计划包含的内容类型
type PlanContentType string

const (
	PlanContentTypeSubPlans PlanContentType = "子计划" // 计划包含子计划
	PlanContentTypeTasks    PlanContentType = "任务"  // 计划包含任务
)

// TaskType 定义了任务的类型，每个类型可以对应 task 包中的一个具体动作
type TaskType string

const (
	TaskPlanAnalysis                    TaskType = "计划分析"   // 解析Plan的Task列表并添加到待执行队列的特殊任务
	TaskTypeWaiting                     TaskType = "等待"     // 等待任务
	TaskTypeReleaseFeedWeight           TaskType = "下料"     // 下料口释放指定重量任务
	TaskTypeFullCollection              TaskType = "全量采集"   // 新增的全量采集任务
	TaskTypeAlarmNotification           TaskType = "告警通知"   // 告警通知任务
	TaskTypeNotificationRefresh         TaskType = "通知刷新"   // 通知刷新任务
	TaskTypeDeviceThresholdCheck        TaskType = "设备阈值检查" // 设备阈值检查任务
	TaskTypeAreaCollectorThresholdCheck TaskType = "区域阈值检查" // 区域阈值检查任务
)

// -- Task Parameters --
const (
	// 这个参数是 TaskPlanAnalysis 类型的 Task Parameters 中用于记录plan_id的字段的key
	ParamsPlanID = "plan_id"
)

// PlanStatus 定义了计划的状态
type PlanStatus string

const (
	PlanStatusDisabled PlanStatus = "已禁用"  // 禁用计划
	PlanStatusEnabled  PlanStatus = "已启用"  // 启用计划
	PlanStatusStopped  PlanStatus = "执行完毕" // 执行完毕
	PlanStatusFailed   PlanStatus = "执行失败" // 执行失败
)

type PlanType string

const (
	PlanTypeCustom PlanType = "自定义任务"
	PlanTypeSystem PlanType = "系统任务"
)

// Plan 代表系统中的一个计划，可以包含子计划或任务
type Plan struct {
	Model

	Name          PlanName          `gorm:"not null" json:"name"`
	Description   string            `json:"description"`
	PlanType      PlanType          `gorm:"not null;index" json:"plan_type"` // 任务类型, 包括系统任务和用户自定义任务
	ExecutionType PlanExecutionType `gorm:"not null;index" json:"execution_type"`
	Status        PlanStatus        `gorm:"default:'已禁用';index" json:"status"` // 计划是否被启动
	ExecuteNum    uint32            `gorm:"default:0" json:"execute_num"`      // 计划预期执行次数
	ExecuteCount  uint32            `gorm:"default:0" json:"execute_count"`    // 执行计数器

	// 针对 PlanExecutionTypeAutomatic，使用 Cron 表达式定义调度规则
	CronExpression string `json:"cron_expression"`

	// ContentType 标识此计划是包含子计划还是任务。
	// 应用程序层应确保只设置其中一种类型的内容。
	ContentType PlanContentType `gorm:"not null" json:"content_type"`

	// SubPlans 直接与此计划关联的子计划。仅当 ContentType 为 PlanContentTypeSubPlans 时有效。
	SubPlans []SubPlan `gorm:"foreignKey:ParentPlanID" json:"sub_plans"`
	// Tasks 直接与此计划关联的任务。仅当 ContentType 为 PlanContentTypeTasks 时有效。
	Tasks []Task `gorm:"foreignKey:PlanID;constraint:OnDelete:CASCADE;" json:"tasks"`
}

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

// ValidateExecutionOrder 校验计划中的步骤或子计划顺序不能有重复的
func (p Plan) ValidateExecutionOrder() error {
	orderMap := make(map[int]bool)

	switch p.ContentType {
	case PlanContentTypeTasks:
		for _, task := range p.Tasks {
			if orderMap[task.ExecutionOrder] {
				return fmt.Errorf("任务执行顺序重复: %d", task.ExecutionOrder)
			}
			orderMap[task.ExecutionOrder] = true
		}
	case PlanContentTypeSubPlans:
		for _, subPlan := range p.SubPlans {
			if orderMap[subPlan.ExecutionOrder] {
				return fmt.Errorf("子计划执行顺序重复: %d", subPlan.ExecutionOrder)
			}
			orderMap[subPlan.ExecutionOrder] = true
		}
	}
	return nil
}

// ReorderSteps 重新排序计划中的步骤（任务或子计划），使其 ExecutionOrder 从 1 开始且连续。
// 这个方法假设重复的顺序已经被其他方法验证过，它只负责修复断层和不从1开始的序列。
func (p *Plan) ReorderSteps() {
	switch p.ContentType {
	case PlanContentTypeTasks:
		if len(p.Tasks) == 0 {
			return
		}

		// 1. 按当前的 ExecutionOrder 对任务进行排序
		sort.Slice(p.Tasks, func(i, j int) bool {
			return p.Tasks[i].ExecutionOrder < p.Tasks[j].ExecutionOrder
		})

		// 2. 重新分配连续的 ExecutionOrder
		for i := range p.Tasks {
			p.Tasks[i].ExecutionOrder = i + 1
		}

	case PlanContentTypeSubPlans:
		if len(p.SubPlans) == 0 {
			return
		}

		// 1. 按当前的 ExecutionOrder 对子计划进行排序
		sort.Slice(p.SubPlans, func(i, j int) bool {
			return p.SubPlans[i].ExecutionOrder < p.SubPlans[j].ExecutionOrder
		})

		// 2. 重新分配连续的 ExecutionOrder
		for i := range p.SubPlans {
			p.SubPlans[i].ExecutionOrder = i + 1
		}
	}
}

// SubPlan 代表作为另一个计划一部分的子计划，具有执行顺序
type SubPlan struct {
	Model

	ParentPlanID   uint32 `gorm:"not null;index" json:"parent_plan_id"` // 父计划的ID
	ChildPlanID    uint32 `gorm:"not null;index" json:"child_plan_id"`  // 子计划的ID (它本身也是一个 Plan)
	ExecutionOrder int    `gorm:"not null" json:"execution_order"`      // 在父计划中的执行顺序
	ChildPlan      *Plan  `gorm:"-" json:"child_plan"`                  // 完整子计划数据，仅内存中
}

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

// Task 代表计划中的一个任务，具有执行顺序
type Task struct {
	// 手动定义字段以将 ID 类型设置为 int，以匹配 TaskExecutionLog 中的 TaskID
	ID        int `gorm:"primarykey"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt gorm.DeletedAt `gorm:"index"` // 保持软删除功能

	PlanID         uint32         `gorm:"not null;index" json:"plan_id"` // 此任务所属计划的ID
	Name           string         `gorm:"not null" json:"name"`
	Description    string         `json:"description"`
	ExecutionOrder int            `gorm:"not null" json:"execution_order"` // 在计划中的执行顺序
	Type           TaskType       `gorm:"not null" json:"type"`            // 任务的类型，对应 task 包中的具体动作
	Parameters     datatypes.JSON `json:"parameters"`                      // 任务特定参数的JSON (例如: 设备ID, 值)

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

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

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

// SaveParameters 将一个结构体序列化为 JSON 并保存到 Task 的 Parameters 字段。
// 示例：
//
//	params := LoraParameters{...}
//	if err := task.SaveParameters(params); err != nil { ... }
func (t *Task) SaveParameters(v interface{}) error {
	data, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("序列化任务参数失败: %w", err)
	}
	t.Parameters = data
	return nil
}

// DeviceTask 是设备和任务之间的关联模型，表示一个设备可以执行多个任务，一个任务可以被多个设备执行。
type DeviceTask struct {
	Model
	DeviceID uint32 `gorm:"not null;index"` // 设备ID
	TaskID   uint32 `gorm:"not null;index"` // 任务ID

	// 可选：如果需要存储关联的额外信息，可以在这里添加字段，例如：
	// Configuration datatypes.JSON `json:"configuration"` // 任务在特定设备上的配置
}

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