package repository

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

	"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"

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

// 定义仓库层可导出的公共错误
var (
	ErrUpdateWithInvalidRoot    = errors.New("更新操作的目标根计划无效或ID为0")
	ErrNewSubPlanInUpdate       = errors.New("计划树中包含一个ID为0的新子计划，更新操作只允许关联已存在的计划")
	ErrNodeDoesNotExist         = errors.New("计划树中包含一个或多个在数据库中不存在的计划")
	ErrCreateWithNonZeroID      = errors.New("创建操作的计划ID必须为0")
	ErrSubPlanIDIsZeroOnCreate  = errors.New("子计划ID为0，创建操作只允许关联已存在的计划")
	ErrMixedContent             = errors.New("计划不能同时包含任务和子计划")
	ErrDeleteWithReferencedPlan = errors.New("禁止删除正在被引用的计划")
)

// PlanTypeFilter 定义计划类型的过滤器
type PlanTypeFilter string

const (
	PlanTypeFilterAll    PlanTypeFilter = "所有任务"
	PlanTypeFilterCustom PlanTypeFilter = "自定义任务"
	PlanTypeFilterSystem PlanTypeFilter = "系统任务"
)

// ListPlansOptions 定义了查询计划时的可选参数
type ListPlansOptions struct {
	PlanType PlanTypeFilter

	// IncludeAssociations 控制是否预加载关联的任务和子计划，以便计算 HasTasks 和 HasSubPlans 字段。
	IncludeAssociations bool
}

// TaskListOptions 定义了查询任务时的可选参数
type TaskListOptions struct {
	// --- 通用筛选 ---
	Name   *string          // 按任务名称进行模糊查询
	PlanID *uint32          // 按所属的计划ID进行精确查询
	Type   *models.TaskType // 按任务类型精确查询

	// --- 关联筛选 ---
	// 用于通过 device_tasks 关联表筛选与特定设备ID关联的任务
	DeviceID *uint32

	// --- JSON 参数内筛选 (仅对特定任务类型有效) ---
	// 用于筛选 TaskTypeAreaCollectorThresholdCheck 类型的任务
	AreaControllerID *uint32
	// 用于筛选 TaskTypeDeviceThresholdCheck 和 TaskTypeAreaCollectorThresholdCheck 类型的任务
	SensorType *models.SensorType
	// 用于筛选 TaskTypeDeviceThresholdCheck 和 TaskTypeAreaCollectorThresholdCheck 类型的任务
	Level *models.SeverityLevel

	// --- 排序 ---
	OrderBy string // 例如 "id desc"
}

// PlanRepository 定义了与计划模型相关的数据库操作接口
// 这是为了让业务逻辑层依赖于抽象，而不是具体的数据库实现
type PlanRepository interface {
	// ListPlans 获取计划列表，支持过滤和分页
	ListPlans(ctx context.Context, opts ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error)
	// GetBasicPlanByID 根据ID获取计划的基本信息，不包含子计划和任务详情
	GetBasicPlanByID(ctx context.Context, id uint32) (*models.Plan, error)
	// GetPlanByID 根据ID获取计划，包含子计划和任务详情
	GetPlanByID(ctx context.Context, id uint32) (*models.Plan, error)
	// GetPlansByIDs 根据ID列表获取计划，不包含子计划和任务详情
	GetPlansByIDs(ctx context.Context, ids []uint32) ([]models.Plan, error)
	// GetSystemPlanByName 根据计划名称获取系统计划，包含子计划和任务详情
	GetSystemPlanByName(ctx context.Context, planName models.PlanName) (*models.Plan, error)
	// CreatePlan 创建一个新的计划
	CreatePlan(ctx context.Context, plan *models.Plan) error
	// CreatePlanTx 在指定事务中创建一个新的计划
	CreatePlanTx(ctx context.Context, tx *gorm.DB, plan *models.Plan) error
	// UpdatePlanMetadataAndStructure 更新计划的元数据和结构，但不包括状态等运行时信息
	UpdatePlanMetadataAndStructure(ctx context.Context, plan *models.Plan) error
	// UpdatePlan 更新计划的所有字段
	UpdatePlan(ctx context.Context, plan *models.Plan) error
	// UpdatePlanStatus 更新指定计划的状态
	UpdatePlanStatus(ctx context.Context, id uint32, status models.PlanStatus) error
	// UpdateExecuteCount 更新指定计划的执行计数
	UpdateExecuteCount(ctx context.Context, id uint32, count uint32) error
	// DeletePlan 根据ID删除计划，同时删除其关联的任务（非子任务）或子计划关联
	DeletePlan(ctx context.Context, id uint32) error
	// FlattenPlanTasks 递归展开计划，返回按执行顺序排列的所有任务列表
	FlattenPlanTasks(ctx context.Context, planID uint32) ([]models.Task, error)
	// DeleteTask 根据ID删除任务
	DeleteTask(ctx context.Context, id int) error
	// FindTaskByID 根据ID获取任务的基本信息
	FindTaskByID(ctx context.Context, id int) (*models.Task, error)
	// FindPlanAnalysisTaskByParamsPlanID 根据Parameters中的ParamsPlanID字段值查找TaskPlanAnalysis类型的Task
	FindPlanAnalysisTaskByParamsPlanID(ctx context.Context, paramsPlanID uint32) (*models.Task, error)
	// FindRunnablePlans 获取所有应执行的计划
	FindRunnablePlans(ctx context.Context) ([]*models.Plan, error)
	// FindInactivePlans 获取所有已禁用或已停止的计划
	FindInactivePlans(ctx context.Context) ([]*models.Plan, error)
	// FindPlanAnalysisTaskByPlanID 根据 PlanID 找到其关联的 'plan_analysis' 任务
	FindPlanAnalysisTaskByPlanID(ctx context.Context, planID uint32) (*models.Task, error)
	// CreatePlanAnalysisTask 创建一个 plan_analysis 类型的任务并返回它
	CreatePlanAnalysisTask(ctx context.Context, plan *models.Plan) (*models.Task, error)
	// FindPlansWithPendingTasks 查找所有正在执行的计划
	FindPlansWithPendingTasks(ctx context.Context) ([]*models.Plan, error)
	// StopPlanTransactionally 停止一个计划的执行，包括更新状态、移除待执行任务和更新执行日志
	StopPlanTransactionally(ctx context.Context, planID uint32) error
	// UpdatePlanStateAfterExecution 更新计划执行后的状态（计数和状态）
	UpdatePlanStateAfterExecution(ctx context.Context, planID uint32, newCount uint32, newStatus models.PlanStatus) error
	// ListTasksByDeviceID 根据设备ID获取关联任务列表
	ListTasksByDeviceID(ctx context.Context, deviceID uint32) ([]*models.Task, error)
	// ListTasks 支持分页和过滤的任务列表查询
	ListTasks(ctx context.Context, opts TaskListOptions, page, pageSize int) ([]models.Task, int64, error)
}

