package service

import (
	"context"
	"errors"
	"fmt"

	"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/recipe"
	"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 (
	ErrRecipeNameConflict = errors.New("配方名称已存在")
	ErrRecipeNotFound     = errors.New("配方不存在")
)

// RecipeService 定义了配方相关的应用服务接口
type RecipeService interface {
	CreateRecipe(ctx context.Context, req *dto.CreateRecipeRequest) (*dto.RecipeResponse, error)
	UpdateRecipe(ctx context.Context, id uint32, req *dto.UpdateRecipeRequest) (*dto.RecipeResponse, error)
	DeleteRecipe(ctx context.Context, id uint32) error
	GetRecipeByID(ctx context.Context, id uint32) (*dto.RecipeResponse, error)
	ListRecipes(ctx context.Context, req *dto.ListRecipeRequest) (*dto.ListRecipeResponse, error)
	// GenerateRecipeWithAllRawMaterials 添加新方法
	GenerateRecipeWithAllRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error)
	// GenerateRecipeWithPrioritizedStockRawMaterials 生成新配方，优先使用有库存的原料
	GenerateRecipeWithPrioritizedStockRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error)
	// AIDiagnoseRecipe 智能诊断配方, 返回智能诊断结果和使用的AI模型
	AIDiagnoseRecipe(ctx context.Context, recipeID uint32, pigTypeID uint32) (*dto.ReviewRecipeResponse, error)
}

// recipeServiceImpl 是 RecipeService 接口的实现
type recipeServiceImpl struct {
	ctx       context.Context
	recipeSvc recipe.Service
}

// NewRecipeService 创建一个新的 RecipeService 实例
func NewRecipeService(ctx context.Context, recipeSvc recipe.Service) RecipeService {
	return &recipeServiceImpl{
		ctx:       ctx,
		recipeSvc: recipeSvc,
	}
}

// GenerateRecipeWithAllRawMaterials 实现新方法
func (s *recipeServiceImpl) GenerateRecipeWithAllRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "GenerateRecipeWithAllRawMaterials")
	// 直接调用领域服务的方法
	return s.recipeSvc.GenerateRecipeWithAllRawMaterials(serviceCtx, pigTypeID)
}

// GenerateRecipeWithPrioritizedStockRawMaterials 实现生成优先使用库存原料配方的方法
func (s *recipeServiceImpl) GenerateRecipeWithPrioritizedStockRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "GenerateRecipeWithPrioritizedStockRawMaterials")
	// 直接调用领域服务的方法
	return s.recipeSvc.GenerateRecipeWithPrioritizedStockRawMaterials(serviceCtx, pigTypeID)
}

// CreateRecipe 创建配方
func (s *recipeServiceImpl) CreateRecipe(ctx context.Context, req *dto.CreateRecipeRequest) (*dto.RecipeResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateRecipe")

	recipeModel := dto.ConvertCreateRecipeRequestToModel(req)

	createdRecipe, err := s.recipeSvc.CreateRecipe(serviceCtx, recipeModel)
	if err != nil {
		if errors.Is(err, recipe.ErrRecipeNameConflict) {
			return nil, ErrRecipeNameConflict
		}
		return nil, fmt.Errorf("创建配方失败: %w", err)
	}

	// 创建成功后，获取包含完整信息的配方
	fullRecipe, err := s.recipeSvc.GetRecipeByID(serviceCtx, createdRecipe.ID)
	if err != nil {
		// 理论上不应该发生，因为刚创建成功
		return nil, fmt.Errorf("创建后获取配方信息失败: %w", err)
	}

	return dto.ConvertRecipeToDto(fullRecipe), nil
}

