package utils

import (
	"context"
	"errors"
	"github.com/gogf/gf/database/gdb"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gmeta"
	"youngmini_server/app/dao"
	"youngmini_server/app/model"
)

var OrderProcedureManager = orderProcedureManager{}
type orderProcedureManager struct {

}

// MethodType 拍单方式
type MethodType int
const (
	MethodTypeNo MethodType = iota
	MethodTypeYes
	MethodTypeOther
)

type buySamplesType int

const (
	buySamplesTypeNo buySamplesType = iota + 1
)

type procedureStage int

// 订单执行阶段
const (
	procedureStageWaitForSelect procedureStage = iota + 1 // 等待反选阶段
	procedureStageBuySamples                              // 拍单阶段
	procedureStageDraft                                   // 初稿阶段
	procedureStageArticle                                 // 作品阶段
	procedureStageLinkTest                                // 链接质检阶段
	procedureStageDataTest                                // 数据质检阶段
	procedureStageReturnSamples                           // 拍单样品寄回阶段
	procedureStageComplete                                // 订单完成阶段
)

type procedureState int

const (
	procedureStateNotStart procedureState = iota + 1
	orderStepStateGoingOn
	orderStepStateFail
	orderStepStateSuc
)

// DeductReasonOrderOvertime 扣费原因超期扣费
const DeductReasonOrderOvertime = 1

// 下面的值会出现在"suc_next_step"或"fail_next_step"字段，表示当前步骤成功或失败后进入的下个步骤
// 254 表示任务终止
const orderFinishCode uint = 254
// 255 表示进入订单的下一个stage
const nextStageCode = 255
// 0 表示保持在本步骤不动
const selfStepCode = 0

type OrderLimitAndReduceFeeInfo struct {
	gmeta.Meta `orm:"table:task_base_info"`
	TaskId int `json:"task_id"`
	DraftInfo *model.TaskProcedureDraftInfo `orm:"with:task_base_id=task_id"`
	ArticleInfo *model.TaskProcedureArticleInfo `orm:"with:task_base_id=task_id"`
	DataAndQualityInfo *model.TaskProcedureQualityDataTestInfo `orm:"with:task_base_id=task_id"`
}

func (*orderProcedureManager) setLimitDaysInfoForProcedure(node *model.WorkflowNodeContainer, limitInfo *OrderLimitAndReduceFeeInfo, stage int) {
	if node.ProcedureStage == int(procedureStageDraft) && node.StepInStage == 1  && limitInfo.DraftInfo != nil {
		// 上传初稿天数限制
		node.LimitDays1 = limitInfo.DraftInfo.UploadLimitDay
		// 上传初稿超期扣费率
		node.ReduceFeeRatio1 = limitInfo.DraftInfo.UploadReduceRate
		// 上传初稿天数限制
		node.LimitDays2 = limitInfo.DraftInfo.ReviseLimitDay
		// 上传初稿超期扣费率
		node.ReduceFeeRatio2 = limitInfo.DraftInfo.ReviseReduceRate
	}
	if node.ProcedureStage == int(procedureStageArticle) && node.StepInStage == 1 && limitInfo.ArticleInfo != nil {
		// 上传作品天数限制
		node.LimitDays1 = limitInfo.ArticleInfo.UploadLimitDay
		// 上传作品超期扣费率
		node.ReduceFeeRatio1 = limitInfo.ArticleInfo.UploadReduceRate
		// 上传作品天数限制
		node.LimitDays2 = limitInfo.ArticleInfo.ReviseLimitDay
		// 上传作品超期扣费率
		node.ReduceFeeRatio2 = limitInfo.ArticleInfo.ReviseReduceRate
	}
	if node.ProcedureStage == int(procedureStageLinkTest) && node.StepInStage == 1 && limitInfo.DataAndQualityInfo != nil {
		// 上传发布链接天数限制
		node.LimitDays1 = limitInfo.DataAndQualityInfo.QualityTestLimitDay
		// 上传发布链接超期扣费率
		node.ReduceFeeRatio1 = limitInfo.DataAndQualityInfo.QualityTestReduceRate
		// 上传发布链接天数限制
		node.LimitDays2 = limitInfo.DataAndQualityInfo.QualityReviseLimitDay
		// 上传发布链接超期扣费率
		node.ReduceFeeRatio2 = limitInfo.DataAndQualityInfo.QualityReviseReduseRate
	}
	if node.ProcedureStage == int(procedureStageDataTest) && node.StepInStage == 1 && limitInfo.DataAndQualityInfo != nil {
		// 上传质检链接天数限制
		node.LimitDays1 = limitInfo.DataAndQualityInfo.DataTestLimitDay
		// 上传质检链接超期扣费率
		node.ReduceFeeRatio1 = limitInfo.DataAndQualityInfo.DataTestReduceRate
		// 上传质检链接天数限制
		node.LimitDays2 = limitInfo.DataAndQualityInfo.DataReviceLimitDay
		// 上传质检链接超期扣费率
		node.ReduceFeeRatio2 = limitInfo.DataAndQualityInfo.DataReviceReduceRate
	}
}