// gormPlanRepository 是 PlanRepository 的 GORM 实现
type gormPlanRepository struct {
	ctx context.Context
	db  *gorm.DB
}

// NewGormPlanRepository 创建一个新的 PlanRepository GORM 实现实例
func NewGormPlanRepository(ctx context.Context, db *gorm.DB) PlanRepository {
	return &gormPlanRepository{
		ctx: ctx,
		db:  db,
	}
}

// ListPlans 获取计划列表，支持过滤和分页
func (r *gormPlanRepository) ListPlans(ctx context.Context, opts ListPlansOptions, page, pageSize int) ([]models.Plan, int64, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "ListPlans")
	if page <= 0 || pageSize <= 0 {
		return nil, 0, ErrInvalidPagination
	}

	var plans []models.Plan
	var total int64

	query := r.db.WithContext(repoCtx).Model(&models.Plan{})

	switch opts.PlanType {
	case PlanTypeFilterCustom:
		query = query.Where("plan_type = ?", models.PlanTypeCustom)
	case PlanTypeFilterSystem:
		query = query.Where("plan_type = ?", models.PlanTypeSystem)
	case PlanTypeFilterAll:
		// 不添加 plan_type 的过滤条件
	default:
		// 默认查询自定义
		query = query.Where("plan_type = ?", models.PlanTypeCustom)
	}

	// 如果需要包含关联，则进行预加载
	if opts.IncludeAssociations {
		query = query.Preload("Tasks").Preload("SubPlans")
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	offset := (page - 1) * pageSize
	err := query.Limit(pageSize).Offset(offset).Order("id DESC").Find(&plans).Error

	return plans, total, err
}

// GetBasicPlanByID 根据ID获取计划的基本信息，不包含子计划和任务详情
func (r *gormPlanRepository) GetBasicPlanByID(ctx context.Context, id uint32) (*models.Plan, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "GetBasicPlanByID")
	var plan models.Plan
	// GORM 默认不会加载关联，除非使用 Preload，所以直接 First 即可满足要求
	result := r.db.WithContext(repoCtx).First(&plan, id)
	if result.Error != nil {
		return nil, result.Error
	}
	return &plan, nil
}

// GetPlansByIDs 根据ID列表获取计划，不包含子计划和任务详情
func (r *gormPlanRepository) GetPlansByIDs(ctx context.Context, ids []uint32) ([]models.Plan, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPlansByIDs")
	var plans []models.Plan
	if len(ids) == 0 {
		return plans, nil
	}
	err := r.db.WithContext(repoCtx).Where("id IN ?", ids).Find(&plans).Error
	if err != nil {
		return nil, err
	}
	return plans, nil
}

// GetSystemPlanByName 根据计划名称获取系统计划，包含子计划和任务详情, 系统任务不该有重名情况, 所以可以这么查询
func (r *gormPlanRepository) GetSystemPlanByName(ctx context.Context, planName models.PlanName) (*models.Plan, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "GetSystemPlanByName")
	var plan models.Plan
	// 首先只查询计划的基本信息，获取其ID
	err := r.db.WithContext(repoCtx).Select("id").Where("name = ? AND plan_type = ?", planName, models.PlanTypeSystem).First(&plan).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil // 未找到系统计划不是错误
	}
	if err != nil {
		return nil, fmt.Errorf("查询系统计划 '%s' 失败: %w", planName, err)
	}

	// 如果找到了计划ID，则复用 GetPlanByID 来获取完整的计划详情
	return r.GetPlanByID(repoCtx, plan.ID)
}

