package implments

import (
	baseDal "LAPP_ACURA_MOM_BACKEND/dao/base"
	meDal "LAPP_ACURA_MOM_BACKEND/dao/me"
	"LAPP_ACURA_MOM_BACKEND/global"
	"LAPP_ACURA_MOM_BACKEND/grmi"
	baseMeta "LAPP_ACURA_MOM_BACKEND/meta/base"
	meMeta "LAPP_ACURA_MOM_BACKEND/meta/me"
	baseModel "LAPP_ACURA_MOM_BACKEND/models/base"
	meModel "LAPP_ACURA_MOM_BACKEND/models/me"
	model "LAPP_ACURA_MOM_BACKEND/models/om"
	"github.com/go-xorm/xorm"
	"strconv"
	"strings"
)

// GenerateProductCraft 生成总成工艺数据
func (impl *SerialOrderServiceImplement) GenerateProductCraft(user *global.User, session *xorm.Session, serialOrder *model.SerialOrder) error {
	var (
		productId  = serialOrder.ArtId
		projectId  = serialOrder.ProjectId
		workLineId = serialOrder.UsedResourceId
	)
	workLineProcessDao := baseDal.NewWorkLineProcessLstDAO(session, user.PlantNr, user.UserId)
	operationDao := baseDal.NewOperationDAO(session, user.PlantNr, user.UserId)
	articleWithAttrDao := baseDal.NewArticleAtcodLstDAO(session, user.PlantNr, user.UserId)
	scanRuleDao := baseDal.NewScanRuleInfoDAO(session, user.PlantNr, user.UserId)
	guideDao := baseDal.NewGuideRuleInfoDAO(session, user.PlantNr, user.UserId)
	bomHeadDao := meDal.NewBomHeadDAO(session, user.PlantNr, user.UserId)
	labelTemplateDetailDao := baseDal.NewLabelTemplateDetailDAO(session, user.PlantNr, user.UserId)
	articleDao := baseDal.NewArticleDAO(session, user.PlantNr, user.UserId)
	printRuleDao := baseDal.NewPrintBasicRuleInfoDAO(session, user.PlantNr, user.UserId)
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	shipableAUConfigDao := meDal.NewShipableAUConfigDAO(session, user.PlantNr, user.UserId)
	projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	bomLstDao := meDal.NewBomLstDAO(session, user.PlantNr, user.UserId)
	// 获取所有的标签打印模板 并组建MAP
	labelTemplateLi, err := labelTemplateDetailDao.SelectAllTemplate()
	if err != nil {
		return grmi.NewBusinessError("查询标签模板失败,error:" + err.Error())
	}
	labelTemplateMap := make(map[string]*baseModel.LabelTemplateHead)
	for _, template := range labelTemplateLi {
		innerTemplate := template
		labelTemplateMap[template.LabelTemplateId] = &innerTemplate
	}
	project, err := projectDao.SelectOne(projectId)
	if err != nil {
		return grmi.NewBusinessError("查询项目失败, 错误:" + err.Error())
	}
	if project == nil {
		return grmi.NewBusinessError("工单归属项目不存在, 错误:" + err.Error())
	}
	product, err := productDao.SelectOne(productId, projectId)
	if err != nil {
		return grmi.NewBusinessError("查询产品总成数据失败, error:" + err.Error())
	}
	if product == nil {
		return grmi.NewBusinessError("产品总成不存在, 总成ID:" + productId)
	}
	article, err := articleDao.SelectOne(productId, product.ProjectId)
	if err != nil {
		return grmi.NewBusinessError("查询工单总成失败, error:" + err.Error())
	}
	if article == nil {
		return grmi.NewBusinessError("工单总成不存在, 总成ID:" + productId)
	}
	bomHead, err := bomHeadDao.SelectOne(productId)
	if err != nil {
		return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error())
	}
	if bomHead == nil {
		return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + productId)
	}
	var Me meModel.BomLst
	Me.PlantNr = user.PlantNr
	Me.BomId = bomHead.BomId
	bomList, err := Me.SetMenu(bomHead.MatNr)
	if err != nil {
		return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error())
	}
	if len(bomList) == 0 {
		return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId)
	}
	bomHead.BomLstLi = bomList
	// 获取关联的所有零件ID
	artMap := impl.ExtractArtId(bomHead)
	artIdLi := make([]string, 0, len(artMap))
	for innerArtId := range artMap {
		artIdLi = append(artIdLi, innerArtId)
	}
	artIdLi = append(artIdLi, productId)
	allArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(artIdLi, projectId)
	if err != nil {
		return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
	}
	// 查询所有使用到的工序
	usedPoLi, err := workLineProcessDao.SelectProcessByWorkLine(workLineId)
	if err != nil {
		return grmi.NewBusinessError("获取产线关联的工序失败, error:" + err.Error())
	}
	if len(usedPoLi) == 0 {
		return grmi.NewBusinessError("该产线未关联工序, 产线ID:" + workLineId)
	}
	// 拆分预装子工单
	// 1. 将所有工序数据生成MAP
	// 2. 从后往前查 如果有shipableAU标识 代表需要生成工单 需要查询shipableAUConfig数据表中该po的配置
	// 3. 生成工单有两种规则 一种是总装线 主工单 一种是预装线 子工单
	bomLstLi, err := bomLstDao.Select([]grmi.Predicate{
		meMeta.BomLst_BomId.NewPredicate(grmi.Equal, productId),
		meMeta.BomLst_ShipableAU.NewPredicate(grmi.Equal, true),
	}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询BOM子项数据失败, 错误:" + err.Error())
	}
	poDataMap := make(map[string]baseModel.Process)
	allPoIdInterfaceLi := make([]interface{}, 0, len(usedPoLi))

	for _, po := range usedPoLi {
		poDataMap[po.PO] = po
		allPoIdInterfaceLi = append(allPoIdInterfaceLi, po.PO)
	}
	sfpUsedPoMap := make(map[string]interface{})
	orderPoMap := make(map[string]*model.OrderAUPoData)
	for _, bomLst := range bomLstLi {
		sfp, err := articleDao.SelectOne(bomLst.CmatNr, projectId)
		if err != nil {
			return grmi.NewBusinessError("查询物料失败, 错误:" + err.Error())
		}
		if sfp == nil {
			return grmi.NewBusinessError("半成品物料不存在, 物料ID:" + bomLst.CmatNr)
		}
		auConfig, err := shipableAUConfigDao.SelectOne(bomLst.CmatNr)
		if err != nil {
			return grmi.NewBusinessError("查询半成品配置数据失败, 错误:" + err.Error())
		}
		if auConfig == nil {
			return grmi.NewBusinessError("半成品配置不存在， 物料ID:" + bomLst.CmatNr)
		}

		sfpBomHead, err := bomHeadDao.SelectOne(bomLst.CmatNr)
		if err != nil {
			return grmi.NewBusinessError("查询物料BOM失败, error:" + err.Error())
		}
		if sfpBomHead == nil {
			return grmi.NewBusinessError("不存在对应的物料BOM, 物料ID:" + bomLst.CmatNr)
		}
		var sfpMe meModel.BomLst
		sfpMe.PlantNr = user.PlantNr
		sfpMe.BomId = sfpBomHead.BomId
		sfpBomLstLi, err := sfpMe.SetMenu(sfpBomHead.MatNr)
		if err != nil {
			return grmi.NewBusinessError("获取BOM细则失败, error:" + err.Error())
		}
		if len(sfpBomLstLi) == 0 {
			return grmi.NewBusinessError("未获取到BOM细则, 物料ID:" + bomHead.BomId)
		}
		sfpBomHead.BomLstLi = sfpBomLstLi
		// 获取关联的所有零件ID
		sfpArtMap := impl.ExtractArtId(sfpBomHead)
		sfpArtIdLi := make([]string, 0, len(sfpArtMap))
		for innerArtId := range sfpArtMap {
			sfpArtIdLi = append(sfpArtIdLi, innerArtId)
		}
		sfpArtIdLi = append(sfpArtIdLi, sfp.ArtId)
		sfpAllArticleAttributeLi, err := articleWithAttrDao.SelectWithArticleByArtIdLi(sfpArtIdLi, projectId)
		if err != nil {
			return grmi.NewBusinessError("查询半成品关联所有属性失败, 错误:" + err.Error())
		}
		auPoData, exist := poDataMap[auConfig.AuPo]
		if !exist {
			return grmi.NewBusinessError("半成品指定工序不存在于总工单的工序序列中, 半成品ID:" + sfp.ArtId)
		}
		// 根据该工序查询所有后序
		poDataLi := impl.GetOrderPOLi(auPoData, poDataMap)
		// 该工单用到的所有生产工序
		poDataLi = append(poDataLi, auPoData)
		// 查询这个工单需要用到的所有工艺数据
		poIdLi := make([]string, 0, len(poDataLi))
		for _, innerPoData := range poDataLi {
			sfpUsedPoMap[innerPoData.PO] = nil
			poIdLi = append(poIdLi, innerPoData.PO)
		}
		opLi, err := operationDao.GetMeOperation(sfpArtIdLi, projectId, poIdLi, workLineId)
		if err != nil {
			return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
		}
		if len(opLi) == 0 {
			return grmi.NewBusinessError("未获取到工艺数据")
		}
		// 所有需要生成工单工序绑定的数据
		orderPoMap[bomLst.CmatNr] = &model.OrderAUPoData{
			Config:         auConfig,
			MeOperationLi:  opLi,
			IsMainOrder:    false,
			SFP:            sfp,
			AllAttributeLi: sfpAllArticleAttributeLi,
			ArtRelMap:      sfpArtMap,
		}
	}
	mainOrderPOIdLi := make([]string, 0)
	for _, usedPoData := range usedPoLi {
		_, exist := sfpUsedPoMap[usedPoData.PO]
		if !exist {
			mainOrderPOIdLi = append(mainOrderPOIdLi, usedPoData.PO)
		}
	}
	if len(mainOrderPOIdLi) == 0 {
		return grmi.NewBusinessError("主工单无生产所需工序")
	}
	opLi, err := operationDao.GetMeOperation(artIdLi, projectId, mainOrderPOIdLi, workLineId)
	if err != nil {
		return grmi.NewBusinessError("获取工艺操作失败,error:" + err.Error())
	}
	if len(opLi) == 0 {
		return grmi.NewBusinessError("未获取到工艺数据")
	}

	// 所有需要生成工单工序绑定的数据
	orderPoMap[productId] = &model.OrderAUPoData{
		Config:         nil,
		MeOperationLi:  opLi,
		IsMainOrder:    true,
		SFP:            nil,
		AllAttributeLi: allArticleAttributeLi,
		ArtRelMap:      artMap,
	}

	// 获取所有使用工序的扫码校验规则 组建MAP数据结构
	scanRuleLi, err := scanRuleDao.Select([]grmi.Predicate{baseMeta.ScanRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.ScanRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询扫码校验条码失败, error:" + err.Error())
	}
	scanRuleMap := make(map[int]baseModel.ScanRuleInfo)
	for _, scanRule := range scanRuleLi {
		scanRuleMap[scanRule.OperationId] = scanRule
	}
	// 获取所有使用工序指导操作的规则 组建MAP数据结构
	guideRuleLi, err := guideDao.Select([]grmi.Predicate{baseMeta.GuideRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.GuideRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, nil)
	if err != nil {
		return grmi.NewBusinessError("查询指导操作规则失败, error:" + err.Error())
	}
	guideRuleMap := make(map[int]baseModel.GuideRuleInfo, len(guideRuleLi))
	for _, guideRule := range guideRuleLi {
		guideRuleMap[guideRule.OperationId] = guideRule
	}
	// 获取所有使用工序扫码校验规则 组建MAP数据结构
	printRuleLi, err := printRuleDao.Select([]grmi.Predicate{baseMeta.PrintBasicRuleInfo_ProjectId.NewPredicate(grmi.Equal, projectId), baseMeta.PrintBasicRuleInfo_PO.NewPredicate(grmi.Include, allPoIdInterfaceLi...)}, []grmi.Field{baseMeta.PrintBasicRuleInfo_OperationId})
	printRuleMap := make(map[int][]baseModel.PrintBasicRuleInfo)
	for _, data := range printRuleLi {
		_, exist := printRuleMap[data.OperationId]
		if !exist {
			printRuleMap[data.OperationId] = make([]baseModel.PrintBasicRuleInfo, 0)
		}
		printRuleMap[data.OperationId] = append(printRuleMap[data.OperationId], data)
	}
	// 获取所有物料属性关联关系
	attributeLi, err := articleWithAttrDao.SelectAllWithAttrValue()
	if err != nil {
		return grmi.NewBusinessError("查询零件属性失败,error:" + err.Error())
	}
	attributeMap := make(map[int][]baseModel.ArticleWithAttrValue)
	for _, data := range attributeLi {
		_, exist := attributeMap[data.ArticleAtcodLst.AttrCode]
		if !exist {
			attributeMap[data.ArticleAtcodLst.AttrCode] = make([]baseModel.ArticleWithAttrValue, 0)
		}
		attributeMap[data.ArticleAtcodLst.AttrCode] = append(attributeMap[data.ArticleAtcodLst.AttrCode], data)
	}
	// 生成序列工单对应的工艺数据
	allocateMap := make(map[string][]baseModel.ArtQtyAllocate)
	checkRuleMap := make(map[string]baseModel.ArtCheckRule)
	opcBasicMap := make(map[int][]baseModel.OPCBasicComInfo)
	twMap := make(map[int][]baseModel.TightenWrenchInfo)
	planDate := serialOrder.PlanStartDate.Restore().Format(grmi.DateOutFormat)
	schedKey := serialOrder.SchedKey
	var usedArticle *baseModel.Article
	var usedSerialOrder *model.SerialOrder

	for _, orderOPData := range orderPoMap {
		var serialOrderId string
		if !orderOPData.IsMainOrder {
			usedArticle = orderOPData.SFP
			if orderOPData.Config.BarCodeRule == baseModel.BARCODE_RULE_ACURA_PRE {

				//number, err := snrDao.GetNextSnr(project.PreOrderSnr)
				//if err != nil {
				//	return grmi.NewBusinessError("获取流水号失败，错误:" + err.Error())
				//}

				serialOrderId = strings.Replace(orderOPData.SFP.ArtId, "-", "", -1) + "$date$" + "$serialNumber$"
			} else {
				return grmi.NewBusinessError("不支持的子工单条码生成规则，物料Id:" + orderOPData.SFP.ArtId)
			}
			sfpSerialOrder := model.SerialOrder{
				SerialOrderId:    serialOrderId,
				ArtId:            orderOPData.SFP.ArtId,
				ProjectId:        projectId,
				Descr:            orderOPData.SFP.Descr1,
				CustOrderId:      serialOrder.CustOrderId,
				LastPieceInBatch: serialOrder.LastPieceInBatch,
				PreSchedKey:      schedKey,
				SchedKey:         schedKey,
				OrderType:        baseModel.ORDER_TYPE_PRE,
				OrderType1:       serialOrder.OrderType1,
				OrderType2:       serialOrder.OrderType2,
				PlanResourceId:   serialOrder.PlanResourceId,
				UsedResourceId:   serialOrder.UsedResourceId,
				PlanQty:          1,
				QtyUomId:         serialOrder.QtyUomId,
				OpTimePerItem:    serialOrder.OpTimePerItem,
				TimeUomId:        serialOrder.TimeUomId,
				PlanStartDate:    serialOrder.PlanStartDate,
				PlanEndDate:      serialOrder.PlanEndDate,
				SetupStartTime:   serialOrder.SetupStartTime,
				SetupEndTime:     serialOrder.SetupEndTime,
				PlanStartTime:    serialOrder.PlanStartTime,
				PlanEndTime:      serialOrder.PlanEndTime,
				PickingFlag:      orderOPData.SFP.PickingFlag,
				MainOrderId:      serialOrder.SerialOrderId,
				SyncKey:          serialOrder.SyncKey,
			}
			usedSerialOrder = &sfpSerialOrder
			usedSerialOrder.SerialOrderStatus = model.SerialOrderStatus{
				SerialOrderId: serialOrderId,
				Status:        baseModel.WO_STATUS_RELEASED,
			}
		} else {
			usedArticle = article
			serialOrderId = serialOrder.SerialOrderId
			usedSerialOrder = serialOrder
		}
		var PO string
		opPos := 1
		for _, op := range orderOPData.MeOperationLi {
			// 生成工序操作状态表
			if PO != op.Process.PO {
				PO = op.Process.PO
				serialOrderWorkPlace := model.SerialOrderPOLst{
					SerialOrderId: serialOrderId,
					PO:            PO,
					Status:        baseModel.WO_STATUS_RELEASED,
					HasDependOn:   op.Process.HasDependOn,
				}
				usedSerialOrder.SerialOrderPOLstLi = append(usedSerialOrder.SerialOrderPOLstLi, serialOrderWorkPlace)
			}
			serialOrderStep := model.SerialOrderStepLst{
				PO:            op.Process.PO,
				OperationId:   op.OperationId,
				SerialOrderId: serialOrderId,
				StepNo:        op.Operation.StepNo,
				Pos:           opPos,
				SubArtId:      op.ArticleAtcodLst.ArtId,
				StepDesc:      op.Operation.StepDesc,
				AttrCode:      op.ArticleAtcodLst.AttrCode,
				AttrValue:     op.ArticleAtcodLst.AttrValue,
				StepType:      op.Operation.StepType,
				Status:        baseModel.WO_STATUS_RELEASED,
			}
			opPos++
			usedSerialOrder.SerialOrderStepLstLi = append(usedSerialOrder.SerialOrderStepLstLi, serialOrderStep)
			if serialOrderStep.StepType == baseModel.STEP_CHECK_RULE { // 生成扫码校验的工艺数据
				scanRule, exist := scanRuleMap[serialOrderStep.OperationId]
				if !exist {
					return grmi.NewBusinessError("未获取到扫描校验详细规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
				}

				checkRuleMapPointer, allocateMapPointer, opLi, err := impl.ScanCodeCheck(user, session, checkRuleMap, allocateMap, orderOPData.ArtRelMap, &scanRule, &op, &serialOrderStep, op.Operation.ActivateInBKStation)
				if err != nil {
					return err
				}
				checkRuleMap = *checkRuleMapPointer
				allocateMap = *allocateMapPointer
				usedSerialOrder.SerialOrderOPDetailLi = append(usedSerialOrder.SerialOrderOPDetailLi, opLi...)
			} else if serialOrderStep.StepType == baseModel.STEP_OPC_RULE { // 生成OPC通讯的工艺数据
				opcBasicMapPointer, opLi, err := impl.OPCCommunication(user, session, opcBasicMap, &serialOrderStep, attributeMap, artMap, op.Operation.ActivateInBKStation)
				if err != nil {
					return err
				}
				opcBasicMap = *opcBasicMapPointer
				usedSerialOrder.SerialOrderOPDetailLi = append(usedSerialOrder.SerialOrderOPDetailLi, opLi...)
			} else if serialOrderStep.StepType == baseModel.STEP_TW_RULE { // 生成开发协议的工艺数据
				twMapPointer, opLi, err := impl.TightenWrenchInfo(user, session, twMap, &serialOrderStep, op.Operation.ActivateInBKStation, usedArticle)
				if err != nil {
					return err
				}
				twMap = *twMapPointer
				usedSerialOrder.SerialOrderOPDetailLi = append(usedSerialOrder.SerialOrderOPDetailLi, opLi...)
			} else if serialOrderStep.StepType == baseModel.STEP_GUIDE_RULE {
				guideRule, exist := guideRuleMap[serialOrderStep.OperationId]
				if !exist {
					return grmi.NewBusinessError("未获取到指导操作的详细规则")
				}
				op := impl.GuideRuleInfo(&guideRule, &serialOrderStep, op.Operation.ActivateInBKStation)
				usedSerialOrder.SerialOrderOPDetailLi = append(usedSerialOrder.SerialOrderOPDetailLi, op)

			} else if serialOrderStep.StepType == baseModel.STEP_PRINT_RULE {
				ruleLi, exist := printRuleMap[serialOrderStep.OperationId]
				if !exist {
					return grmi.NewBusinessError("未获取到打印规则, 工艺ID:" + strconv.Itoa(serialOrderStep.OperationId))
				}

				generateRuleLi := make([]baseModel.PrintBasicRuleInfo, 0)
				for _, rule := range ruleLi {
					if rule.Action == baseModel.PRINT_RULE_ACTION_UPDATE {
						generateRuleLi = append(generateRuleLi, rule)
					}
				}
				if len(generateRuleLi) > 1 {
					return grmi.NewBusinessError("改工步匹配到超过1条生成打印标识的打印规则")
				}
				var generateRule *baseModel.PrintBasicRuleInfo
				var printTemplate *baseModel.LabelTemplateHead
				if len(generateRuleLi) == 1 {
					generateRule = &generateRuleLi[0]
					var templateId string
					if generateRule.MainCodeName == "Product" {
						templateId = product.PrintTemplateId
					} else {
						templateId = generateRule.TemplateId
					}
					printTemplate, exist = labelTemplateMap[templateId]
					if !exist {
						return grmi.NewBusinessError("存在单据打印规则中配置的标签模板, 模板ID:" + generateRule.TemplateId)
					}
				}
				var printUsedArticle *baseModel.Article
				if op.Operation.AttrCode == -1 {
					printUsedArticle = usedArticle
				} else {
					for _, articleAttribute := range orderOPData.AllAttributeLi {
						if op.Operation.AttrCode == articleAttribute.AttrCode && op.Operation.AttrValue == articleAttribute.AttrValue {
							printUsedArticle = &articleAttribute.Article
							break
						}
					}
				}
				if printUsedArticle == nil {
					return grmi.NewBusinessError("单据打印规则没有获取到需要的物料数据, 工艺Id:" + strconv.Itoa(op.Operation.OperationId))
				}
				op, printHeadLi, printDetailLi := impl.PrintRuleInfo(&serialOrderStep, generateRule, printTemplate, printUsedArticle, op.Operation.ActivateInBKStation, planDate)
				usedSerialOrder.SerialOrderOPDetailLi = append(usedSerialOrder.SerialOrderOPDetailLi, op)
				usedSerialOrder.LabelHeadLi = append(usedSerialOrder.LabelHeadLi, printHeadLi...)
				usedSerialOrder.LabelDetailLi = append(usedSerialOrder.LabelDetailLi, printDetailLi...)
			} else {
				return grmi.NewBusinessError("获取到的工艺数据系统暂不支持该操作类型:" + serialOrderStep.StepType)
			}
		}
		if !orderOPData.IsMainOrder {
			serialOrder.PreOrderLi = append(serialOrder.PreOrderLi, *usedSerialOrder)
		}
	}

	return nil
}