//func (*orderProcedureManager)calPrevSortId(curSortId int, )
func (t *orderProcedureManager)fillOrderWorkflowToDatabase(taskId, orderId int, stageList []int, ctx context.Context, tx *gdb.TX) error {
	var orderLimitInfo *OrderLimitAndReduceFeeInfo
	err := g.DB().Model(dao.TaskBaseInfo.Table).WithAll().Where(dao.TaskBaseInfo.Columns.TaskId, taskId).Scan(&orderLimitInfo)
	if err != nil {}

	res, err := g.DB().Model(dao.WorkflowNodeTemplate.Table).
		OrderAsc("procedure_stage, step_in_stage").
		All("procedure_stage in (?)", stageList)
	if err != nil {
		return err
	}

	workflowNodeList := make([]model.WorkflowNodeContainer, 0)

	var sucSortId, failSortId, nextStep uint

	//建立每一个流程的每一步到sortId的索引
	sortIdMap := make(map[uint]map[uint]uint, 0)
	for i := 0; i < res.Len(); i++  {
		curStage := res[i]["procedure_stage"].Uint()
		curStepInStage := res[i]["step_in_stage"].Uint()
		if _, ok := sortIdMap[curStage]; !ok {
			sortIdMap[curStage] = make(map[uint]uint)
		}

		sortIdMap[curStage][curStepInStage] = uint(i + 1)
	}

	for i := 0; i < res.Len(); i++ {
		curStage := res[i]["procedure_stage"].Uint()

		// 将成功后转到的本stage内的step值替换为完成后的sortId
		nextStep = res[i]["suc_next_step"].Uint()
		if nextStep == nextStageCode {
			// 如果是255则下一步为下一个节点
			sucSortId = uint(i + 2)
		} else if nextStep == orderFinishCode {
			// 如果下一步是254，则订单直接终止
			sucSortId = orderFinishCode
		} else if nextStep == selfStepCode {
			// 如果为0则是本节点
			sucSortId = uint(i + 1)
		} else {
			// 否则查找
			v, ok := sortIdMap[curStage][nextStep]
			if !ok {
				panic("not found sort id of success next step")
			}
			sucSortId = v
		}

		// 将失败后转到的本stage内的step值替换为完成后的sortId
		nextStep = res[i]["fail_next_step"].Uint()
		if nextStep == nextStageCode {
			// 如果是255则下一步为下一个节点
			failSortId = uint(i + 2)
		} else if nextStep == orderFinishCode {
			// 如果下一步是254，则订单直接终止
			failSortId = orderFinishCode
		} else if nextStep == selfStepCode {
			// 如果为0则是本节点
			failSortId = uint(i + 1)
		} else {
			// 否则根据已建立的
			v, ok := sortIdMap[curStage][nextStep]
			if !ok {
				panic("not found sort id of failed next step")
			}
			failSortId = v
		}

		node := model.WorkflowNodeContainer{
			NodeNameFirst:       res[i]["node_name_first"].String(),
			NodeNameAfterSecond: res[i]["node_name_after_second"].String(),
			ProcedureStage:      res[i]["procedure_stage"].Int(),
			CurExecutionTimes:   res[i]["cur_execution_times"].Int(),
			MaxExecutionTimes:   res[i]["max_execution_times"].Int(),
			StepInStage:         res[i]["step_in_stage"].Uint(),
			Tip:                 res[i]["tip"].String(),
			SucNextStep:         sucSortId,
			FailNextStep:        failSortId,
			State:               res[i]["state"].Int(),
			StartDate:           gtime.Now(),
			CompleteDate:        nil,
			CompleteUserName:    "",
			LimitDays1:          0,
			ReduceFeeRatio1:     0,
			LimitDays2:          0,
			ReduceFeeRatio2:     0,
			OperateRoleRestrict: res[i]["operate_role_restrict"].Int(),
			SortId:              i + 1,
			OrderId:             orderId,
		}

		// 上传初稿 上传作品 上传链接 上传数据阶段设置限制天数和扣费信息
		t.setLimitDaysInfoForProcedure(&node, orderLimitInfo, node.ProcedureStage)

		workflowNodeList = append(workflowNodeList, node)
	}

	_, err = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Insert(workflowNodeList)
	if err != nil {
		return err
	}

	return nil
}

