package alarm

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"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"

	"gorm.io/gorm"
)

// AlarmService 定义了告警领域服务接口。
type AlarmService interface {
	// CreateAlarmIfNotExists 检查是否存在相同的活跃告警，如果不存在，则创建一条新的告警记录。
	// "相同"的定义是：SourceType, SourceID, 和 AlarmCode 都相同。
	CreateAlarmIfNotExists(ctx context.Context, newAlarm *models.ActiveAlarm) error

	// CloseAlarm 关闭一个活跃告警，将其归档到历史记录。
	// 如果指定的告警当前不活跃，则不执行任何操作并返回 nil。
	CloseAlarm(ctx context.Context, sourceType models.AlarmSourceType, sourceID uint32, alarmCode models.AlarmCode, resolveMethod string, resolvedBy *uint32) error

	// SnoozeAlarm 忽略一个活跃告警，或更新其忽略时间。
	// 如果告警不存在，将返回错误。
	SnoozeAlarm(ctx context.Context, alarmID uint32, duration time.Duration) error

	// CancelAlarmSnooze 取消对一个告警的忽略状态。
	// 如果告警不存在，或本就未被忽略，不执行任何操作并返回 nil。
	CancelAlarmSnooze(ctx context.Context, alarmID uint32) error

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

// alarmService 是 AlarmService 接口的具体实现。
type alarmService struct {
	ctx       context.Context
	alarmRepo repository.AlarmRepository
	uow       repository.UnitOfWork
}

// NewAlarmService 创建一个新的 AlarmService 实例。
func NewAlarmService(ctx context.Context, alarmRepo repository.AlarmRepository, uow repository.UnitOfWork) AlarmService {
	return &alarmService{
		ctx:       ctx,
		alarmRepo: alarmRepo,
		uow:       uow,
	}
}

// CreateAlarmIfNotExists 实现了创建告警（如果不存在）的逻辑。
func (s *alarmService) CreateAlarmIfNotExists(ctx context.Context, newAlarm *models.ActiveAlarm) error {
	serviceCtx, logger := logs.Trace(ctx, s.ctx, "CreateAlarmIfNotExists")

	// 1. 检查告警是否已处于活跃状态
	isActive, err := s.alarmRepo.IsAlarmActiveInUse(serviceCtx, newAlarm.SourceType, newAlarm.SourceID, newAlarm.AlarmCode)
	if err != nil {
		logger.Errorf("检查告警活跃状态时发生数据库错误: %v", err)
		return err // 直接返回数据库错误
	}

	if isActive {
		// 2. 如果已活跃，则记录日志并忽略
		logger.Infof("相同的告警已处于活跃状态，已忽略。来源: %s, ID: %d, 告警代码: %s", newAlarm.SourceType, newAlarm.SourceID, newAlarm.AlarmCode)
		return nil
	}

	// 3. 如果不活跃，则创建新告警
	logger.Infof("告警尚不活跃，正在创建新告警。来源: %s, ID: %d, 告警代码: %s", newAlarm.SourceType, newAlarm.SourceID, newAlarm.AlarmCode)
	return s.alarmRepo.CreateActiveAlarm(serviceCtx, newAlarm)
}

// CloseAlarm 实现了关闭告警并将其归档的逻辑。
func (s *alarmService) CloseAlarm(ctx context.Context, sourceType models.AlarmSourceType, sourceID uint32, alarmCode models.AlarmCode, resolveMethod string, resolvedBy *uint32) error {
	serviceCtx, logger := logs.Trace(ctx, s.ctx, "CloseAlarm")

	// 1. 在事务外进行快速只读检查，避免不必要的事务开销
	isActive, err := s.alarmRepo.IsAlarmActiveInUse(serviceCtx, sourceType, sourceID, alarmCode)
	if err != nil {
		logger.Errorf("关闭告警失败：预检查告警活跃状态失败: %v", err)
		return err
	}

	// 如果告警本就不活跃，则无需任何操作
	if !isActive {
		return nil
	}

	// 2. 确认告警存在后，再进入事务执行“移动”操作
	logger.Infof("检测到活跃告警，正在执行关闭和归档操作。来源: %s, ID: %d, 告警代码: %s", sourceType, sourceID, alarmCode)
	return s.uow.ExecuteInTransaction(serviceCtx, func(tx *gorm.DB) error {
		// 在事务中再次查找，确保数据一致性并获取完整对象
		activeAlarm, err := s.alarmRepo.GetActiveAlarmByUniqueFieldsTx(serviceCtx, tx, sourceType, sourceID, alarmCode)
		if err != nil {
			// 此时如果没找到，可能在预检查和本事务之间已被其他进程关闭，同样视为正常
			if errors.Is(err, gorm.ErrRecordNotFound) {
				logger.Infof("告警在事务开始前已被关闭，无需操作。")
				return nil
			}
			logger.Errorf("关闭告警失败：在事务中查找活跃告警失败: %v", err)
			return err
		}

		// 创建历史告警记录
		historicalAlarm := &models.HistoricalAlarm{
			SourceType:    activeAlarm.SourceType,
			SourceID:      activeAlarm.SourceID,
			AlarmCode:     activeAlarm.AlarmCode,
			AlarmSummary:  activeAlarm.AlarmSummary,
			Level:         activeAlarm.Level,
			AlarmDetails:  activeAlarm.AlarmDetails,
			TriggerTime:   activeAlarm.TriggerTime,
			ResolveTime:   time.Now(),
			ResolveMethod: resolveMethod,
			ResolvedBy:    resolvedBy,
		}

		// 在事务中插入历史告警
		if err := s.alarmRepo.CreateHistoricalAlarmTx(serviceCtx, tx, historicalAlarm); err != nil {
			logger.Errorf("关闭告警失败：归档告警 %d 到历史表失败: %v", activeAlarm.ID, err)
			return err
		}

		// 在事务中删除活跃告警
		if err := s.alarmRepo.DeleteActiveAlarmTx(serviceCtx, tx, activeAlarm.ID); err != nil {
			logger.Errorf("关闭告警失败：从活跃表删除告警 %d 失败: %v", activeAlarm.ID, err)
			return err
		}

		logger.Infof("告警 %d 已成功关闭并归档。", activeAlarm.ID)
		return nil
	})
}

// SnoozeAlarm 忽略一个活跃告警，或更新其忽略时间。
func (s *alarmService) SnoozeAlarm(ctx context.Context, alarmID uint32, duration time.Duration) error {
	serviceCtx, logger := logs.Trace(ctx, s.ctx, "SnoozeAlarm")

	if duration <= 0 {
		return errors.New("忽略时长必须为正数")
	}

	ignoredUntil := time.Now().Add(duration)
	err := s.alarmRepo.UpdateIgnoreStatus(serviceCtx, alarmID, true, &ignoredUntil)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			logger.Warnf("尝试忽略一个不存在的告警: %d", alarmID)
			return fmt.Errorf("告警 %d 不存在", alarmID)
		}
		logger.Errorf("更新告警 %d 的忽略状态失败: %v", alarmID, err)
		return err
	}

	logger.Infof("告警 %d 已被成功忽略，持续时间: %v", alarmID, duration)
	return nil
}

