package repository

import (
	"context"
	"errors"
	"fmt"
	"time"

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

	"gorm.io/gorm"
)

// ActiveAlarmListOptions 定义了查询活跃告警列表时的可选参数
type ActiveAlarmListOptions struct {
	SourceType  *models.AlarmSourceType // 按告警来源类型过滤
	SourceID    *uint32                 // 按告警来源ID过滤
	Level       *models.SeverityLevel   // 按告警严重性等级过滤
	IsIgnored   *bool                   // 按是否被忽略过滤
	TriggerTime *time.Time              // 告警触发时间范围 - 开始时间
	EndTime     *time.Time              // 告警触发时间范围 - 结束时间
	OrderBy     string                  // 排序字段，例如 "trigger_time DESC"
}

// HistoricalAlarmListOptions 定义了查询历史告警列表时的可选参数
type HistoricalAlarmListOptions struct {
	SourceType       *models.AlarmSourceType // 按告警来源类型过滤
	SourceID         *uint32                 // 按告警来源ID过滤
	Level            *models.SeverityLevel   // 按告警严重性等级过滤
	TriggerTimeStart *time.Time              // 告警触发时间范围 - 开始时间
	TriggerTimeEnd   *time.Time              // 告警触发时间范围 - 结束时间
	ResolveTimeStart *time.Time              // 告警解决时间范围 - 开始时间 (对应 models.HistoricalAlarm.ResolveTime)
	ResolveTimeEnd   *time.Time              // 告警解决时间范围 - 结束时间 (对应 models.HistoricalAlarm.ResolveTime)
	OrderBy          string                  // 排序字段，例如 "trigger_time DESC"
}

// AlarmRepository 定义了对告警模型的数据库操作接口
type AlarmRepository interface {
	// CreateActiveAlarm 创建一条新的活跃告警记录
	CreateActiveAlarm(ctx context.Context, alarm *models.ActiveAlarm) error

	// IsAlarmActiveInUse 检查具有相同来源和告警代码的告警当前是否处于活跃表中
	IsAlarmActiveInUse(ctx context.Context, sourceType models.AlarmSourceType, sourceID uint32, alarmCode models.AlarmCode) (bool, error)

	// GetActiveAlarmByUniqueFieldsTx 在指定事务中根据唯一业务键获取一个活跃告警
	GetActiveAlarmByUniqueFieldsTx(ctx context.Context, tx *gorm.DB, sourceType models.AlarmSourceType, sourceID uint32, alarmCode models.AlarmCode) (*models.ActiveAlarm, error)

	// CreateHistoricalAlarmTx 在指定事务中创建一条历史告警记录
	CreateHistoricalAlarmTx(ctx context.Context, tx *gorm.DB, alarm *models.HistoricalAlarm) error

	// DeleteActiveAlarmTx 在指定事务中根据主键 ID 删除一个活跃告警
	DeleteActiveAlarmTx(ctx context.Context, tx *gorm.DB, id uint32) error

	// UpdateIgnoreStatus 更新指定告警的忽略状态
	UpdateIgnoreStatus(ctx context.Context, id uint32, isIgnored bool, ignoredUntil *time.Time) error

	// ListActiveAlarms 支持分页和过滤的活跃告警列表查询。
	// 返回活跃告警列表、总记录数和错误。
	ListActiveAlarms(ctx context.Context, opts ActiveAlarmListOptions, page, pageSize int) ([]models.ActiveAlarm, int64, error)

	// ListHistoricalAlarms 支持分页和过滤的历史告警列表查询。
	// 返回历史告警列表、总记录数和错误。
	ListHistoricalAlarms(ctx context.Context, opts HistoricalAlarmListOptions, page, pageSize int) ([]models.HistoricalAlarm, int64, error)

	// UpdateAlarmNotificationStatus 显式更新告警的通知相关状态字段。
	// lastNotifiedAt: 传入具体的发送时间。
	// isIgnored: 告警新的忽略状态。
	// ignoredUntil: 告警新的忽略截止时间 (nil 表示没有忽略截止时间/已取消忽略)。
	UpdateAlarmNotificationStatus(ctx context.Context, alarmID uint32, lastNotifiedAt time.Time, isIgnored bool, ignoredUntil *time.Time) error

	// ClearExpiredIgnoredAlarms 清除所有已过期的告警忽略状态。
	// 将 is_ignored 设置为 false，ignored_until 设置为 NULL。
	// 返回受影响的行数和错误。
	ClearExpiredIgnoredAlarms(ctx context.Context) (int64, error)

	// <-- 下列两个方法是为了性能做出的架构妥协, 业务逻辑入侵仓库层带来的收益远大于通过业务层进行数据筛选 -->

	// ListAlarmsForNotification 查询满足发送告警消息条件的活跃告警列表。
	// 返回活跃告警列表和错误。
	// intervalByLevel: key=SeverityLevel, value=interval_in_minutes
	ListAlarmsForNotification(ctx context.Context, intervalByLevel map[models.SeverityLevel]uint32) ([]models.ActiveAlarm, error)
	// 查询满足发送告警消息条件的记录总数
	CountAlarmsForNotification(ctx context.Context, intervalByLevel map[models.SeverityLevel]uint32) (int64, error)
}

