package pig

import (
	"context"
	"errors"
	"fmt"

	"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"
)

// PigPenTransferManager 定义了与猪只位置转移相关的底层数据库操作。
// 它是一个内部服务，被主服务 PigBatchService 调用。
type PigPenTransferManager interface {
	// LogTransfer 在数据库中创建一条猪只迁移日志。
	LogTransfer(ctx context.Context, tx *gorm.DB, log *models.PigTransferLog) error

	// GetPenByID 用于获取猪栏的详细信息，供上层服务进行业务校验。
	GetPenByID(ctx context.Context, tx *gorm.DB, penID uint32) (*models.Pen, error)

	// GetPensByBatchID 获取一个猪群当前关联的所有猪栏。
	GetPensByBatchID(ctx context.Context, tx *gorm.DB, batchID uint32) ([]*models.Pen, error)

	// UpdatePenFields 更新一个猪栏的指定字段。
	UpdatePenFields(ctx context.Context, tx *gorm.DB, penID uint32, updates map[string]interface{}) error

	// GetCurrentPigsInPen 通过汇总猪只迁移日志，计算给定猪栏中的当前猪只数量。
	GetCurrentPigsInPen(ctx context.Context, tx *gorm.DB, penID uint32) (int, error)

	// GetTotalPigsInPensForBatchTx 计算指定猪群下所有猪栏的当前总存栏数
	GetTotalPigsInPensForBatchTx(ctx context.Context, tx *gorm.DB, batchID uint32) (int, error)

	// ReleasePen 将猪栏的猪群归属移除，并将其状态标记为空闲。
	ReleasePen(ctx context.Context, tx *gorm.DB, penID uint32) error
}

// pigPenTransferManager 是 PigPenTransferManager 接口的具体实现。
// 它作为调栏管理器，处理底层的数据库交互。
type pigPenTransferManager struct {
	ctx          context.Context
	penRepo      repository.PigPenRepository
	logRepo      repository.PigTransferLogRepository
	pigBatchRepo repository.PigBatchRepository
}

// NewPigPenTransferManager 是 pigPenTransferManager 的构造函数。
func NewPigPenTransferManager(ctx context.Context, penRepo repository.PigPenRepository, logRepo repository.PigTransferLogRepository, pigBatchRepo repository.PigBatchRepository) PigPenTransferManager {
	return &pigPenTransferManager{
		ctx:          ctx,
		penRepo:      penRepo,
		logRepo:      logRepo,
		pigBatchRepo: pigBatchRepo,
	}
}

// LogTransfer 实现了在数据库中创建迁移日志的逻辑。
func (s *pigPenTransferManager) LogTransfer(ctx context.Context, tx *gorm.DB, log *models.PigTransferLog) error {
	managerCtx := logs.AddFuncName(ctx, s.ctx, "LogTransfer")
	return s.logRepo.CreatePigTransferLog(managerCtx, tx, log)
}

// GetPenByID 实现了获取猪栏信息的逻辑。
func (s *pigPenTransferManager) GetPenByID(ctx context.Context, tx *gorm.DB, penID uint32) (*models.Pen, error) {
	managerCtx := logs.AddFuncName(ctx, s.ctx, "GetPenByID")
	return s.penRepo.GetPenByIDTx(managerCtx, tx, penID)
}

// GetPensByBatchID 实现了获取猪群关联猪栏列表的逻辑。
func (s *pigPenTransferManager) GetPensByBatchID(ctx context.Context, tx *gorm.DB, batchID uint32) ([]*models.Pen, error) {
	managerCtx := logs.AddFuncName(ctx, s.ctx, "GetPensByBatchID")
	return s.penRepo.GetPensByBatchIDTx(managerCtx, tx, batchID)
}

// UpdatePenFields 实现了更新猪栏字段的逻辑。
func (s *pigPenTransferManager) UpdatePenFields(ctx context.Context, tx *gorm.DB, penID uint32, updates map[string]interface{}) error {
	managerCtx := logs.AddFuncName(ctx, s.ctx, "UpdatePenFields")
	return s.penRepo.UpdatePenFieldsTx(managerCtx, tx, penID, updates)
}