// GenOrderWorkflowList 生成订单的流程
func (t *orderProcedureManager) GenOrderWorkflowList(taskId, orderId int, ctx context.Context, tx *gdb.TX) error {
	r, err := g.DB().Model(model.TaskProcedureDecisionCondition{}).One(dao.TaskProcedureDecisionCondition.Columns.TaskBaseId, taskId)
	if err != nil {
		return err
	}

	var procedureIdList []int
	// 添加等待反选步骤,必须添加
	procedureIdList = append(procedureIdList, 1)

	// 如果拍单类型不为“不拍单”，则将拍单对应的步骤加入到流程中
	if r[dao.TaskProcedureDecisionCondition.Columns.BuySamplesType].Int() != int(buySamplesTypeNo) {
		procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.BuySamplesStage].Int())
	}

	// 如果需要检查初稿则把检查初稿相关步骤加入任务流程
	if r[dao.TaskProcedureDecisionCondition.Columns.ExamineDraft].Int() != int(MethodTypeNo) {
		procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.ExamineDraftStage].Int())
	}

	// 根据是否审核作品添加相应的步骤
	if r.Map()[dao.TaskProcedureDecisionCondition.Columns.ReviewArticle] != 0 {
		procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.ReviewArticleStage].Int())
	}

	// 添加质检链接步骤id
	procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.QuilityTestStage].Int())
	// 添加数据质检步骤id
	procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.DataTestStage].Int())

	// 根据是否寄回样品添加相应步骤
	//if r[dao.TaskProcedureDecisionCondition.Columns.ReturnSamples].Int() != 0 {
	//	procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.ReturnSamplesStage].Int())
	//}

	procedureIdList = append(procedureIdList, r[dao.TaskProcedureDecisionCondition.Columns.TaskFisnishStage].Int())

	return t.fillOrderWorkflowToDatabase(taskId, orderId, procedureIdList, ctx, tx)
}

// GetOrderWorkFlow 获取订单的所有流程节点
func (*orderProcedureManager)GetOrderWorkFlow(orderId int) (gdb.Result, error) {

	res, err := g.DB().Model(dao.WorkflowNodeContainer.Table).
		OrderAsc(dao.WorkflowNodeContainer.Columns.SortId).
		All(dao.WorkflowNodeContainer.Columns.OrderId, orderId)

	return res, err
}

// handleProcedureOvertimeReduceFee 处理扣费 返回扣费金额和错误
func (*orderProcedureManager)handleProcedureOvertimeReduceFee(procedureNode *model.WorkflowNodeContainer,
	orderInfo *model.OrderInfo, ctx context.Context, tx *gdb.TX) (int64, error) {

	// 天数显示为0表示不限制天数
	if procedureNode.LimitDays1 <= 0 && procedureNode.LimitDays2 <= 0 {
		return 0, nil
	}

	// 订单处于上传初稿 上传作品 上传链接 上传数据阶段时会产生超期扣费
	if procedureNode.ProcedureStage == int(procedureStageDraft) || procedureNode.ProcedureStage == int(procedureStageArticle) ||
		procedureNode.ProcedureStage == int(procedureStageLinkTest) || procedureNode.ProcedureStage == int(procedureStageDataTest) &&
		procedureNode.StepInStage == 1 {

		days := int(procedureNode.CompleteDate.Sub(procedureNode.StartDate).Seconds() / 86400)
		overTime := false
		reduceRatio := 0
		if procedureNode.CurExecutionTimes == 1 {
			if days > procedureNode.LimitDays1 {
				overTime = true
				reduceRatio = procedureNode.ReduceFeeRatio1
			}
		} else {
			if days > procedureNode.LimitDays2 {
				overTime = true
				reduceRatio = procedureNode.ReduceFeeRatio2
			}
		}


		if overTime && reduceRatio > 0{
			rec, err := tx.Ctx(ctx).Model(dao.TaskRecruitTalentLevel.Table).One(dao.TaskRecruitTalentLevel.Columns.TrtId, orderInfo.TaskLevelId)
			if err != nil {
				return 0, err
			}

			// 扣费金额
			deductBoBoCoinValue := int64(rec[dao.TaskRecruitTalentLevel.Columns.RewardRoyalties].Float64() * float64(reduceRatio) * 0.01)
			// 步骤名
			var stageName string
			if procedureNode.CurExecutionTimes <= 2 {
				stageName = procedureNode.NodeNameFirst
			} else {
				stageName = procedureNode.NodeNameAfterSecond
			}

			_, err = tx.Ctx(ctx).Model(dao.BobocoinDeductRecord.Table).Insert(model.BobocoinDeductRecord{
				TalentId:       orderInfo.TalentId,
				BobocoinValue:  deductBoBoCoinValue,
				OrderId:        orderInfo.OrderId,
				ProcedureStage: procedureNode.ProcedureStage,
				Reason:         DeductReasonOrderOvertime,
				CreatedAt:       gtime.Now(),
				TaskName: 		orderInfo.TaskName,
				OperationStage: stageName,
			})
			if err != nil {
				return 0, err
			}

			return  deductBoBoCoinValue, nil
		}
	}

	return 0, nil
}