// GetPlanByID 根据ID获取计划，包含子计划和任务详情
func (r *gormPlanRepository) GetPlanByID(ctx context.Context, id uint32) (*models.Plan, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "GetPlanByID")
	var plan models.Plan

	// 先获取基本计划信息
	result := r.db.WithContext(repoCtx).First(&plan, id)
	if result.Error != nil {
		return nil, result.Error
	}

	// 根据内容类型加载关联数据
	switch plan.ContentType {
	case models.PlanContentTypeSubPlans:
		// 加载子计划引用
		var subPlans []models.SubPlan
		result = r.db.WithContext(repoCtx).Where("parent_plan_id = ?", plan.ID).Order("execution_order").Find(&subPlans)
		if result.Error != nil {
			return nil, result.Error
		}

		// 递归加载每个子计划的完整信息
		for i := range subPlans {
			childPlan, err := r.GetPlanByID(repoCtx, subPlans[i].ChildPlanID)
			if err != nil {
				return nil, err
			}
			subPlans[i].ChildPlan = childPlan
		}

		plan.SubPlans = subPlans
	case models.PlanContentTypeTasks:
		// 加载任务
		result = r.db.WithContext(repoCtx).Preload("Tasks", func(taskDB *gorm.DB) *gorm.DB {
			return taskDB.Order("execution_order")
		}).First(&plan, id)
		if result.Error != nil {
			return nil, result.Error
		}
	default:
		return nil, fmt.Errorf("未知的计划内容类型: %v; 计划ID: %v", plan.ContentType, plan.ID)
	}

	return &plan, nil
}

// CreatePlan 创建一个新的计划
func (r *gormPlanRepository) CreatePlan(ctx context.Context, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePlan")
	return r.CreatePlanTx(repoCtx, r.db, plan)
}

// CreatePlanTx 在指定事务中创建一个新的计划
func (r *gormPlanRepository) CreatePlanTx(ctx context.Context, tx *gorm.DB, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePlanTx")
	// 1. 前置校验
	if plan.ID != 0 {
		return ErrCreateWithNonZeroID
	}

	// 检查是否同时包含任务和子计划
	if len(plan.Tasks) > 0 && len(plan.SubPlans) > 0 {
		return ErrMixedContent
	}

	// 检查是否有重复的执行顺序
	if err := plan.ValidateExecutionOrder(); err != nil {
		return fmt.Errorf("计划 (ID: %d) 的执行顺序无效: %w", plan.ID, err)
	}

	// 如果是子计划类型，验证所有子计划是否存在且ID不为0
	if plan.ContentType == models.PlanContentTypeSubPlans {
		childIDsToValidate := make(map[uint32]bool)
		for _, subPlanLink := range plan.SubPlans {
			if subPlanLink.ChildPlanID == 0 {
				return ErrSubPlanIDIsZeroOnCreate
			}
			childIDsToValidate[subPlanLink.ChildPlanID] = true
		}

		var ids []uint32
		for id := range childIDsToValidate {
			ids = append(ids, id)
		}

		if len(ids) > 0 {
			var count int64
			if err := tx.WithContext(repoCtx).Model(&models.Plan{}).Where("id IN ?", ids).Count(&count).Error; err != nil {
				return fmt.Errorf("验证子计划存在性失败: %w", err)
			}
			if int(count) != len(ids) {
				return ErrNodeDoesNotExist
			}
		}
	}

	// 2. 创建根计划
	// GORM 会自动处理关联的 Tasks (如果 ContentType 是 tasks 且 Task.ID 为 0),
	// 以及 Tasks 内部已经填充好的 Devices 关联。
	if err := tx.WithContext(repoCtx).Create(plan).Error; err != nil {
		return err
	}

	// 3. 创建触发器Task
	// 关键修改：调用 createPlanAnalysisTask 并处理其返回的 Task 对象
	_, err := r.createPlanAnalysisTask(repoCtx, tx, plan)
	if err != nil {
		return err
	}
	return nil
}

// UpdatePlan 更新计划
func (r *gormPlanRepository) UpdatePlan(ctx context.Context, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlan")
	return r.db.WithContext(repoCtx).Save(plan).Error
}

// UpdatePlanMetadataAndStructure 是更新计划元数据和结构的公共入口点
func (r *gormPlanRepository) UpdatePlanMetadataAndStructure(ctx context.Context, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanMetadataAndStructure")
	return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
		return r.updatePlanMetadataAndStructureTx(repoCtx, tx, plan)
	})
}

// updatePlanMetadataAndStructureTx 在事务中协调整个更新过程
func (r *gormPlanRepository) updatePlanMetadataAndStructureTx(ctx context.Context, tx *gorm.DB, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "updatePlanMetadataAndStructureTx")
	if err := r.validatePlanTree(repoCtx, tx, plan); err != nil {
		return err
	}
	if err := r.reconcilePlanNode(repoCtx, tx, plan); err != nil {
		return err
	}

	// 更新Plan触发器
	return r.updatePlanAnalysisTask(repoCtx, tx, plan)
}