// UpdateRecipe 更新配方
func (s *recipeServiceImpl) UpdateRecipe(ctx context.Context, id uint32, req *dto.UpdateRecipeRequest) (*dto.RecipeResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateRecipe")

	// 1. 转换 DTO 为模型
	recipeModel := dto.ConvertUpdateRecipeRequestToModel(req)
	recipeModel.ID = id

	// 2. 更新配方基础信息
	_, err := s.recipeSvc.UpdateRecipe(serviceCtx, recipeModel)
	if err != nil {
		if errors.Is(err, recipe.ErrRecipeNotFound) {
			return nil, ErrRecipeNotFound
		}
		if errors.Is(err, recipe.ErrRecipeNameConflict) {
			return nil, ErrRecipeNameConflict
		}
		return nil, fmt.Errorf("更新配方基础信息失败: %w", err)
	}

	// 3. 更新配方原料
	ingredients := make([]models.RecipeIngredient, len(req.RecipeIngredients))
	for i, item := range req.RecipeIngredients {
		ingredients[i] = models.RecipeIngredient{
			RecipeID:      id,
			RawMaterialID: item.RawMaterialID,
			Percentage:    item.Percentage,
		}
	}
	err = s.recipeSvc.UpdateRecipeIngredients(serviceCtx, id, ingredients)
	if err != nil {
		if errors.Is(err, recipe.ErrRecipeNotFound) {
			return nil, ErrRecipeNotFound
		}
		return nil, fmt.Errorf("更新配方原料失败: %w", err)
	}

	// 4. 更新成功后，获取最新的完整配方信息并返回
	updatedRecipe, err := s.recipeSvc.GetRecipeByID(serviceCtx, id)
	if err != nil {
		if errors.Is(err, recipe.ErrRecipeNotFound) {
			// 理论上不应该发生，因为刚更新成功
			return nil, ErrRecipeNotFound
		}
		return nil, fmt.Errorf("更新后获取配方信息失败: %w", err)
	}

	return dto.ConvertRecipeToDto(updatedRecipe), nil
}

// DeleteRecipe 删除配方
func (s *recipeServiceImpl) DeleteRecipe(ctx context.Context, id uint32) error {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteRecipe")
	err := s.recipeSvc.DeleteRecipe(serviceCtx, id)
	if err != nil {
		if errors.Is(err, recipe.ErrRecipeNotFound) {
			return ErrRecipeNotFound
		}
		return fmt.Errorf("删除配方失败: %w", err)
	}
	return nil
}

// GetRecipeByID 获取单个配方
func (s *recipeServiceImpl) GetRecipeByID(ctx context.Context, id uint32) (*dto.RecipeResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetRecipeByID")

	recipeModel, err := s.recipeSvc.GetRecipeByID(serviceCtx, id)
	if err != nil {
		if errors.Is(err, recipe.ErrRecipeNotFound) {
			return nil, ErrRecipeNotFound
		}
		return nil, fmt.Errorf("获取配方失败: %w", err)
	}
	return dto.ConvertRecipeToDto(recipeModel), nil
}

// ListRecipes 列出配方
func (s *recipeServiceImpl) ListRecipes(ctx context.Context, req *dto.ListRecipeRequest) (*dto.ListRecipeResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListRecipes")

	opts := repository.RecipeListOptions{
		Name:    req.Name,
		OrderBy: req.OrderBy,
	}
	recipes, total, err := s.recipeSvc.ListRecipes(serviceCtx, opts, req.Page, req.PageSize)
	if err != nil {
		return nil, fmt.Errorf("获取配方列表失败: %w", err)
	}

	return dto.ConvertRecipeListToDTO(recipes, total, req.Page, req.PageSize), nil
}

// AIDiagnoseRecipe 实现智能诊断配方的方法
func (s *recipeServiceImpl) AIDiagnoseRecipe(ctx context.Context, recipeID uint32, pigTypeID uint32) (*dto.ReviewRecipeResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "AIDiagnoseRecipe")

	reviewMessage, aiModel, err := s.recipeSvc.AIDiagnoseRecipe(serviceCtx, recipeID, pigTypeID)
	if err != nil {
		return nil, fmt.Errorf("AI 诊断配方失败: %w", err)
	}

	return &dto.ReviewRecipeResponse{
		ReviewMessage: reviewMessage,
		AIModel:       aiModel,
	}, nil
}
