package api

import (
	"box-cost/db/model"
	"box-cost/db/repo"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"time"

	"github.com/gin-gonic/gin"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// StageRequest 用于添加和更新stage的请求结构
type StageRequest struct {
	PlanId string `json:"planId"`
	// PackId  string                  `json:"packId"`
	CompId  string                  `json:"componentId"`
	StageId string                  `json:"stageId,omitempty"`
	Stages  []*model.ComponentStage `json:"stages"`
}

// 更新计划和部件的价格
func updatePrices(c *gin.Context, db *mongo.Collection, planId primitive.ObjectID, compId string) error {
	// 1. 获取计划详情
	var plan model.ProductPlan
	err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&plan)
	if err != nil {
		return err
	}

	// 2. 遍历所有组件,找到目标组件并重新计算价格
	var totalPrice float64 = 0
	for _, comp := range plan.Pack.Components {
		compPrice := 0.0
		// 计算组件的所有stage总价
		for _, stage := range comp.Stages {
			stagePrice := stage.OrderPrice * float64(stage.OrderCount)
			compPrice += stagePrice
		}
		// 截断组件总价到两位小数
		_compPrice := math.Trunc(compPrice*1000) / 1000
		// 更新组件总价
		if comp.Id == compId {
			_, err = db.UpdateOne(c.Request.Context(),
				bson.M{"_id": planId, "pack.components.id": compId},
				bson.M{"$set": bson.M{"pack.components.$.totalPrice": _compPrice}})
			if err != nil {
				return err
			}
		}
		totalPrice += compPrice
	}

	// 3. 更新计划总价(截断到两位小数)
	totalPrice = math.Trunc(totalPrice*1000) / 1000
	_, err = db.UpdateOne(c.Request.Context(),
		bson.M{"_id": planId},
		bson.M{"$set": bson.M{"totalPrice": &totalPrice}})
	return err
}

// 增加stage
// 根据planId packId compentId定位到到计划中的compent
// 注意更新计划价格 组件价格
func AddStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	var req StageRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		return nil, err
	}

	planId, err := primitive.ObjectIDFromHex(req.PlanId)
	if err != nil {
		return nil, err
	}

	// 更新数据库,添加多个stage
	update := bson.M{
		"$push": bson.M{
			"pack.components.$[components].stages": bson.M{
				"$each": req.Stages,
			},
		},
		"$set": bson.M{
			"updateTime": time.Now(),
		},
	}
	arrayFilters := []interface{}{
		bson.M{"components.id": req.CompId},
	}

	opts := options.Update().SetArrayFilters(options.ArrayFilters{
		Filters: arrayFilters,
	})
	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)

	result, err := db.UpdateOne(c.Request.Context(),
		bson.M{"_id": planId},
		update,
		opts,
	)

	if err != nil {
		return nil, err
	}

	// 更新价格
	err = updatePrices(c, db, planId, req.CompId)
	if err != nil {
		return nil, err
	}

	_, err = AddPlanHistory(c, apictx, req.PlanId)
	if err != nil {
		fmt.Println("记录历史失败:", err)
	}
	return result, nil
}

// 删除stage
// 根据planId packId compentId stageId定位到到计划中的stage
// 注意更新计划价格 组件价格
func DeleteStage(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	var req StageRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		return nil, err
	}

	planId, err := primitive.ObjectIDFromHex(req.PlanId)
	if err != nil {
		return nil, err
	}
	if req.StageId == "" || req.CompId == "" {
		return nil, errors.New("stageId and compId are required")
	}

	// 更新数据库
	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
	update := bson.M{
		"$pull": bson.M{
			"pack.components.$[components].stages": bson.M{
				"id": req.StageId,
			},
		},
	}
	arrayFilters := []interface{}{
		bson.M{"components.id": req.CompId},
	}

	opts := options.Update().SetArrayFilters(options.ArrayFilters{
		Filters: arrayFilters,
	})

	result, err := db.UpdateOne(c.Request.Context(),
		bson.M{"_id": planId},
		update,
		opts,
	)

	if err != nil {
		return nil, err
	}

	// 更新价格
	err = updatePrices(c, db, planId, req.CompId)
	if err != nil {
		return nil, err
	}

	_, err = AddPlanHistory(c, apictx, req.PlanId)
	if err != nil {
		fmt.Println("记录历史失败:", err)
	}

	return result, nil
}