// validatePlanTree 对整个计划树进行全面的只读健康检查
func (r *gormPlanRepository) validatePlanTree(ctx context.Context, tx *gorm.DB, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "validatePlanTree")
	// 1. 检查根节点
	if plan == nil || plan.ID == 0 {
		return ErrUpdateWithInvalidRoot
	}
	if len(plan.Tasks) > 0 && len(plan.SubPlans) > 0 {
		return fmt.Errorf("计划 (ID: %d) 不能同时包含任务和子计划", plan.ID)
	}

	// 2. 递归验证所有子节点，并检测循环引用
	allIDs := make(map[uint32]bool)
	recursionStack := make(map[uint32]bool)
	if err := validateNodeAndDetectCycles(plan, allIDs, recursionStack); err != nil {
		return err
	}

	// 3. 检查是否有重复的执行顺序
	if err := plan.ValidateExecutionOrder(); err != nil {
		return fmt.Errorf("计划 (ID: %d) 的执行顺序无效: %w", plan.ID, err)
	}

	// 4. 一次性数据库存在性校验
	var idsToCheck []uint32
	for id := range allIDs {
		idsToCheck = append(idsToCheck, id)
	}

	if len(idsToCheck) > 0 {
		var count int64
		if err := tx.WithContext(repoCtx).Model(&models.Plan{}).Where("id IN ?", idsToCheck).Count(&count).Error; err != nil {
			return fmt.Errorf("检查计划存在性时出错: %w", err)
		}
		if int(count) != len(idsToCheck) {
			return ErrNodeDoesNotExist
		}
	}
	return nil
}

// validateNodeAndDetectCycles 递归地验证节点有效性并检测循环引用
func validateNodeAndDetectCycles(plan *models.Plan, allIDs, recursionStack map[uint32]bool) error {
	if plan == nil {
		return nil
	}
	if plan.ID == 0 {
		return ErrNewSubPlanInUpdate
	}
	if recursionStack[plan.ID] {
		return fmt.Errorf("检测到循环引用：计划 (ID: %d) 是其自身的祖先", plan.ID)
	}
	if allIDs[plan.ID] {
		return nil // 已经验证过这个节点及其子树，无需重复
	}

	recursionStack[plan.ID] = true
	allIDs[plan.ID] = true

	for _, subPlanLink := range plan.SubPlans {
		if err := validateNodeAndDetectCycles(subPlanLink.ChildPlan, allIDs, recursionStack); err != nil {
			return err
		}
	}

	delete(recursionStack, plan.ID) // 回溯，将节点移出当前递归路径
	return nil
}

// reconcilePlanNode 递归地同步数据库状态以匹配给定的计划节点
func (r *gormPlanRepository) reconcilePlanNode(ctx context.Context, tx *gorm.DB, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "reconcilePlanNode")
	if plan == nil {
		return nil
	}
	// 1. 更新节点本身的基础字段
	if err := tx.WithContext(repoCtx).Model(plan).Select("Name", "Description", "ExecutionType", "ExecuteNum", "CronExpression", "ContentType").Updates(plan).Error; err != nil {
		return err
	}

	// 2. 根据内容类型协调子项
	switch plan.ContentType {
	case models.PlanContentTypeTasks:
		// 清理旧的子计划关联
		if err := tx.WithContext(repoCtx).Where("parent_plan_id = ?", plan.ID).Delete(&models.SubPlan{}).Error; err != nil {
			return fmt.Errorf("更新时清理旧的子计划关联失败: %w", err)
		}
		// 协调任务列表
		return r.reconcileTasks(repoCtx, tx, plan)
	case models.PlanContentTypeSubPlans:
		// 清理旧的任务
		if err := tx.WithContext(repoCtx).Where("plan_id = ?", plan.ID).Delete(&models.Task{}).Error; err != nil {
			return fmt.Errorf("更新时清理旧的任务失败: %w", err)
		}
		// 协调子计划关联
		return r.reconcileSubPlans(repoCtx, tx, plan)
	}
	return nil
}

// reconcileTasks 精确同步任务列表
func (r *gormPlanRepository) reconcileTasks(ctx context.Context, tx *gorm.DB, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "reconcileTasks")
	var existingTasks []models.Task
	if err := tx.WithContext(repoCtx).Where("plan_id = ?", plan.ID).Find(&existingTasks).Error; err != nil {
		return err
	}

	existingTaskMap := make(map[int]bool)
	for _, task := range existingTasks {
		existingTaskMap[task.ID] = true
	}

	for i := range plan.Tasks {
		task := &plan.Tasks[i]
		if task.ID == 0 {
			task.PlanID = plan.ID
			if err := tx.WithContext(repoCtx).Create(task).Error; err != nil {
				return err
			}
		} else {
			delete(existingTaskMap, task.ID) // 从待删除map中移除
			if err := tx.WithContext(repoCtx).Model(task).Updates(task).Error; err != nil {
				return err
			}
		}
	}

	var tasksToDelete []int
	for id := range existingTaskMap {
		tasksToDelete = append(tasksToDelete, id)
	}

	if len(tasksToDelete) > 0 {
		return r.deleteTasksTx(repoCtx, tx, tasksToDelete)
	}
	return nil
}

