package userstore

import (
	"fmt"

	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/baseapi/utils/errlist"
	"git.rosy.net.cn/jx-callback/business/jxutils"

	"git.rosy.net.cn/jx-callback/business/jxutils/datares"
	"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
)

type FoodRecipeItemParam struct {
	Name   string `json:"name"`
	SkuIDs []int  `json:"skuIDs"`
}

type FoodRecipeStepParam struct {
	Name        string `orm:"size(48)" json:"name"`
	Description string `orm:"size(4096)" json:"description"`
	Img         string `orm:"size(48)" json:"img"`
}

type FoodRecipeItem struct {
	model.FoodRecipeItem
	ItemChoiceList []*dao.FoodRecipeItemChoiceExt `json:"itemChoiceList"`
}

type FoodRecipeDetail struct {
	dao.FoodRecipeWithAction
	ItemList []*FoodRecipeItem       `json:"itemList"`
	StepList []*model.FoodRecipeStep `json:"stepList"`
}

func updateFoodRecipeItemAndStep(ctx *jxcontext.Context, db *dao.DaoDB, recipeID int, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) {
	for k, v := range itemList {
		if len(v.SkuIDs) == 0 {
			return fmt.Errorf("SkuIDs必须要有值")
		}
		skuIDs, err2 := dao.GetSkus(db, v.SkuIDs, nil, nil, nil, nil)
		if err = err2; err != nil {
			return err
		}
		if len(v.SkuIDs) != len(skuIDs) {
			return fmt.Errorf("某些SkuID不存在")
		}

		item := &model.FoodRecipeItem{
			RecipeID: recipeID,
			Index:    int8(k + 1),
			Name:     v.Name,
		}
		dao.WrapAddIDCULDEntity(item, ctx.GetUserName())
		if err = dao.CreateEntity(db, item); err != nil {
			return err
		}
		for k2, v2 := range v.SkuIDs {
			choice := &model.FoodRecipeItemChoice{
				RecipeID: recipeID,
				Index:    item.Index,
				SkuID:    v2,

				ChoiceIndex: int8(k2 + 1),
			}
			dao.WrapAddIDCULDEntity(choice, ctx.GetUserName())
			if err = dao.CreateEntity(db, choice); err != nil {
				return err
			}
		}
	}

	for k, v := range stepList {
		step := &model.FoodRecipeStep{
			RecipeID:    recipeID,
			Index:       int8(k + 1),
			Name:        v.Name,
			Img:         v.Img,
			Description: v.Description,
		}
		dao.WrapAddIDCULDEntity(step, ctx.GetUserName())
		if err = dao.CreateEntity(db, step); err != nil {
			return err
		}
	}
	return err
}

func CreateFoodRecipe(ctx *jxcontext.Context, foodRecipe *model.FoodRecipe, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam, authorID string) (err error) {
	if len(itemList) == 0 {
		return fmt.Errorf("必须要有配料")
	}
	if len(stepList) == 0 {
		return fmt.Errorf("必须要有操作步骤")
	}

	db := dao.GetDB()
	txDB , _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()

	if authorID == "" {
		_, foodRecipe.AuthorID = ctx.GetMobileAndUserID()
	} else {
		foodRecipe.AuthorID = authorID
	}
	dao.WrapAddIDCULDEntity(foodRecipe, ctx.GetUserName())
	if err = dao.CreateEntity(db, foodRecipe); err != nil {
		return err
	}
	if err = updateFoodRecipeItemAndStep(ctx, db, foodRecipe.ID, itemList, stepList); err != nil {
		return err
	}
	dao.Commit(db, txDB)

	tryRegisterDataRes4Recipe(ctx, foodRecipe.Name, foodRecipe.Img, stepList)
	return err
}