// GetCurrentPigsInPen 实现了计算猪栏当前猪只数量的逻辑。
func (s *pigPenTransferManager) GetCurrentPigsInPen(ctx context.Context, tx *gorm.DB, penID uint32) (int, error) {
	managerCtx := logs.AddFuncName(ctx, s.ctx, "GetCurrentPigsInPen")
	// 1. 通过猪栏ID查出所属猪群信息
	pen, err := s.penRepo.GetPenByIDTx(managerCtx, tx, penID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, ErrPenNotFound
		}
		return 0, err
	}

	// 如果猪栏没有关联任何猪群，那么猪只数必为0
	if pen.PigBatchID == nil || *pen.PigBatchID == 0 {
		return 0, nil
	}
	currentBatchID := *pen.PigBatchID

	// 2. 根据猪群ID获取猪群的起始日期
	batch, err := s.pigBatchRepo.GetPigBatchByIDTx(managerCtx, tx, currentBatchID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, ErrPigBatchNotFound
		}
		return 0, err
	}
	batchStartDate := batch.StartDate

	// 3. 调用仓库方法，获取从猪群开始至今，该猪栏的所有倒序日志
	logs, err := s.logRepo.GetLogsForPenSince(managerCtx, tx, penID, batchStartDate)
	if err != nil {
		return 0, err
	}

	// 如果没有日志，猪只数为0
	if len(logs) == 0 {
		return 0, nil
	}

	// 4. 在内存中筛选出最后一段连续日志，并进行计算
	var totalPigs int
	// 再次确认当前猪群ID，以最新的日志为准，防止在极小时间窗口内猪栏被快速切换
	latestBatchID := *pen.PigBatchID

	for _, log := range logs {
		// 一旦发现日志不属于最新的猪群，立即停止计算
		if log.PigBatchID != latestBatchID {
			break
		}
		totalPigs += log.Quantity
	}

	return totalPigs, nil
}

// GetTotalPigsInPensForBatchTx 计算指定猪群下所有猪栏的当前总存栏数
// 该方法通过遍历猪群下的每个猪栏，并调用 GetCurrentPigsInPen 来累加存栏数。
func (s *pigPenTransferManager) GetTotalPigsInPensForBatchTx(ctx context.Context, tx *gorm.DB, batchID uint32) (int, error) {
	managerCtx := logs.AddFuncName(ctx, s.ctx, "GetTotalPigsInPensForBatchTx")
	// 1. 获取该批次下所有猪栏的列表
	pensInBatch, err := s.GetPensByBatchID(managerCtx, tx, batchID)
	if err != nil {
		return 0, fmt.Errorf("获取猪群 %d 下属猪栏失败: %w", batchID, err)
	}

	totalPigs := 0
	// 2. 遍历每个猪栏，累加其存栏数
	for _, pen := range pensInBatch {
		pigsInPen, err := s.GetCurrentPigsInPen(managerCtx, tx, pen.ID)
		if err != nil {
			return 0, fmt.Errorf("获取猪栏 %d 存栏数失败: %w", pen.ID, err)
		}
		totalPigs += pigsInPen
	}

	return totalPigs, nil
}

// ReleasePen 将猪栏的猪群归属移除，并将其状态标记为空闲。
// 此操作通常在猪栏被清空后调用。
func (s *pigPenTransferManager) ReleasePen(ctx context.Context, tx *gorm.DB, penID uint32) error {
	managerCtx := logs.AddFuncName(ctx, s.ctx, "ReleasePen")
	// 1. 获取猪栏信息
	pen, err := s.penRepo.GetPenByIDTx(managerCtx, tx, penID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return fmt.Errorf("猪栏 %d 不存在: %w", penID, ErrPenNotFound)
		}
		return fmt.Errorf("获取猪栏 %d 信息失败: %w", penID, err)
	}

	// 2. 更新猪栏字段
	// 将 pig_batch_id 设置为 nil (SQL NULL)
	// 将 status 设置为 PenStatusEmpty
	updates := map[string]interface{}{
		"pig_batch_id": nil, // 使用 nil 来表示 SQL NULL
		"status":       models.PenStatusEmpty,
	}

	if err := s.penRepo.UpdatePenFieldsTx(managerCtx, tx, penID, updates); err != nil {
		return fmt.Errorf("释放猪栏 %v 失败: %w", pen.PenNumber, err)
	}

	return nil
}