// reconcileSubPlans 精确同步子计划关联并递归
func (r *gormPlanRepository) reconcileSubPlans(ctx context.Context, tx *gorm.DB, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "reconcileSubPlans")
	var existingLinks []models.SubPlan
	if err := tx.WithContext(repoCtx).Where("parent_plan_id = ?", plan.ID).Find(&existingLinks).Error; err != nil {
		return err
	}

	existingLinkMap := make(map[uint32]bool)
	for _, link := range existingLinks {
		existingLinkMap[link.ID] = true
	}

	for i := range plan.SubPlans {
		link := &plan.SubPlans[i]
		link.ParentPlanID = plan.ID
		if link.ID == 0 {
			if err := tx.WithContext(repoCtx).Create(link).Error; err != nil {
				return err
			}
		} else {
			delete(existingLinkMap, link.ID) // 从待删除map中移除
			if err := tx.WithContext(repoCtx).Model(link).Updates(link).Error; err != nil {
				return err
			}
		}
	}

	var linksToDelete []uint32
	for id := range existingLinkMap {
		linksToDelete = append(linksToDelete, id)
	}

	if len(linksToDelete) > 0 {
		if err := tx.WithContext(repoCtx).Delete(&models.SubPlan{}, linksToDelete).Error; err != nil {
			return err
		}
	}
	return nil
}

// DeletePlan 根据ID删除计划，同时删除其关联的任务（非子任务）或子计划关联
func (r *gormPlanRepository) DeletePlan(ctx context.Context, id uint32) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "DeletePlan")
	return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
		// 1. 检查该计划是否是其他计划的子计划
		var count int64
		if err := tx.WithContext(repoCtx).Model(&models.SubPlan{}).Where("child_plan_id = ?", id).Count(&count).Error; err != nil {
			return fmt.Errorf("检查计划是否为子计划失败: %w", err)
		}
		if count > 0 {
			return ErrDeleteWithReferencedPlan
		}

		var plan models.Plan
		// 2. 获取计划以确定其内容类型
		if err := tx.WithContext(repoCtx).First(&plan, id).Error; err != nil {
			return err
		}

		// 3. 根据内容类型删除关联数据
		switch plan.ContentType {
		case models.PlanContentTypeTasks:
			// 删除与此计划关联的所有非子任务
			if err := tx.WithContext(repoCtx).Where("plan_id = ?", id).Delete(&models.Task{}).Error; err != nil {
				return fmt.Errorf("删除计划ID %d 的任务失败: %w", id, err)
			}
		case models.PlanContentTypeSubPlans:
			// 删除与此计划关联的所有子计划链接
			if err := tx.WithContext(repoCtx).Where("parent_plan_id = ?", id).Delete(&models.SubPlan{}).Error; err != nil {
				return fmt.Errorf("删除计划ID %d 的子计划关联失败: %w", id, err)
			}
		}

		// 4. 删除计划本身
		if err := tx.WithContext(repoCtx).Delete(&models.Plan{}, id).Error; err != nil {
			return fmt.Errorf("删除计划ID %d 失败: %w", id, err)
		}

		return nil
	})
}

// FlattenPlanTasks 递归展开计划，返回按执行顺序排列的所有任务列表
func (r *gormPlanRepository) FlattenPlanTasks(ctx context.Context, planID uint32) ([]models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "FlattenPlanTasks")
	plan, err := r.GetPlanByID(repoCtx, planID)
	if err != nil {
		return nil, fmt.Errorf("获取计划(ID: %d)失败: %w", planID, err)
	}

	return r.flattenPlanTasksRecursive(repoCtx, plan)
}

// flattenPlanTasksRecursive 递归展开计划的内部实现
func (r *gormPlanRepository) flattenPlanTasksRecursive(ctx context.Context, plan *models.Plan) ([]models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "flattenPlanTasksRecursive")
	var tasks []models.Task

	switch plan.ContentType {
	case models.PlanContentTypeTasks:
		// 如果计划直接包含任务，直接返回这些任务
		// 由于GetPlanByID已经预加载并排序了任务，这里直接使用即可
		tasks = append(tasks, plan.Tasks...)

	case models.PlanContentTypeSubPlans:
		// 如果计划包含子计划，则递归处理每个子计划
		for _, subPlan := range plan.SubPlans {
			// 获取子计划的任务列表
			var subTasks []models.Task
			var err error

			// 确保子计划已经被加载
			if subPlan.ChildPlan != nil {
				subTasks, err = r.flattenPlanTasksRecursive(repoCtx, subPlan.ChildPlan)
			} else {
				// 如果子计划未加载，则从数据库获取并递归展开
				subTasks, err = r.FlattenPlanTasks(repoCtx, subPlan.ChildPlanID)
			}

			if err != nil {
				return nil, fmt.Errorf("展开子计划(ID: %d)失败: %w", subPlan.ChildPlanID, err)
			}

			// 将子计划的任务添加到结果中
			tasks = append(tasks, subTasks...)
		}
	default:
		return nil, fmt.Errorf("未知的计划内容类型: %v", plan.ContentType)
	}

	return tasks, nil
}