// gormAlarmRepository 是 AlarmRepository 的 GORM 实现。
type gormAlarmRepository struct {
	ctx context.Context
	db  *gorm.DB
}

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

// CreateActiveAlarm 创建一条新的活跃告警记录
func (r *gormAlarmRepository) CreateActiveAlarm(ctx context.Context, alarm *models.ActiveAlarm) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateActiveAlarm")
	return r.db.WithContext(repoCtx).Create(alarm).Error
}

// IsAlarmActiveInUse 检查具有相同来源和告警代码的告警当前是否处于活跃表中
func (r *gormAlarmRepository) IsAlarmActiveInUse(ctx context.Context, sourceType models.AlarmSourceType, sourceID uint32, alarmCode models.AlarmCode) (bool, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "IsAlarmActiveInUse")
	var count int64
	err := r.db.WithContext(repoCtx).Model(&models.ActiveAlarm{}).
		Where("source_type = ? AND source_id = ? AND alarm_code = ?", sourceType, sourceID, alarmCode).
		Count(&count).Error
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

// GetActiveAlarmByUniqueFieldsTx 在指定事务中根据唯一业务键获取一个活跃告警
func (r *gormAlarmRepository) GetActiveAlarmByUniqueFieldsTx(ctx context.Context, tx *gorm.DB, sourceType models.AlarmSourceType, sourceID uint32, alarmCode models.AlarmCode) (*models.ActiveAlarm, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "GetActiveAlarmByUniqueFieldsTx")
	var alarm models.ActiveAlarm
	err := tx.WithContext(repoCtx).
		Where("source_type = ? AND source_id = ? AND alarm_code = ?", sourceType, sourceID, alarmCode).
		First(&alarm).Error
	return &alarm, err
}

// CreateHistoricalAlarmTx 在指定事务中创建一条历史告警记录
func (r *gormAlarmRepository) CreateHistoricalAlarmTx(ctx context.Context, tx *gorm.DB, alarm *models.HistoricalAlarm) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "CreateHistoricalAlarmTx")
	return tx.WithContext(repoCtx).Create(alarm).Error
}

// DeleteActiveAlarmTx 在指定事务中根据主键 ID 删除一个活跃告警
func (r *gormAlarmRepository) DeleteActiveAlarmTx(ctx context.Context, tx *gorm.DB, id uint32) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "DeleteActiveAlarmTx")
	// 使用 Unscoped() 确保执行物理删除，而不是软删除
	return tx.WithContext(repoCtx).Unscoped().Delete(&models.ActiveAlarm{}, id).Error
}

