package service

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

	"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/inventory"
	"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"
)

// 定义库存应用服务特定的错误
var (
	ErrInventoryRawMaterialNotFound = errors.New("原料不存在")
	ErrInventoryInsufficientStock   = errors.New("原料库存不足")
)

// InventoryService 定义了库存相关的应用服务接口
type InventoryService interface {
	AdjustStock(ctx context.Context, req *dto.StockAdjustmentRequest) (*dto.StockLogResponse, error)
	ListCurrentStock(ctx context.Context, req *dto.ListCurrentStockRequest) (*dto.ListCurrentStockResponse, error)
	ListStockLogs(ctx context.Context, req *dto.ListStockLogRequest) (*dto.ListStockLogResponse, error)
}

// inventoryServiceImpl 是 InventoryService 接口的实现
type inventoryServiceImpl struct {
	ctx        context.Context
	invSvc     inventory.InventoryCoreService
	rawMatRepo repository.RawMaterialRepository
}

// NewInventoryService 创建一个新的 InventoryService 实例
func NewInventoryService(ctx context.Context, invSvc inventory.InventoryCoreService, rawMatRepo repository.RawMaterialRepository) InventoryService {
	return &inventoryServiceImpl{
		ctx:        ctx,
		invSvc:     invSvc,
		rawMatRepo: rawMatRepo,
	}
}

// AdjustStock 手动调整库存
func (s *inventoryServiceImpl) AdjustStock(ctx context.Context, req *dto.StockAdjustmentRequest) (*dto.StockLogResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "AdjustStock")

	// 调用领域服务执行核心业务逻辑
	log, err := s.invSvc.AdjustStock(serviceCtx, req.RawMaterialID, req.ChangeAmount, req.SourceType, req.SourceID, req.Remarks)
	if err != nil {
		if errors.Is(err, inventory.ErrRawMaterialNotFound) {
			return nil, ErrInventoryRawMaterialNotFound
		}
		if errors.Is(err, inventory.ErrInsufficientStock) {
			return nil, ErrInventoryInsufficientStock
		}
		return nil, fmt.Errorf("调整库存失败: %w", err)
	}

	// 手动加载 RawMaterial 信息，因为 CreateRawMaterialStockLog 不会预加载它
	rawMaterial, err := s.rawMatRepo.GetRawMaterialByID(serviceCtx, log.RawMaterialID)
	if err != nil {
		// 理论上不应该发生，因为 AdjustStock 内部已经检查过
		return nil, fmt.Errorf("获取原料信息失败: %w", err)
	}
	log.RawMaterial = *rawMaterial

	return dto.ConvertStockLogToDTO(log), nil
}

// ListCurrentStock 列出所有原料的当前库存
func (s *inventoryServiceImpl) ListCurrentStock(ctx context.Context, req *dto.ListCurrentStockRequest) (*dto.ListCurrentStockResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListCurrentStock")

	// 1. 获取分页的原料列表
	rawMatOpts := repository.RawMaterialListOptions{
		Name:     req.RawMaterialName,
		OrderBy:  req.OrderBy, // 注意：这里的排序可能需要调整，比如按原料名排序
		HasStock: req.HasStock,
	}
	rawMaterials, total, err := s.rawMatRepo.ListRawMaterials(serviceCtx, rawMatOpts, req.Page, req.PageSize)
	if err != nil {
		return nil, fmt.Errorf("获取原料列表失败: %w", err)
	}

	if len(rawMaterials) == 0 {
		return &dto.ListCurrentStockResponse{
			List:       []dto.CurrentStockResponse{},
			Pagination: dto.PaginationDTO{Page: req.Page, PageSize: req.PageSize, Total: total},
		}, nil
	}

	// 2. 提取原料ID并批量获取它们的最新库存日志
	materialIDs := make([]uint32, len(rawMaterials))
	for i, rm := range rawMaterials {
		materialIDs[i] = rm.ID
	}
	latestLogMap, err := s.rawMatRepo.BatchGetLatestStockLogsForMaterials(serviceCtx, materialIDs)
	if err != nil {
		return nil, fmt.Errorf("批量获取最新库存日志失败: %w", err)
	}

	// 3. 组合原料信息和库存信息
	stockDTOs := make([]dto.CurrentStockResponse, len(rawMaterials))
	for i, rm := range rawMaterials {
		log, _ := latestLogMap[rm.ID] // 如果找不到，log会是零值
		stockDTOs[i] = *dto.ConvertCurrentStockToDTO(&rm, &log)
	}

	return &dto.ListCurrentStockResponse{
		List:       stockDTOs,
		Pagination: dto.PaginationDTO{Page: req.Page, PageSize: req.PageSize, Total: total},
	}, nil
}

// ListStockLogs 列出库存变动历史
func (s *inventoryServiceImpl) ListStockLogs(ctx context.Context, req *dto.ListStockLogRequest) (*dto.ListStockLogResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListStockLogs")

	// 解析时间字符串
	var startTime, endTime *time.Time
	if req.StartTime != nil && *req.StartTime != "" {
		t, err := time.Parse(time.RFC3339, *req.StartTime)
		if err != nil {
			return nil, fmt.Errorf("无效的开始时间格式: %w", err)
		}
		startTime = &t
	}
	if req.EndTime != nil && *req.EndTime != "" {
		t, err := time.Parse(time.RFC3339, *req.EndTime)
		if err != nil {
			return nil, fmt.Errorf("无效的结束时间格式: %w", err)
		}
		endTime = &t
	}

	// 转换 source types
	sourceTypes := make([]models.StockLogSourceType, len(req.SourceTypes))
	for i, st := range req.SourceTypes {
		sourceTypes[i] = models.StockLogSourceType(st)
	}

	opts := repository.StockLogListOptions{
		RawMaterialID: req.RawMaterialID,
		SourceTypes:   sourceTypes,
		StartTime:     startTime,
		EndTime:       endTime,
		OrderBy:       req.OrderBy,
	}

	logs, total, err := s.invSvc.ListStockLogs(serviceCtx, opts, req.Page, req.PageSize)
	if err != nil {
		return nil, fmt.Errorf("获取库存日志列表失败: %w", err)
	}

	return dto.ConvertStockLogListToDTO(logs, total, req.Page, req.PageSize), nil
}