// CancelAlarmSnooze 取消对一个告警的忽略状态。
func (s *alarmService) CancelAlarmSnooze(ctx context.Context, alarmID uint32) error {
	serviceCtx, logger := logs.Trace(ctx, s.ctx, "CancelAlarmSnooze")

	err := s.alarmRepo.UpdateIgnoreStatus(serviceCtx, alarmID, false, nil)
	if err != nil {
		// 如果告警本就不存在，这不是一个需要上报的错误
		if errors.Is(err, gorm.ErrRecordNotFound) {
			logger.Infof("尝试取消忽略一个不存在的告警: %d，无需操作", alarmID)
			return nil
		}
		logger.Errorf("取消告警 %d 的忽略状态失败: %v", alarmID, err)
		return err
	}

	logger.Infof("告警 %d 的忽略状态已被成功取消。", alarmID)
	return nil
}

// ClearExpiredIgnoredAlarms 清除所有已过期的告警忽略状态。
func (s *alarmService) ClearExpiredIgnoredAlarms(ctx context.Context) (int64, error) {
	serviceCtx, logger := logs.Trace(ctx, s.ctx, "ClearExpiredIgnoredAlarms")

	rowsAffected, err := s.alarmRepo.ClearExpiredIgnoredAlarms(serviceCtx)
	if err != nil {
		logger.Errorf("清除过期忽略告警状态失败: %v", err)
		return 0, err
	}

	if rowsAffected > 0 {
		logger.Infof("成功清除了 %d 条过期忽略告警的状态。", rowsAffected)
	} else {
		logger.Debugf("没有发现需要清除的过期忽略告警。")
	}

	return rowsAffected, nil
}