// UpdateIgnoreStatus 更新指定告警的忽略状态
func (r *gormAlarmRepository) UpdateIgnoreStatus(ctx context.Context, id uint32, isIgnored bool, ignoredUntil *time.Time) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateIgnoreStatus")
	updates := map[string]interface{}{
		"is_ignored":    isIgnored,
		"ignored_until": ignoredUntil,
	}

	result := r.db.WithContext(repoCtx).
		Model(&models.ActiveAlarm{}).
		Where("id = ?", id).
		Updates(updates)

	if result.Error != nil {
		return result.Error
	}

	if result.RowsAffected == 0 {
		return gorm.ErrRecordNotFound
	}

	return nil
}

// ClearExpiredIgnoredAlarms 清除所有已过期的告警忽略状态。
// 将 is_ignored 设置为 false，ignored_until 设置为 NULL。
// 返回受影响的行数和错误。
func (r *gormAlarmRepository) ClearExpiredIgnoredAlarms(ctx context.Context) (int64, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "ClearExpiredIgnoredAlarms")
	now := time.Now()

	updates := map[string]interface{}{
		"is_ignored":    false,
		"ignored_until": nil, // 将 ignored_until 设置为 NULL
	}

	result := r.db.WithContext(repoCtx).
		Model(&models.ActiveAlarm{}).
		Where("is_ignored = ? AND ignored_until <= ?", true, now).
		Updates(updates)

	if result.Error != nil {
		return 0, result.Error
	}

	return result.RowsAffected, nil
}

// ListActiveAlarms 实现了分页和过滤查询活跃告警记录的功能
func (r *gormAlarmRepository) ListActiveAlarms(ctx context.Context, opts ActiveAlarmListOptions, page, pageSize int) ([]models.ActiveAlarm, int64, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "ListActiveAlarms")
	// --- 校验分页参数 ---
	if page <= 0 || pageSize <= 0 {
		return nil, 0, ErrInvalidPagination
	}

	var results []models.ActiveAlarm
	var total int64

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

	// --- 应用过滤条件 ---
	if opts.SourceType != nil {
		query = query.Where("source_type = ?", *opts.SourceType)
	}
	if opts.SourceID != nil {
		query = query.Where("source_id = ?", *opts.SourceID)
	}
	if opts.Level != nil {
		query = query.Where("level = ?", *opts.Level)
	}
	if opts.IsIgnored != nil {
		query = query.Where("is_ignored = ?", *opts.IsIgnored)
	}
	if opts.TriggerTime != nil {
		query = query.Where("trigger_time >= ?", *opts.TriggerTime)
	}
	if opts.EndTime != nil {
		query = query.Where("trigger_time <= ?", *opts.EndTime)
	}

	// --- 计算总数 ---
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// --- 应用排序条件 ---
	orderBy := "trigger_time DESC" // 默认按触发时间倒序
	if opts.OrderBy != "" {
		orderBy = opts.OrderBy
	}
	query = query.Order(orderBy)

	// --- 分页 ---
	offset := (page - 1) * pageSize
	err := query.Limit(pageSize).Offset(offset).Find(&results).Error

	return results, total, err
}

// ListHistoricalAlarms 实现了分页和过滤查询历史告警记录的功能
func (r *gormAlarmRepository) ListHistoricalAlarms(ctx context.Context, opts HistoricalAlarmListOptions, page, pageSize int) ([]models.HistoricalAlarm, int64, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "ListHistoricalAlarms")
	// --- 校验分页参数 ---
	if page <= 0 || pageSize <= 0 {
		return nil, 0, ErrInvalidPagination
	}

	var results []models.HistoricalAlarm
	var total int64

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

	// --- 应用过滤条件 ---
	if opts.SourceType != nil {
		query = query.Where("source_type = ?", *opts.SourceType)
	}
	if opts.SourceID != nil {
		query = query.Where("source_id = ?", *opts.SourceID)
	}
	if opts.Level != nil {
		query = query.Where("level = ?", *opts.Level)
	}
	if opts.TriggerTimeStart != nil {
		query = query.Where("trigger_time >= ?", *opts.TriggerTimeStart)
	}
	if opts.TriggerTimeEnd != nil {
		query = query.Where("trigger_time <= ?", *opts.TriggerTimeEnd)
	}
	if opts.ResolveTimeStart != nil { // 修改字段名
		query = query.Where("resolve_time >= ?", *opts.ResolveTimeStart) // 修改查询字段名
	}
	if opts.ResolveTimeEnd != nil { // 修改字段名
		query = query.Where("resolve_time <= ?", *opts.ResolveTimeEnd) // 修改查询字段名
	}

	// --- 计算总数 ---
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// --- 应用排序条件 ---
	orderBy := "trigger_time DESC" // 默认按触发时间倒序
	if opts.OrderBy != "" {
		orderBy = opts.OrderBy
	}
	query = query.Order(orderBy)

	// --- 分页 ---
	offset := (page - 1) * pageSize
	err := query.Limit(pageSize).Offset(offset).Find(&results).Error

	return results, total, err
}