func UpdateFoodRecipe(ctx *jxcontext.Context, recipeID int, mapData map[string]interface{}, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) {
	db := dao.GetDB()

	localRecipe := &model.FoodRecipe{}
	localRecipe.ID = recipeID
	if err = dao.GetEntity(db, localRecipe); err != nil {
		return err
	}
	valid := dao.StrictMakeMapByStructObject(mapData, localRecipe, ctx.GetUserName())

	txDB , _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()

	if len(valid) > 0 {
		if _, err = dao.UpdateEntityLogically(db, localRecipe, valid, ctx.GetUserName(), nil); err != nil {
			return err
		}
	}
	if len(itemList) > 0 {
		choice := &model.FoodRecipeItemChoice{
			RecipeID: recipeID,
		}
		if _, err = dao.DeleteEntity(db, choice, "RecipeID"); err != nil {
			return err
		}

		recipeItems := &model.FoodRecipeItem{
			RecipeID: recipeID,
		}
		if _, err = dao.DeleteEntity(db, recipeItems, "RecipeID"); err != nil {
			return err
		}
	}
	if len(stepList) > 0 {
		recipeSteps := &model.FoodRecipeStep{
			RecipeID: recipeID,
		}
		if _, err = dao.DeleteEntity(db, recipeSteps, "RecipeID"); err != nil {
			return err
		}
	}
	if err = updateFoodRecipeItemAndStep(ctx, db, recipeID, itemList, stepList); err != nil {
		return err
	}
	dao.Commit(db, txDB)

	tryRegisterDataRes4Recipe(ctx, localRecipe.Name, utils.Interface2String(valid["img"]), stepList)
	return err
}

func tryRegisterDataRes4Recipe(ctx *jxcontext.Context, name, mainImg string, stepList []*FoodRecipeStepParam) (err error) {
	imgList := []string{}
	if mainImg != "" {
		imgList = append(imgList, mainImg)
	}
	for _, v := range stepList {
		if v.Img != "" {
			imgList = append(imgList, v.Img)
		}
	}
	errList := errlist.New()
	for _, v := range imgList {
		_, err := datares.TryRegisterDataResource(ctx, name, v, model.ImgTypeLocal, true)
		errList.AddErr(err)
	}
	return errList.GetErrListAsOne()
}

func QueryFoodRecipes(ctx *jxcontext.Context, keyword, authorID string, skuIDs []int, offset, pageSize int) (recipeInfo *model.PagedInfo, err error) {
	_, userID := ctx.GetMobileAndUserID()
	recipeList, totalCount, err := dao.QueryFoodRecipes(dao.GetDB(), keyword, 0, authorID, userID, skuIDs, offset, pageSize)
	if err == nil {
		recipeInfo = &model.PagedInfo{
			TotalCount: totalCount,
			Data:       recipeList,
		}
	}
	return recipeInfo, err
}

func GetRecommendFoodRecipes(ctx *jxcontext.Context, keyword string, offset, pageSize int) (recipeInfo *model.PagedInfo, err error) {
	_, userID := ctx.GetMobileAndUserID()
	recipeList, totalCount, err := dao.GetRecommendFoodRecipes(dao.GetDB(), keyword, userID, offset, pageSize)
	if err == nil {
		recipeInfo = &model.PagedInfo{
			TotalCount: totalCount,
			Data:       recipeList,
		}
	}
	return recipeInfo, err
}