// DeleteTask 根据ID删除任务
func (r *gormPlanRepository) DeleteTask(ctx context.Context, id int) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteTask")
	// 使用事务确保操作的原子性
	return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
		return r.deleteTasksTx(repoCtx, tx, []int{id})
	})
}

// deleteTasksTx 在事务中批量软删除任务，并物理删除其在关联表中的记录
func (r *gormPlanRepository) deleteTasksTx(ctx context.Context, tx *gorm.DB, ids []int) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "deleteTasksTx")
	if len(ids) == 0 {
		return nil
	}

	// 检查是否有待执行任务引用了这些任务
	var pendingTaskCount int64
	if err := tx.WithContext(repoCtx).Model(&models.PendingTask{}).Where("task_id IN ?", ids).Count(&pendingTaskCount).Error; err != nil {
		return fmt.Errorf("检查待执行任务时出错: %w", err)
	}

	// 如果有待执行任务引用该任务，不能删除
	if pendingTaskCount > 0 {
		return fmt.Errorf("无法删除任务，因为存在 %d 条待执行任务引用这些任务", pendingTaskCount)
	}

	// 因为钩子函数在批量删除中不会被触发, 所以手动删除关联表, 通过批量删除语句优化性能

	// 1. 直接、高效地从关联表中物理删除所有相关记录
	// 这是最关键的优化，避免了不必要的查询和循环
	if err := tx.WithContext(repoCtx).Where("task_id IN ?", ids).Unscoped().Delete(&models.DeviceTask{}).Error; err != nil {
		return fmt.Errorf("清理任务的设备关联失败: %w", err)
	}

	// 2. 对任务本身进行软删除
	result := tx.WithContext(repoCtx).Delete(&models.Task{}, ids)
	if result.Error != nil {
		return result.Error
	}

	// 3. 如果是单个删除且未找到记录，则返回错误
	if len(ids) == 1 && result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}

	return nil
}

// FindPlanAnalysisTaskByParamsPlanID 根据Parameters中的ParamsPlanID字段值查找TaskPlanAnalysis类型的Task
func (r *gormPlanRepository) FindPlanAnalysisTaskByParamsPlanID(ctx context.Context, paramsPlanID uint32) (*models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPlanAnalysisTaskByParamsPlanID")
	return r.findPlanAnalysisTask(repoCtx, r.db, paramsPlanID)
}

// createPlanAnalysisTask 用于创建一个TaskPlanAnalysis类型的Task
// 关键修改：Task.PlanID 设置为 0，实际 PlanID 存储在 Parameters 中，并返回创建的 Task
func (r *gormPlanRepository) createPlanAnalysisTask(ctx context.Context, tx *gorm.DB, plan *models.Plan) (*models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "createPlanAnalysisTask")
	m := map[string]interface{}{
		models.ParamsPlanID: plan.ID,
	}
	parameters, err := json.Marshal(m)
	if err != nil {
		return nil, err
	}

	task := &models.Task{
		PlanID:         0, // 关键：设置为 0，避免被常规 PlanID 查询查到
		Name:           fmt.Sprintf("'%s'计划触发器", plan.Name),
		Description:    fmt.Sprintf("计划名: %s, 计划ID: %d", plan.Name, plan.ID),
		ExecutionOrder: 0, // 触发器任务的执行顺序通常为0或不关心
		Type:           models.TaskPlanAnalysis,
		Parameters:     datatypes.JSON(parameters),
	}

	if err := tx.WithContext(repoCtx).Create(task).Error; err != nil {
		return nil, err
	}
	return task, nil
}

// updatePlanAnalysisTask 使用更安全的方式更新触发器任务
func (r *gormPlanRepository) updatePlanAnalysisTask(ctx context.Context, tx *gorm.DB, plan *models.Plan) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "updatePlanAnalysisTask")
	task, err := r.findPlanAnalysisTask(repoCtx, tx, plan.ID)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return fmt.Errorf("查找现有计划分析任务失败: %w", err)
	}

	// 如果触发器任务不存在，则创建一个
	if task == nil {
		_, err := r.createPlanAnalysisTask(repoCtx, tx, plan)
		return err
	}

	// 如果存在，则更新它的名称和描述以反映计划的最新信息
	task.Name = fmt.Sprintf("'%s'计划触发器", plan.Name)
	task.Description = fmt.Sprintf("计划名: %s, 计划ID: %d", plan.Name, plan.ID)

	return tx.WithContext(repoCtx).Save(task).Error
}

func (r *gormPlanRepository) FindRunnablePlans(ctx context.Context) ([]*models.Plan, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "FindRunnablePlans")
	var plans []*models.Plan
	err := r.db.WithContext(repoCtx).
		Where("status = ?", models.PlanStatusEnabled).
		Where(
			r.db.WithContext(repoCtx).Where("execution_type = ?", models.PlanExecutionTypeManual).
				Or("execution_type = ? AND (execute_num = 0 OR execute_count < execute_num)", models.PlanExecutionTypeAutomatic),
		).
		Find(&plans).Error
	return plans, err
}

func (r *gormPlanRepository) FindInactivePlans(ctx context.Context) ([]*models.Plan, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "FindInactivePlans")
	var plans []*models.Plan
	err := r.db.WithContext(repoCtx).
		Where("status != ?", models.PlanStatusEnabled).
		Find(&plans).Error
	return plans, err
}