func (r *gormAlarmRepository) UpdateAlarmNotificationStatus(ctx context.Context, alarmID uint32, lastNotifiedAt time.Time, isIgnored bool, ignoredUntil *time.Time) error {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "UpdateAlarmNotificationStatus")

	// 1. 内部安全地构造 map，将强类型参数转换为 GORM 需要的格式
	// GORM 的 Updates 方法会正确处理 *time.Time (nil -> DB NULL)
	updates := map[string]interface{}{
		"last_notified_at": lastNotifiedAt, // time.Time 会被 GORM 视为非空时间
		"is_ignored":       isIgnored,
		"ignored_until":    ignoredUntil, // *time.Time (nil) 会被 GORM 写入 NULL
	}

	// 2. 执行更新
	result := r.db.WithContext(repoCtx).
		Model(&models.ActiveAlarm{}).
		Where("id = ?", alarmID).
		Updates(updates) // 仅更新 updates map 中指定的三个字段

	if result.Error != nil {
		return result.Error
	}
	return nil
}

// CountAlarmsForNotification 查询满足发送告警消息条件的记录总数
func (r *gormAlarmRepository) CountAlarmsForNotification(ctx context.Context, intervalByLevel map[models.SeverityLevel]uint32) (int64, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "CountAlarmsForNotification")
	var total int64

	// 1. 构造基础查询对象 (包含 Context 和 Model)
	baseTx := r.db.WithContext(repoCtx).Model(&models.ActiveAlarm{})

	// 2. 传递给辅助函数应用所有 WHERE 逻辑
	query := r.buildNotificationBaseQuery(baseTx, intervalByLevel)

	// 3. 只执行 Count
	err := query.Count(&total).Error

	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return 0, err
	}

	return total, nil
}

// ListAlarmsForNotification 查询满足发送告警消息条件的活跃告警列表
func (r *gormAlarmRepository) ListAlarmsForNotification(ctx context.Context, intervalByLevel map[models.SeverityLevel]uint32) ([]models.ActiveAlarm, error) {
	repoCtx := logs.AddFuncName(ctx, r.ctx, "ListAlarmsForNotification")
	var results []models.ActiveAlarm

	// 1. 构造基础查询对象 (包含 Context 和 Model)
	baseTx := r.db.WithContext(repoCtx).Model(&models.ActiveAlarm{})

	// 2. 传递给辅助函数应用所有 WHERE 逻辑
	query := r.buildNotificationBaseQuery(baseTx, intervalByLevel)

	// 3. 执行 Find (不排序，高性能)
	err := query.Find(&results).Error

	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}

	return results, nil
}