func GetRecipeDetail(ctx *jxcontext.Context, recipeID int) (recipeDetail *FoodRecipeDetail, err error) {
	_, userID := ctx.GetMobileAndUserID()
	db := dao.GetDB()
	recipeList, _, err := dao.QueryFoodRecipes(db, "", recipeID, "", userID, nil, 0, 0)
	if err != nil {
		return nil, err
	}
	if len(recipeList) == 0 {
		return nil, fmt.Errorf("找不到菜谱:%d", recipeID)
	}

	itemList, err := dao.QueryFoodRecipesItems(db, recipeID)
	if err != nil {
		return nil, err
	}

	itemChoiceList, err := dao.QueryFoodRecipesItemChoices(db, recipeID)
	if err != nil {
		return nil, err
	}
	choiceMap := make(map[int8][]*dao.FoodRecipeItemChoiceExt)
	for _, v := range itemChoiceList {
		v.SkuName = jxutils.ComposeSkuNameOriginal(v.Prefix, v.SkuNameName, v.Comment, v.Unit, v.SpecQuality, v.SpecUnit, 0)
		choiceMap[v.Index] = append(choiceMap[v.Index], v)
	}

	stepList, err := dao.QueryFoodRecipesSteps(db, recipeID)
	if err != nil {
		return nil, err
	}

	recipeDetail = &FoodRecipeDetail{
		FoodRecipeWithAction: *recipeList[0],
		StepList:             stepList,
	}
	for _, v := range itemList {
		recipeDetail.ItemList = append(recipeDetail.ItemList, &FoodRecipeItem{
			FoodRecipeItem: *v,
			ItemChoiceList: choiceMap[v.Index],
		})
	}
	return recipeDetail, err
}

func VoteFoodRecipe(ctx *jxcontext.Context, recipeID, voteType int) (err error) {
	_, userID := ctx.GetMobileAndUserID()
	if userID == "" {
		return fmt.Errorf("内部错误，找不到用户")
	}

	db := dao.GetDB()
	recipeList, _, err := dao.QueryFoodRecipes(db, "", recipeID, "", userID, nil, 0, 0)
	if err != nil {
		return err
	}
	if len(recipeList) == 0 {
		return fmt.Errorf("找不到菜谱:%d", recipeID)
	}
	recipe := &recipeList[0].FoodRecipe

	txDB , _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()

	recipeUser := &model.FoodRecipeUser{
		RecipeID: recipeID,
		UserID:   userID,
	}
	if err = dao.GetEntity(db, recipeUser, "RecipeID", "UserID"); err != nil {
		if !dao.IsNoRowsError(err) {
			return err
		}
	}

	actionMask := int8(0)
	if voteType > 0 {
		recipe.UpvoteCount++
		actionMask = model.RecipeActionUpvote
	} else if voteType < 0 {
		recipe.DownvoteCount++
		actionMask = model.RecipeActionDownvote
	}
	if recipeUser.ActionType&model.RecipeActionUpvote != 0 {
		recipe.UpvoteCount--
	} else if recipeUser.ActionType&model.RecipeActionDownvote != 0 {
		recipe.DownvoteCount--
	}

	if recipeUser.ActionType&actionMask != 0 || (recipeUser.ActionType&(model.RecipeActionUpvote|model.RecipeActionDownvote) == 0 && voteType == 0) {
		return fmt.Errorf("已经做过此操作了")
	}

	recipeUser.ActionType = (recipeUser.ActionType & ^(model.RecipeActionUpvote | model.RecipeActionDownvote)) | actionMask
	if recipeUser.ID == 0 {
		dao.WrapAddIDCULDEntity(recipeUser, ctx.GetUserName())
		err = dao.CreateEntity(db, recipeUser)
	} else {
		recipeUser.LastOperator = ctx.GetUserName()
		_, err = dao.UpdateEntity(db, recipeUser)
	}
	if err != nil {
		return err
	}
	_, err = dao.UpdateEntity(db, recipe, "UpvoteCount", "DownvoteCount")
	if err != nil {
		return err
	}

	dao.Commit(db, txDB)
	return err
}

func DeleteRecipes(ctx *jxcontext.Context, recipeIDs []int) (num int64, err error) {
	db := dao.GetDB()
	txDB , _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()
	for _, recipeID := range recipeIDs {
		recipe := &model.FoodRecipe{}
		recipe.ID = recipeID
		num2, err2 := dao.DeleteEntityLogically(db, recipe, nil, ctx.GetUserName(), nil)
		if err = err2; err != nil {
			return 0, err
		}
		num += num2
	}
	dao.Commit(db, txDB)
	return 0, err
}