// 根据planId compentId stageId定位到到计划中的stage
// 注意更新计划价格 组件价格
// 根据planId compentId stageId定位到到计划中的stage
// 注意更新计划价格 组件价格
func updateStage(c *gin.Context, db *mongo.Collection, req *StageRequest) (interface{}, error) {
	if len(req.PlanId) == 0 || len(req.CompId) == 0 || len(req.StageId) == 0 {
		return nil, fmt.Errorf("planId, componentId and stageId are required")
	}

	planId, err := primitive.ObjectIDFromHex(req.PlanId)
	if err != nil {
		return nil, fmt.Errorf("invalid planId: %v", err)
	}

	// 构建更新操作
	update := bson.M{
		"$set": bson.M{
			"pack.components.$[comp].stages.$[stg]": req.Stages[0],
			"updateTime":                            time.Now(),
		},
	}

	// 使用正确的数组过滤器
	arrayFilters := options.ArrayFilters{
		Filters: []interface{}{
			bson.M{"comp.id": req.CompId},
			bson.M{"stg.id": req.StageId},
		},
	}

	// 执行更新
	opts := options.FindOneAndUpdate().
		SetArrayFilters(arrayFilters).
		SetReturnDocument(options.After)

	var updatedDoc bson.M
	err = db.FindOneAndUpdate(
		c.Request.Context(),
		bson.M{"_id": planId},
		update,
		opts,
	).Decode(&updatedDoc)

	if err != nil {
		if err == mongo.ErrNoDocuments {
			// 打印更详细的错误信息
			fmt.Printf("Debug - Failed to update. PlanId: %s, CompId: %s, StageId: %s\n", planId.Hex(), req.CompId, req.StageId)
			return nil, fmt.Errorf("plan or stage not found, planId: %s, compId: %s, stageId: %s", req.PlanId, req.CompId, req.StageId)
		}
		return nil, fmt.Errorf("failed to update stage: %v", err)
	}

	fmt.Printf("Debug - Update successful\n")

	// 更新价格
	err = updatePrices(c, db, planId, req.CompId)
	if err != nil {
		return nil, fmt.Errorf("failed to update prices: %v", err)
	}

	return true, nil
}

// 更新stages
// 根据planId packId compentId stageId定位到到计划中的stage
// 注意更新计划价格 组件价格
func UpdateStages(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	var req StageRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		return nil, err
	}

	// 参数检查
	if len(req.PlanId) == 0 || len(req.CompId) == 0 || len(req.Stages) == 0 {
		return nil, fmt.Errorf("invalid parameters: planId, componentId and stages are required")
	}

	planId, err := primitive.ObjectIDFromHex(req.PlanId)
	if err != nil {
		return nil, fmt.Errorf("invalid planId: %v", err)
	}

	// 获取更新前的数据用于验证
	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
	ctx := c.Request.Context()

	var plan model.ProductPlan
	err = db.FindOne(ctx, bson.M{"_id": planId}).Decode(&plan)
	if err != nil {
		return nil, fmt.Errorf("failed to find plan: %v", err)
	}

	// 验证组件是否存在
	componentExists := false
	if plan.Pack != nil {
		for _, comp := range plan.Pack.Components {
			if comp.Id == req.CompId {
				componentExists = true
				break
			}
		}
	}
	if !componentExists {
		return nil, fmt.Errorf("component %s not found in plan", req.CompId)
	}

	// 准备批量更新操作
	var models []mongo.WriteModel
	now := time.Now()

	for _, stage := range req.Stages {
		if stage.Id == "" {
			continue // 跳过没有ID的stage
		}

		update := bson.M{
			"$set": bson.M{
				"pack.components.$[components].stages.$[stage]": stage,
				"updateTime": now,
			},
		}

		arrayFilters := options.ArrayFilters{
			Filters: []interface{}{
				bson.M{"components.id": req.CompId},
				bson.M{"stage.id": stage.Id},
			},
		}

		model := mongo.NewUpdateOneModel().
			SetFilter(bson.M{"_id": planId}).
			SetUpdate(update).
			SetArrayFilters(arrayFilters)

		models = append(models, model)
	}

	if len(models) == 0 {
		return nil, fmt.Errorf("no valid stages to update")
	}

	// 执行批量更新
	opts := options.BulkWrite().SetOrdered(false) // 允许并行执行
	result, err := db.BulkWrite(c.Request.Context(), models, opts)
	if err != nil {
		return nil, fmt.Errorf("failed to update stages: %v", err)
	}

	// 更新价格
	err = updatePrices(c, db, planId, req.CompId)
	if err != nil {
		return nil, fmt.Errorf("failed to update prices: %v", err)
	}

	// 记录历史
	_, err = AddPlanHistory(c, apictx, req.PlanId)
	if err != nil {
		fmt.Println("记录历史失败:", err)
	}

	return result, nil
}