// findPlanAnalysisTask 是一个内部使用的、更高效的查找方法
// 关键修改：通过查询 parameters JSON 字段来查找
func (r *gormPlanRepository) findPlanAnalysisTask(ctx context.Context, tx *gorm.DB, planID uint32) (*models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "findPlanAnalysisTask")
	var task models.Task
	err := tx.WithContext(repoCtx).Where("type = ? AND parameters->>'plan_id' = ?", models.TaskPlanAnalysis, fmt.Sprintf("%d", planID)).First(&task).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil // 未找到不是错误，返回nil, nil
	}
	return &task, err
}

// FindPlanAnalysisTaskByPlanID 是暴露给外部的公共方法
// 关键修改：通过查询 parameters JSON 字段来查找
func (r *gormPlanRepository) FindPlanAnalysisTaskByPlanID(ctx context.Context, planID uint32) (*models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPlanAnalysisTaskByPlanID")
	return r.findPlanAnalysisTask(repoCtx, r.db, planID)
}

// CreatePlanAnalysisTask 创建一个 plan_analysis 类型的任务并返回它
// 这个方法是公开的，主要由 TaskManager 在发现触发器任务定义丢失时调用。
func (r *gormPlanRepository) CreatePlanAnalysisTask(ctx context.Context, plan *models.Plan) (*models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "CreatePlanAnalysisTask")
	var createdTask *models.Task
	err := r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
		var err error
		createdTask, err = r.createPlanAnalysisTask(repoCtx, tx, plan)
		return err
	})
	return createdTask, err
}

// FindPlansWithPendingTasks 查找所有正在执行的计划
func (r *gormPlanRepository) FindPlansWithPendingTasks(ctx context.Context) ([]*models.Plan, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "FindPlansWithPendingTasks")
	var plans []*models.Plan

	// 关联 pending_tasks, task_execution_logs, tasks 表来查找符合条件的计划
	err := r.db.WithContext(repoCtx).Table("plans").
		Joins("JOIN tasks ON plans.id = tasks.plan_id").
		Joins("JOIN task_execution_logs ON tasks.id = task_execution_logs.task_id").
		Joins("JOIN pending_tasks ON task_execution_logs.id = pending_tasks.task_execution_log_id").
		Group("plans.id"). // 避免重复，因为一个计划可能有多个待执行任务
		Find(&plans).Error

	return plans, err
}

// StopPlanTransactionally 停止一个计划的执行，包括更新状态、移除待执行任务和更新执行日志。
func (r *gormPlanRepository) StopPlanTransactionally(ctx context.Context, planID uint32) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "StopPlanTransactionally")
	return r.db.WithContext(repoCtx).Transaction(func(tx *gorm.DB) error {
		// 使用事务创建新的仓库实例，确保所有操作都在同一个事务中
		planRepoTx := NewGormPlanRepository(repoCtx, tx)
		executionLogRepoTx := NewGormExecutionLogRepository(repoCtx, tx)
		pendingTaskRepoTx := NewGormPendingTaskRepository(repoCtx, tx)

		// 1. 更新计划状态为“已停止”
		if err := planRepoTx.UpdatePlanStatus(repoCtx, planID, models.PlanStatusDisabled); err != nil {
			return fmt.Errorf("更新计划 #%d 状态为 '已停止' 失败: %w", planID, err)
		}

		// 2. 查找当前正在进行的计划执行日志
		planLog, err := executionLogRepoTx.FindInProgressPlanExecutionLogByPlanID(repoCtx, planID)
		if err != nil {
			return fmt.Errorf("查找计划 #%d 正在进行的执行日志失败: %w", planID, err)
		}

		if planLog == nil {
			// 没有正在进行的执行，直接返回成功
			return nil
		}

		// 3. 查找所有需要被取消的任务执行日志
		taskLogs, err := executionLogRepoTx.FindIncompleteTaskExecutionLogsByPlanLogID(repoCtx, planLog.ID)
		if err != nil {
			return fmt.Errorf("查找计划执行日志 #%d 下未完成的任务日志失败: %w", planLog.ID, err)
		}

		if len(taskLogs) > 0 {
			var taskLogIDs []uint32
			for _, tl := range taskLogs {
				taskLogIDs = append(taskLogIDs, tl.ID)
			}

			// 3.1 批量更新任务执行日志状态为“已取消”
			if err := executionLogRepoTx.UpdateTaskExecutionLogStatusByIDs(repoCtx, taskLogIDs, models.ExecutionStatusCancelled); err != nil {
				return fmt.Errorf("批量更新任务执行日志状态为 '已取消' 失败: %w", err)
			}

			// 3.2 查找并删除待执行队列中对应的任务
			pendingTasks, err := pendingTaskRepoTx.FindPendingTasksByTaskLogIDs(repoCtx, taskLogIDs)
			if err != nil {
				return fmt.Errorf("查找计划执行日志 #%d 下对应的待执行任务失败: %w", planLog.ID, err)
			}

			if len(pendingTasks) > 0 {
				var pendingTaskIDs []uint32
				for _, pt := range pendingTasks {
					pendingTaskIDs = append(pendingTaskIDs, pt.ID)
				}
				if err := pendingTaskRepoTx.DeletePendingTasksByIDs(repoCtx, pendingTaskIDs); err != nil {
					return fmt.Errorf("批量删除待执行任务失败: %w", err)
				}
			}
		}

		// 4. 更新计划执行历史的总状态为“失败”
		if err := executionLogRepoTx.UpdatePlanExecutionLogStatus(repoCtx, planLog.ID, models.ExecutionStatusFailed); err != nil {
			return fmt.Errorf("更新计划执行日志 #%d 状态为 '失败' 失败: %w", planLog.ID, err)
		}

		return nil
	})
}