// buildNotificationBaseQuery 负责组合 Group A 和 Group B 的逻辑
func (r *gormAlarmRepository) buildNotificationBaseQuery(tx *gorm.DB, intervalByLevel map[models.SeverityLevel]uint32) *gorm.DB {

	// 1. 获取所有配置的 Level 列表
	configuredLevels := make([]models.SeverityLevel, 0, len(intervalByLevel))
	for level := range intervalByLevel {
		configuredLevels = append(configuredLevels, level)
	}

	// 2. 构造 Group A (只发送一次)
	// Group A 是一个独立的 GORM SubQuery，用于构建 OR 关系
	groupAQuery := r.buildGroupAClause(tx.Session(&gorm.Session{}), configuredLevels)

	// 3. 构造 Group B (间隔发送)
	// Group B 也是一个独立的 GORM SubQuery
	groupBQuery := r.buildGroupBClause(tx.Session(&gorm.Session{}), intervalByLevel, configuredLevels)

	// 4. 最终组合：Group A OR Group B

	// 核心逻辑：利用 GORM 的 Where(SubQuery) OR Where(SubQuery) 特性。
	// GORM 允许将 WHERE 或 OR 的参数写成 func(db *gorm.DB) *gorm.DB
	// 这样可以确保子查询的括号被正确处理，实现 (A) OR (B) 结构。

	// 注意：我们必须检查配置，因为如果 Group B 配置为空，我们不应该将其添加到 OR 关系中。
	if len(configuredLevels) == 0 {
		// 只有 Group A 存在（即 Level NOT IN 的条件是 1=1）
		return tx.Where(groupAQuery)
	}

	// 存在 Group A 和 Group B，用 OR 连接
	return tx.Where(groupAQuery).Or(groupBQuery)
}

// buildGroupAClause 构造 Group A 的 WHERE 语句和参数列表。
// 针对 Level 缺失配置（或所有 Level）的告警，使用“只发送一次”逻辑：LastNotifiedAt IS NULL
// 参数 configuredLevels: 用于构建 Level NOT IN (?) 子句。
func (r *gormAlarmRepository) buildGroupAClause(tx *gorm.DB, configuredLevels []models.SeverityLevel) *gorm.DB {
	// A.1. 构造 Level 范围检查子句 (Level NOT IN 或 1=1)
	if len(configuredLevels) > 0 {
		tx = tx.Where("level NOT IN (?)", configuredLevels)
	} else {
		// 如果配置列表为空，则所有 Level 都符合，使用 1=1
		tx = tx.Where("1 = 1")
	}

	// A.2. 构造 Group A 核心逻辑 (LastNotifiedAt IS NULL 且未被忽略)
	// 由于前置任务已处理忽略状态，这里只需筛选未被忽略且未通知过的告警
	return tx.Where("is_ignored = ? AND last_notified_at IS NULL", false)
}

// buildGroupBClause 构造 Group B 的 WHERE 语句和参数列表。
// 针对 Level 存在配置的告警，使用“间隔发送”逻辑。
func (r *gormAlarmRepository) buildGroupBClause(tx *gorm.DB, intervalByLevel map[models.SeverityLevel]uint32, configuredLevels []models.SeverityLevel) *gorm.DB {
	now := time.Now()

	// B.1. 构造 Level IN 子句
	tx = tx.Where("level IN (?)", configuredLevels)

	// B.2. 构造 Level-Based 间隔检查 (OR 部分)
	// 核心思想：利用 GORM 的 Or 链式调用构建 Level 间隔检查子句

	// 初始化 Level 间隔检查查询 (ICC)
	iccQuery := tx.Session(&gorm.Session{}) // 创建一个干净的子查询对象来构建 ICC

	// 动态添加 Level 间隔检查 OR 条件
	for level, minutes := range intervalByLevel {
		// PostgreSQL 语法: last_notified_at + (5 * interval '1 minute') <= ?
		sql := fmt.Sprintf("level = ? AND last_notified_at + (%d * interval '1 minute') <= ?", minutes)

		// 每次使用 Or 叠加新的 Level 检查
		iccQuery = iccQuery.Or(sql, level, now)
	}

	// B.3. 组合 Group B 核心逻辑: (is_ignored = false AND (last_notified_at IS NULL OR [ICC]))
	// 由于前置任务已处理忽略状态，这里只需筛选未被忽略且满足通知条件的告警
	return tx.Where("is_ignored = ?", false).Where(
		tx.Session(&gorm.Session{}).Where("last_notified_at IS NULL").Or(iccQuery),
	)
}