type UpdatePlanFieldsReq struct {
	PlanId     string `json:"planId"`
	Total      int    `json:"total,omitempty"`
	CreateUser string `json:"createUser,omitempty"`
	Name       string `json:"name,omitempty"`
}

// 单独更新计划字段
func UpdatePlanfileds(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	var req UpdatePlanFieldsReq
	if err := c.ShouldBindJSON(&req); err != nil {
		return nil, err
	}

	planId, _ := primitive.ObjectIDFromHex(req.PlanId)
	if planId.IsZero() {
		return nil, fmt.Errorf("invalid planId")
	}
	result, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, req.PlanId, &model.ProductPlan{
		Total:      req.Total,
		CreateUser: req.CreateUser,
		Name:       req.Name,
		UpdateTime: time.Now(),
	})
	if err != nil {
		return nil, err
	}

	// 记录历史
	_, err = AddPlanHistory(c, apictx, req.PlanId)
	if err != nil {
		fmt.Println("记录历史失败:", err)
	}

	return result, nil
}

// 新增组件
func AddCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Param("id")
	planId, _ := primitive.ObjectIDFromHex(_planId)
	if planId.IsZero() {
		return nil, fmt.Errorf("planId is invalid")
	}

	var component model.PackComponent
	if err := c.ShouldBindJSON(&component); err != nil {
		return nil, err
	}

	// 计算新的总价
	newTotalPrice := component.TotalPrice
	oldPlan := &model.ProductPlan{}
	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
	if err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&oldPlan); err != nil {
		return nil, err
	} else if oldPlan.TotalPrice != nil {
		newTotalPrice += *oldPlan.TotalPrice
	}
	_newTotalPrice := math.Trunc(newTotalPrice*1000) / 1000
	update := bson.M{
		"$push": bson.M{
			"pack.components": component,
		},
		"$set": bson.M{
			"updateTime": time.Now(),
			"totalPrice": _newTotalPrice,
		},
		"$inc": bson.M{
			"pack.compCounts": 1,
		},
	}

	filter := bson.M{"_id": planId}
	result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
	if result.Err() != nil {
		return nil, result.Err()
	}

	_, err := AddPlanHistory(c, apictx, _planId)
	if err != nil {
		fmt.Println("记录历史失败:", err)
	}

	return true, nil
}

func DeleteCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Param("id")
	planId, _ := primitive.ObjectIDFromHex(_planId)
	if planId.IsZero() {
		return nil, fmt.Errorf("planId is invalid")
	}

	componentId := c.Param("componentId")
	if len(componentId) == 0 {
		return nil, fmt.Errorf("componentId is invalid")
	}

	oldPlan := &model.ProductPlan{}
	// Find the component to be deleted and its price
	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
	var componentPrice float64
	if err := db.FindOne(c.Request.Context(), bson.M{"_id": planId}).Decode(&oldPlan); err != nil {
		return nil, err
	} else if oldPlan.Pack != nil {
		for _, comp := range oldPlan.Pack.Components {
			if comp.Id == componentId {
				componentPrice = comp.TotalPrice
				break
			}
		}
	}

	var newTotalPrice float64
	if oldPlan.TotalPrice != nil {
		newTotalPrice = *oldPlan.TotalPrice - componentPrice
		if newTotalPrice < 0 {
			newTotalPrice = 0
		}
	}
	_newTotalPrice := math.Trunc(newTotalPrice*1000) / 1000
	update := bson.M{
		"$pull": bson.M{
			"pack.components": bson.M{"id": componentId},
		},
		"$set": bson.M{
			"updateTime": time.Now(),
			"totalPrice": _newTotalPrice,
		},
		"$inc": bson.M{
			"pack.compCounts": -1,
		},
	}

	filter := bson.M{"_id": planId}
	result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
	if result.Err() != nil {
		return nil, result.Err()
	}

	_, err := AddPlanHistory(c, apictx, _planId)
	if err != nil {
		fmt.Println("记录历史失败:", err)
	}

	return true, nil
}

type UpdateCompnonetReq struct {
	Name   string                  `json:"name,omitempty"`
	Stages []*model.ComponentStage `json:"stages,omitempty"`
}

func UpdateCompnonet(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Param("id")
	planId, _ := primitive.ObjectIDFromHex(_planId)
	if planId.IsZero() {
		return nil, fmt.Errorf("planId is invalid")
	}
	componentId := c.Param("componentId")
	if len(componentId) == 0 {
		return nil, fmt.Errorf("componentId is invalid")
	}

	var nameUpdate UpdateCompnonetReq
	if err := c.ShouldBindJSON(&nameUpdate); err != nil {
		return nil, err
	}

	// 检查是否有需要更新的字段
	if len(nameUpdate.Name) == 0 && len(nameUpdate.Stages) == 0 {
		return nil, fmt.Errorf("no fields to update")
	}

	db := apictx.Svc.Mongo.GetCollection(repo.CollectionProductPlan)
	update := bson.M{
		"$set": bson.M{
			"updateTime": time.Now(),
		},
	}

	if len(nameUpdate.Name) > 0 {
		update["$set"].(bson.M)["pack.components.$.name"] = nameUpdate.Name
	}

	if len(nameUpdate.Stages) > 0 {
		update["$set"].(bson.M)["pack.components.$.stages"] = nameUpdate.Stages
	}

	filter := bson.M{
		"_id":                planId,
		"pack.components.id": componentId,
	}

	result := db.FindOneAndUpdate(c.Request.Context(), filter, update)
	if result.Err() != nil {
		return nil, result.Err()
	}

	// 如果更新了stages,需要更新价格
	if len(nameUpdate.Stages) > 0 {
		err := updatePrices(c, db, planId, componentId)
		if err != nil {
			return nil, fmt.Errorf("failed to update prices: %v", err)
		}
	}

	// 记录历史
	_, err := AddPlanHistory(c, apictx, _planId)
	if err != nil {
		fmt.Println("记录历史失败:", err)
	}

	return true, nil
}

// 记录历史
func AddPlanHistory(c *gin.Context, apictx *ApiSession, planId string, ht ...string) (interface{}, error) {
	htype := "update"
	if len(ht) > 0 {
		htype = ht[0]
	}
	// 获取更新后的数据并记录历史
	newPlan := &model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, newPlan)
	if !found {
		return false, errors.New("数据未找到")
	}
	if err != nil {
		return false, err
	}

	newPlanBytes, _ := json.Marshal(newPlan)
	userId, _ := primitive.ObjectIDFromHex(apictx.User.ID)
	userInfo, err := getUserById(apictx, userId)
	if err != nil {
		return false, err
	}

	history := &model.History{
		Userinfo:   userInfo,
		TargetId:   planId,
		Path:       c.Request.URL.Path,
		Collection: repo.CollectionProductPlan,
		Type:       htype,
		Content:    string(newPlanBytes),
		CreateTime: time.Now(),
	}

	_, err = repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionPlanHistory, history)
	if err != nil {
		return false, err
	}
	return true, nil
}