// UpdatePlanStatus 更新指定计划的状态
func (r *gormPlanRepository) UpdatePlanStatus(ctx context.Context, id uint32, status models.PlanStatus) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanStatus")
	result := r.db.WithContext(repoCtx).Model(&models.Plan{}).Where("id = ?", id).Update("status", status)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}

func (r *gormPlanRepository) UpdatePlanStateAfterExecution(ctx context.Context, planID uint32, newCount uint32, newStatus models.PlanStatus) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdatePlanStateAfterExecution")
	return r.db.WithContext(repoCtx).Model(&models.Plan{}).Where("id = ?", planID).Updates(map[string]interface{}{
		"execute_count": newCount,
		"status":        newStatus,
	}).Error
}

// UpdateExecuteCount 更新指定计划的执行计数
func (r *gormPlanRepository) UpdateExecuteCount(ctx context.Context, id uint32, count uint32) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateExecuteCount")
	result := r.db.WithContext(repoCtx).Model(&models.Plan{}).Where("id = ?", id).Update("execute_count", count)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}
	return nil
}

// FindTaskByID 根据ID获取任务的基本信息
func (r *gormPlanRepository) FindTaskByID(ctx context.Context, id int) (*models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "FindTaskByID")
	var task models.Task
	result := r.db.WithContext(repoCtx).First(&task, id)
	if result.Error != nil {
		return nil, result.Error
	}
	return &task, nil
}

func (r *gormPlanRepository) ListTasksByDeviceID(ctx context.Context, deviceID uint32) ([]*models.Task, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "ListTasksByDeviceID")
	tasks := []*models.Task{}
	// 使用 Joins 方法来连接 tasks 表和 device_tasks 关联表，
	// 然后通过 Where 子句筛选出与指定 deviceID 关联的所有任务。
	err := r.db.WithContext(repoCtx).Joins("JOIN device_tasks ON device_tasks.task_id = tasks.id").
		Where("device_tasks.device_id = ?", deviceID).
		Find(&tasks).Error

	if err != nil {
		return nil, err
	}

	return tasks, nil
}

// ListTasks 实现了分页和过滤查询任务的功能，并优化了 JOIN 后的计数逻辑
func (r *gormPlanRepository) ListTasks(ctx context.Context, opts TaskListOptions, page, pageSize int) ([]models.Task, int64, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "ListTasks")
	if page <= 0 || pageSize <= 0 {
		return nil, 0, ErrInvalidPagination
	}

	var results []models.Task
	var total int64

	// 1. 构建基础查询，应用所有筛选条件
	query := r.db.WithContext(repoCtx).Model(&models.Task{})

	if opts.Name != nil && *opts.Name != "" {
		query = query.Where("tasks.name LIKE ?", "%"+*opts.Name+"%")
	}
	if opts.PlanID != nil {
		query = query.Where("tasks.plan_id = ?", *opts.PlanID)
	}
	if opts.Type != nil {
		query = query.Where("tasks.type = ?", *opts.Type)
	}

	// --- JSON 字段查询 ---
	if opts.AreaControllerID != nil {
		// 使用 ->> 操作符查询 JSON 字段的值（作为文本）
		query = query.Where("parameters->>'area_controller_id' = ?", *opts.AreaControllerID)
	}
	if opts.SensorType != nil {
		query = query.Where("parameters->>'sensor_type' = ?", *opts.SensorType)
	}
	if opts.Level != nil {
		query = query.Where("parameters->>'level' = ?", *opts.Level)
	}
	// --- 结束 ---

	if opts.DeviceID != nil {
		query = query.Joins("JOIN device_tasks ON device_tasks.task_id = tasks.id").Where("device_tasks.device_id = ?", *opts.DeviceID)
	}

	// 2. 执行计数查询
	countQuery := query
	if opts.DeviceID != nil {
		if err := countQuery.Distinct("tasks.id").Count(&total).Error; err != nil {
			return nil, 0, err
		}
	} else {
		if err := countQuery.Count(&total).Error; err != nil {
			return nil, 0, err
		}
	}

	if total == 0 {
		return []models.Task{}, 0, nil
	}

	// 3. 为数据查询应用排序、分页和预加载
	orderBy := "tasks.id DESC"
	if opts.OrderBy != "" {
		orderBy = opts.OrderBy
	}

	err := query.Order(orderBy).
		Limit(pageSize).
		Offset((page - 1) * pageSize).
		Preload("Devices").
		Find(&results).Error

	return results, total, err
}