// OnOperateOrder 根据当前流程节点的操作成功失败状态决定订单下一步流程节点
func (t *orderProcedureManager)OnOperateOrder(orderId int, isSuccess bool, operatorId int, operatorName string, failedReason ...string) error {
	err := g.DB().Transaction(context.Background(), func(ctx context.Context, tx *gdb.TX) error {

		// 获取订单当前状态
		var orderInfo *model.OrderInfo
		err1 := tx.Ctx(ctx).Model(dao.OrderInfo.Table).Where(dao.OrderInfo.Columns.OrderId, orderId).Scan(&orderInfo)
		if err1 != nil {
			return err1
		}

		// 任务结束状态为已结束则禁止操作
		if orderInfo.CompleteStatus > 1 {
			return errors.New("order have completed")
		}

		var curWorkflowNode *model.WorkflowNodeContainer
		// 获取订单当前流程节点
		err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).
			Where("order_id = ? and sort_id = ?", orderInfo.OrderId, orderInfo.OrderStatus).Scan(&curWorkflowNode)
		if err1 != nil {
			return err1
		}

		// 根据成功状态分别进入下一个流程节点
		var nextStep uint
		if isSuccess {
			nextStep = curWorkflowNode.SucNextStep
		} else {
			nextStep = curWorkflowNode.FailNextStep
		}

		if nextStep == orderFinishCode {
			// 获取此步骤对应的结束类型
			rec, err2 := tx.Ctx(ctx).Model(dao.ROrderCompeleteStageToType.Table).
				One("order_procedure_stage = ? and order_procedure_step = ?",
					curWorkflowNode.ProcedureStage, curWorkflowNode.StepInStage)
			if err2 != nil {
				return errors.New("query order complete type failed")
			}

			// 更新当前流程节点的状态
			_, err2 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
				dao.WorkflowNodeContainer.Columns.CompleteDate:     gtime.Now(),
				dao.WorkflowNodeContainer.Columns.CompleteUserName: operatorName,
				dao.WorkflowNodeContainer.Columns.State:            orderStepStateSuc,
			}, dao.WorkflowNodeContainer.Columns.ContainerId, curWorkflowNode.ContainerId)
			if err2 != nil {
				return err2
			}

			// 记录结束类型到订单表
			completeType := rec[dao.ROrderCompeleteStageToType.Columns.CompleteType].Int()
			// 下一步为任务完结则设置任务结束状态为已结束，并设置结束类型
			_, err2 = tx.Ctx(ctx).Model(dao.OrderInfo.Table).Update(g.Map{
				dao.OrderInfo.Columns.CompleteStatus: completeType,
				dao.OrderInfo.Columns.CompleteDate:   gtime.Now(),
			}, dao.OrderInfo.Columns.OrderId, orderId)
			if err2 != nil {
				return err1
			}

			// 如果任务正常结束，则将任务稿费写入卜卜币收入表
			if completeType == int(OrderCompleteTypeNormal) {

				_, err2 = tx.Ctx(ctx).Model(dao.BobocoinIncomeRecord.Table).Insert(model.BobocoinIncomeRecord{
					BobocoinValue:  orderInfo.SettleAmount,
					OrderId:        orderId,
					RecruitLevelId: orderInfo.TaskLevelId,
					TalentId:       orderInfo.TalentId,
					CompleteDate:   gtime.Now(),
					TaskName:       orderInfo.TaskName,
				})
				if err2 != nil {
					return err1
				}
			}

			return nil
		}

		// 获取订单下一个流程节点
		var nextWorkflowNode *model.WorkflowNodeContainer
		err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Where("order_id = ? and sort_id = ?", orderId, nextStep).Scan(&nextWorkflowNode)
		if err1 != nil {
			return err1
		}

		var failResStr string
		if isSuccess {
			failResStr = "success"
		} else {
			if failedReason == nil {
				failResStr = "failed"
			}
			failResStr = failedReason[0]
		}

		if isSuccess {
			// 如果是执行成功，则更新当前节点为完成状态
			_, err2 := tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
				dao.WorkflowNodeContainer.Columns.CompleteDate:     gtime.Now(),
				dao.WorkflowNodeContainer.Columns.CompleteUserName: operatorName,
				dao.WorkflowNodeContainer.Columns.State:            orderStepStateSuc,
				dao.WorkflowNodeContainer.Columns.FailReason:       failResStr,
			}, dao.WorkflowNodeContainer.Columns.ContainerId, curWorkflowNode.ContainerId)

			if err2 != nil {
				return err2
			}

		} else {
			// 执行失败，当前节点状态更新为失败
			_, err2 := tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
				dao.WorkflowNodeContainer.Columns.CompleteDate:     nil,
				dao.WorkflowNodeContainer.Columns.CompleteUserName: nil,
				dao.WorkflowNodeContainer.Columns.State:            orderStepStateFail,
				dao.WorkflowNodeContainer.Columns.FailReason:       failResStr,
			}, dao.WorkflowNodeContainer.Columns.ContainerId, curWorkflowNode.ContainerId)

			if err2 != nil {
				return err2
			}
		}

		// 执行扣费
		deductValue, err1 := t.handleProcedureOvertimeReduceFee(curWorkflowNode, orderInfo, ctx, tx)
		if err1 != nil {
			return err1
		}

		orderSettleAmount := orderInfo.SettleAmount
		if deductValue > 0 {
			orderSettleAmount -= deductValue
		}

		if isSuccess {
			// 如果是成功状态则更新下个节点状态，增加下个节点执行次数
			_, err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
				dao.WorkflowNodeContainer.Columns.StartDate:         gtime.Now(),
				dao.WorkflowNodeContainer.Columns.CompleteDate:      nil,
				dao.WorkflowNodeContainer.Columns.CompleteUserName:  nil,
				dao.WorkflowNodeContainer.Columns.State:             orderStepStateGoingOn,
				dao.WorkflowNodeContainer.Columns.CurExecutionTimes: nextWorkflowNode.CurExecutionTimes + 1,
			}, dao.WorkflowNodeContainer.Columns.ContainerId, nextWorkflowNode.ContainerId)
			if err1 != nil {
				return err1
			}
		} else {
			// 如果是失败状态则更新下个节点状态，增加下个节点执行次数并将失败原因添加到下个节点
			_, err1 = tx.Ctx(ctx).Model(dao.WorkflowNodeContainer.Table).Update(g.Map{
				dao.WorkflowNodeContainer.Columns.StartDate:         gtime.Now(),
				dao.WorkflowNodeContainer.Columns.CompleteDate:      nil,
				dao.WorkflowNodeContainer.Columns.CompleteUserName:  nil,
				dao.WorkflowNodeContainer.Columns.State:             orderStepStateGoingOn,
				dao.WorkflowNodeContainer.Columns.FailReason:        failResStr,
				dao.WorkflowNodeContainer.Columns.CurExecutionTimes: nextWorkflowNode.CurExecutionTimes + 1,
			}, dao.WorkflowNodeContainer.Columns.ContainerId, nextWorkflowNode.ContainerId)
			if err1 != nil {
				return err1
			}
		}

		// 将下一个流程节点sortId记录在订单状态中
		_, err1 = tx.Ctx(ctx).Model(dao.OrderInfo.Table).Update(g.Map{
			dao.OrderInfo.Columns.SettleAmount: orderSettleAmount,
			dao.OrderInfo.Columns.OrderStatus:  nextStep,
		}, dao.OrderInfo.Columns.OrderId, orderId)
		if err1 != nil {
			return err1
		}

		// 将操作记录插入记录表
		_, err1 = tx.Ctx(ctx).Model(dao.OrderStatusRecord.Table).Insert(model.OrderStatusRecord{
			OrderId:     orderId,
			AlterBefore: curWorkflowNode.SortId,
			AlterAfter:  nextWorkflowNode.SortId,
			RecordId:    operatorId,
			RecordName:  operatorName,
			CreatedAt:   gtime.Now(),
		})

		return err1
	})

	return err
}