package schedule

import (
	dal "LAPP_ACURA_MOM_BACKEND/dao/om"
	plnDal "LAPP_ACURA_MOM_BACKEND/dao/pln"
	"LAPP_ACURA_MOM_BACKEND/db"
	"LAPP_ACURA_MOM_BACKEND/grmi"
	common "LAPP_ACURA_MOM_BACKEND/models/base"
	model "LAPP_ACURA_MOM_BACKEND/models/om"
	plnModel "LAPP_ACURA_MOM_BACKEND/models/pln"
	"errors"
	"fmt"
	"github.com/go-xorm/xorm"
	"time"
)

// TaskSrv 调度批次任务对象
type TaskSrv struct {
	TaskId            string
	TaskType          string
	PlantNr           int
	ProjectId         string
	SortKey           int64  // 用于给MES用的排序号
	ArtId             string // 任务产品ID
	Priority          int
	Status            int
	SchedResGrpId     string        // 调度资源组ID
	SchedResId        string        // 调度资源
	FixResource       bool          // 锁定资源
	PlanQty           float64       // 计划数量
	CancelQty         float64       // 取消数量
	ReleasedQty       float64       // 已下达数量 - 每个独立生成序列订单
	FinishedQty       float64       // 已完成数量
	Duration          time.Duration // 任务所需工时
	EarliestStartTime time.Time     // 最早开始时间
	LatestEndTime     time.Time     // 最迟结束时间
	SetupStartTime    time.Time     // 换型开始时间
	SetupEndTime      time.Time     // 换型结束时间
	FixStartTime      bool          // 锁定派车给开始时间
	SchedStartTime    time.Time     // 排程开始时间
	SchedEndTime      time.Time     // 排程结束时间
	ActStartTime      time.Time     // 实际开始时间
	ActEndTime        time.Time     // 实际结束时间
	CustOrder         plnModel.CustOrder
	CustOrderStatus   plnModel.CustOrderStatus
	CustOrderQty      plnModel.CustOrderQty
	SerialTaskArray   []SerialTaskSrv // 批次订单的序列订单
}

// SerialTaskSrv 序列任务对象
type SerialTaskSrv struct {
	TaskId               string
	TaskType             string
	ParentTaskId         string
	PlantNr              int
	ProjectId            string
	Barcode              string // 序列订单条码
	ArtId                string // 任务产品ID
	Status               int
	SchedResId           string        // 调度资源
	PlanQty              float64       // 计划数量
	Duration             time.Duration // 任务所需工时
	EarliestStartTime    time.Time     // 最早开始时间
	LatestEndTime        time.Time     // 最迟结束时间
	SetupStartTime       time.Time     // 换型开始时间
	SetupEndTime         time.Time     // 换型结束时间
	SchedStartTime       time.Time     // 排程开始时间
	SchedEndTime         time.Time     // 排程结束时间
	ActStartTime         time.Time     // 实际开始时间
	ActEndTime           time.Time     // 实际结束时间
	SerialOrderTab       model.SerialOrder
	SerialOrderStatusTab model.SerialOrderStatus
}

// Get 基于指定的批次订单号获取批次订单对象
func (tasksrv *TaskSrv) Get() (err error) {

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := plnDal.NewCustOrderDAO(session, tasksrv.PlantNr, "scheduler")
	var custOrder *plnModel.CustOrder
	if custOrder, err = dao.SelectOne(tasksrv.TaskId); err != nil {
		return grmi.NewBusinessError("获取工厂客户订单失败, 客户订单ID:" + tasksrv.TaskId + ", error:" + err.Error())
	}
	tasksrv.Init(*custOrder)
	return
}

// Init 基于给定的订单表和订单状态表进行结构初始化
func (tasksrv *TaskSrv) Init(custOrder plnModel.CustOrder) {
	// 初始化批次任务的批次订单信息
	tasksrv.TaskId = custOrder.CustOrderId
	tasksrv.TaskType = custOrder.OrderType
	tasksrv.SortKey = custOrder.PlanStartTime.Restore().Unix() // 用于给MES用的排序号
	tasksrv.ArtId = custOrder.ProductFamilyId                  // 任务产品ID
	//tasksrv.Priority = custOrder.Priority                             // 优先级
	tasksrv.Status = custOrder.OrderStatus.Status // 订单状态
	//tasksrv.SchedResGrpId = wotab.PlanResourceGroupId             // 调度资源组ID
	tasksrv.SchedResId = custOrder.WorkLineId                     // 调度资源
	tasksrv.FixResource = false                                   // 锁定资源
	tasksrv.PlanQty = float64(custOrder.OrderQty.PlanQty)         // 计划数量
	tasksrv.ReleasedQty = float64(custOrder.OrderQty.ReleasedQty) // 已下达数量 - 每个独立生成序列订单
	tasksrv.FinishedQty = float64(custOrder.OrderQty.ActQty)      // 已完成数量
	tasksrv.CancelQty = float64(custOrder.OrderQty.CancelQty)
	tasksrv.EarliestStartTime = custOrder.PlanStartDate.Restore() // 最早开始时间
	tasksrv.LatestEndTime = custOrder.PlanEndTime.Restore()       // 最迟结束时间
	tasksrv.SetupStartTime = custOrder.SetupStartTime.Restore()   // 换型开始时间
	tasksrv.SetupEndTime = custOrder.SetupEndTime.Restore()       // 换型结束时间
	if custOrder.FixStartTimeToggle {                             // 锁定开始时间
		tasksrv.FixStartTime = true
	} else {
		tasksrv.FixStartTime = false
	}
	tasksrv.SchedStartTime = custOrder.PlanStartTime.Restore() // 排程开始时间
	tasksrv.SchedEndTime = custOrder.PlanEndTime.Restore()     // 排程结束时间
	tasksrv.ActStartTime = custOrder.ActStartTime.Restore()    // 实际开始时间
	tasksrv.ActEndTime = custOrder.ActEndTime.Restore()        // 实际结束时间

	// 获取调度任务所需要的工时
	calQty := custOrder.PlanQty - custOrder.OrderQty.CancelQty
	//if custOrder.RatePerHourToggle {
	if custOrder.QuantityPerHour <= 0 || calQty <= 0 {
		tasksrv.Duration = 0
	} else {
		tasksrv.Duration = time.Duration(float64(calQty)/float64(custOrder.QuantityPerHour)*3600) * time.Second
	}

	//}
	//if wotab.TimePerItemToggle {
	//	perItemSeconds := float64(wotab.OpTimePerItem) * GetTimeUomSeconds(wotab.TimeUomId)
	//	tasksrv.Duration = time.Duration(float64(calQty)*perItemSeconds) * time.Second
	//}
	//if wotab.TimePerBatchToggle {
	//	if wotab.BatchTime <= 0 || wotab.BatchQuantity <= 0 || calQty <= 0 {
	//		tasksrv.Duration = 0
	//	} else {
	//		perBatchSeconds := float64(wotab.BatchTime) * GetTimeUomSeconds(wotab.TimeUomId)
	//		tasksrv.Duration = time.Duration(perBatchSeconds*float64(calQty)/float64(wotab.BatchQuantity)) * time.Hour
	//	}
	//}
	tasksrv.CustOrder = custOrder
	tasksrv.CustOrderStatus = custOrder.OrderStatus
	tasksrv.CustOrderQty = custOrder.OrderQty
}

// LoadSerialOrders 加载批次订单的序列订单
func (tasksrv *TaskSrv) LoadSerialOrders(session *xorm.Session) (err error) {
	var (
		i             int
		sordertablst  []model.SerialOrder
		serialtasksrv SerialTaskSrv
	)
	// 初始化批次订单的序列订单信息
	tasksrv.SerialTaskArray = []SerialTaskSrv{}
	dao := dal.NewSerialOrderDAO(session, tasksrv.PlantNr, "scheduler")
	if sordertablst, err = dao.SelectByCustOrder(tasksrv.CustOrder.CustOrderId, common.WO_STATUS_LOCKED); err != nil {
		return grmi.NewBusinessError(fmt.Sprintf("查询客户订单的序列订单失败！, error:" + err.Error() + " ,客户订单ID: " + tasksrv.CustOrder.CustOrderId))
	}
	for i = 0; i < len(sordertablst); i++ {
		serialtasksrv = SerialTaskSrv{}
		serialtasksrv.Init(sordertablst[i])
		tasksrv.SerialTaskArray = append(tasksrv.SerialTaskArray, serialtasksrv)
	}
	return
}

// CreateStatusRecord 生成状态记录
func (tasksrv *TaskSrv) CreateStatusRecord(prevstatus int, eventsrv EventSrv) (err error) {
	var wostatusrectab model.WorkOrderStatusRecLst

	// 初始化
	wostatusrectab = model.WorkOrderStatusRecLst{}
	wostatusrectab.PlantNr = tasksrv.PlantNr
	wostatusrectab.WorkOrderId = tasksrv.TaskId
	wostatusrectab.PrevStatus = prevstatus
	wostatusrectab.Status = tasksrv.Status
	wostatusrectab.TriggerEvent = eventsrv.TriggerEvent
	wostatusrectab.TriggerObjectId = eventsrv.TriggerObjectId
	wostatusrectab.TriggerSubObjectId = eventsrv.TriggerSubObjectId
	wostatusrectab.OutputStatus = common.OUTPUT_TRIGGER_UNHANDLED // 未处理
	wostatusrectab.OutputEvent = eventsrv.OutputEvent
	wostatusrectab.OutputEventMessageId = eventsrv.OutputEventMsgId
	wostatusrectab.OutputObjectId = eventsrv.OutputObjectId
	wostatusrectab.OutputSubObjectId = eventsrv.OutputSubObjectId
	SetTableLastModify(&wostatusrectab, wostatusrectab, common.MODIFY_MODE_CREATE, eventsrv.TriggerService)

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewWorkOrderStatusRecLstDAO(session, tasksrv.PlantNr, "scheduler")
	if err := dao.InsertOne(&wostatusrectab); err != nil {
		err = errors.New(fmt.Sprintf("无法为工单%s插入状态变更记录%v!", tasksrv.TaskId, err))
		return err
	}

	return
}

// ReleaseSerialOrder 下达批次订单，同步生成对应的序列订单
func (tasksrv *TaskSrv) ReleaseSerialOrder(relQty int, releaseId string) (err error) {
	//engine := db.Eloquent.Master()
	//session := engine.NewSession()
	//defer session.Close()
	//if err = session.Begin(); err != nil {
	//	return grmi.NewBusinessError("开启事务失败, error:" + err.Error())
	//}
	//var user = "scheduler"
	//workLineDao := baseDal.NewWorkLineDAO(session, tasksrv.PlantNr, user)
	//innerLog, _ := logger.NewLogger("scheduler", "scheduler")
	//innerLog.Debug("开始派工， 派工任务ID:"+releaseId, ", 客户订单ID:"+tasksrv.CustOrder.CustOrderId+", 通过数量进行派工,派工数量:"+strconv.Itoa(relQty))
	//// 判断批次订单状态是否满足下达条件 >= 24 && < 80
	//if tasksrv.CustOrderStatus.Status < common.WO_STATUS_LOCKED || tasksrv.CustOrderStatus.Status >= common.WO_STATUS_FINISHED {
	//	_ = session.Rollback()
	//	return grmi.NewBusinessError("客户订单状态不满足派工条件, 无法下达, 客户订单ID:" + tasksrv.CustOrder.CustOrderId + ", 订单状态:" + strconv.Itoa(tasksrv.CustOrderStatus.Status))
	//}
	//innerLog.Debug("开始派工， 派工任务ID:"+releaseId, ", 客户订单ID:"+tasksrv.CustOrder.CustOrderId+", 加载客户订单下的序列订单完成")
	//// 计算实际可下达的序列订单数量
	//actRelQty := len(tasksrv.SerialTaskArray)
	//if actRelQty <= 0 {
	//	_ = session.Rollback()
	//	innerLog.Error("开始派工， 派工任务ID:" + releaseId + ", 客户订单ID:" + tasksrv.CustOrder.CustOrderId + ", 该客户订单下可派工的订单数量为0")
	//	return grmi.NewBusinessError("客户订单可下达数量为0, 客户订单ID:" + tasksrv.CustOrder.CustOrderId)
	//}
	//if relQty > actRelQty {
	//	_ = session.Rollback()
	//	innerLog.Error("开始派工， 派工任务ID:" + releaseId + ", 客户订单ID:" + tasksrv.CustOrder.CustOrderId + ", 需要派工数量大于客户订单可派工数量, 需要派工数量:" + strconv.Itoa(relQty) + ", 可派工数量:" + strconv.Itoa(actRelQty))
	//	return grmi.NewBusinessError("客户订单需要派工数量大于可派工数量, 客户订单ID:" + tasksrv.CustOrder.CustOrderId)
	//}
	//workLine, err := workLineDao.SelectOne(tasksrv.CustOrder.WorkLineId)
	//if err != nil {
	//	_ = session.Rollback()
	//	innerLog.Error("开始派工， 派工任务ID:" + releaseId + ", 客户订单ID:" + tasksrv.CustOrder.CustOrderId + ", 获取产线数据失败, error:" + err.Error())
	//	return grmi.NewBusinessError("获取产线数据失败, 客户订单ID:" + tasksrv.CustOrder.CustOrderId + ", error:" + err.Error())
	//}
	//if workLine == nil {
	//	_ = session.Rollback()
	//	innerLog.Error("开始派工， 派工任务ID:" + releaseId + ", 客户订单ID:" + tasksrv.CustOrder.CustOrderId + ", 产线不存在, 产线ID:" + tasksrv.CustOrder.WorkLineId)
	//	return grmi.NewBusinessError("获取产线不存在, 产线ID:" + tasksrv.CustOrder.WorkLineId + ", 客户订单ID:" + tasksrv.CustOrder.CustOrderId)
	//}
	//custOrderSvr := plnSvr.NewCustOrderService()
	//resCustOrder, err := custOrderSvr.ReleaseCustOrder(session, relQty, &tasksrv.CustOrder)
	//if err != nil {
	//	_ = session.Rollback()
	//	innerLog.Error("开始派工， 派工任务ID:" + releaseId + ", 客户订单ID:" + tasksrv.CustOrder.CustOrderId + ", 客户订单派工阶段失败, error:" + err.Error())
	//}
	//_ = session.Commit()
	//innerLog.Debug("派工完成， 派工任务ID:"+releaseId, ", 客户订单ID:"+tasksrv.CustOrder.CustOrderId+", 派工数量:"+strconv.Itoa(relQty))
	//tasksrv.Init(*resCustOrder)
	return
}

// Get 基于指定的序列订单号获取序列订单对象
func (stasksrv *SerialTaskSrv) Get() (err error) {
	var serordtab model.SerialOrder

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, stasksrv.PlantNr, "scheduler")
	if serordtab, err = dao.SelectInfo(stasksrv.TaskId); err != nil {
		err = errors.New(fmt.Sprintf("获取工厂%d序列订单%s失败！", stasksrv.PlantNr, stasksrv.TaskId))
		return
	}
	stasksrv.Init(serordtab)
	return
}

// Init 初始化序列订单
func (stasksrv *SerialTaskSrv) Init(serialordertab model.SerialOrder) {

	stasksrv.SerialOrderTab = serialordertab
	stasksrv.SerialOrderStatusTab = serialordertab.SerialOrderStatus
}

// Create 创建序列订单
func (stasksrv *SerialTaskSrv) Create() (err error) {

	// 插入序列订单
	stasksrv.SerialOrderTab = model.SerialOrder{}
	stasksrv.SerialOrderTab.PlantNr = stasksrv.PlantNr
	stasksrv.SerialOrderTab.ProjectId = stasksrv.ProjectId
	stasksrv.SerialOrderTab.ArtId = stasksrv.ArtId
	stasksrv.SerialOrderTab.WorkOrderId = stasksrv.ParentTaskId
	stasksrv.SerialOrderTab.PlanResourceId = stasksrv.SchedResId
	stasksrv.SerialOrderTab.OrderType = stasksrv.TaskType
	stasksrv.SerialOrderTab.SerialOrderId = stasksrv.TaskId
	stasksrv.SerialOrderTab.PlanQty = float64(stasksrv.PlanQty)
	stasksrv.SerialOrderTab.PlanStartTime = grmi.DateTime(stasksrv.SchedStartTime)
	stasksrv.SerialOrderTab.PlanEndTime = grmi.DateTime(stasksrv.SchedEndTime)
	SetTableLastModify(&stasksrv.SerialOrderTab, stasksrv.SerialOrderTab, common.MODIFY_MODE_CREATE, "rel_service")

	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	dao := dal.NewSerialOrderDAO(session, stasksrv.PlantNr, "rel_service")
	if err = dao.InsertOne(&stasksrv.SerialOrderTab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s头创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	// 插入序列订单状态行
	stasksrv.SerialOrderStatusTab = model.SerialOrderStatus{}
	stasksrv.SerialOrderStatusTab.PlantNr = stasksrv.PlantNr
	stasksrv.SerialOrderStatusTab.SerialOrderId = stasksrv.TaskId
	stasksrv.SerialOrderStatusTab.Status = common.WO_STATUS_PLANNED
	stasksrv.SerialOrderStatusTab.TriggerObjectId = stasksrv.ParentTaskId
	stasksrv.SerialOrderStatusTab.TriggerEvent = common.BATORD_EVENT_LOCK
	SetTableLastModify(&stasksrv.SerialOrderStatusTab, stasksrv.SerialOrderStatusTab, common.MODIFY_MODE_CREATE, "rel_service")
	serial := dal.NewSerialOrderStatusDAO(session, stasksrv.PlantNr, "rel_service")
	if err = serial.InsertOne(&stasksrv.SerialOrderStatusTab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态头创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	// 插入序列订单状态记录行
	// 如果需要触发动作可在此处添加
	serordstatrectab := model.SerialOrderStatusRecLst{}
	serordstatrectab.PlantNr = stasksrv.PlantNr
	serordstatrectab.SerialOrderId = stasksrv.TaskId
	serordstatrectab.PrevStatus = 0
	serordstatrectab.Status = common.WO_STATUS_PLANNED
	serordstatrectab.OutputEvent = common.OUTPUT_EVENT_SER_NEW
	serordstatrectab.OutputObjectId = serordstatrectab.SerialOrderId
	serordstatrectab.OutputStatus = common.OUTPUT_TRIGGER_UNHANDLED
	SetTableLastModify(&serordstatrectab, serordstatrectab, common.MODIFY_MODE_CREATE, "rel_service")
	serord := dal.NewSerialOrderStatusRecLstDAO(session, stasksrv.PlantNr, "rel_service")
	if err = serord.InsertOne(&serordstatrectab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态记录行创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	return
}

// Release 下达序列订单
func (stasksrv *SerialTaskSrv) Release() (err error) {
	var prevstatus int

	// 判断序列订单是否可以被下达
	if stasksrv.SerialOrderStatusTab.Status >= common.WO_STATUS_RELEASED {
		return
	}

	// 获取序列订单当前状态
	prevstatus = stasksrv.SerialOrderStatusTab.Status

	// 更新序列订单状态行
	stasksrv.SerialOrderStatusTab.Status = common.WO_STATUS_RELEASED
	stasksrv.SerialOrderStatusTab.TriggerObjectId = stasksrv.ParentTaskId
	stasksrv.SerialOrderStatusTab.TriggerEvent = common.BATORD_EVENT_RELEASE
	SetTableLastModify(&stasksrv.SerialOrderStatusTab, stasksrv.SerialOrderStatusTab, common.MODIFY_MODE_UPDATE, "rel_service")
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	serial := dal.NewSerialOrderStatusDAO(session, stasksrv.PlantNr, "rel_service")
	if err = serial.UpdateOne(&stasksrv.SerialOrderStatusTab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态头更新失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}

	// 插入序列订单状态记录行
	// 如果需要触发动作可在此处添加
	serordstatrectab := model.SerialOrderStatusRecLst{}
	serordstatrectab.PlantNr = stasksrv.PlantNr
	serordstatrectab.SerialOrderId = stasksrv.TaskId
	serordstatrectab.PrevStatus = prevstatus
	serordstatrectab.Status = common.WO_STATUS_RELEASED
	serordstatrectab.OutputEvent = common.OUTPUT_EVENT_SER_REL
	serordstatrectab.OutputObjectId = serordstatrectab.SerialOrderId
	serordstatrectab.OutputStatus = common.OUTPUT_TRIGGER_UNHANDLED
	SetTableLastModify(&serordstatrectab, serordstatrectab, common.MODIFY_MODE_UPDATE, "rel_service")
	serord := dal.NewSerialOrderStatusRecLstDAO(session, stasksrv.PlantNr, "rel_service")
	if err = serord.UpdateOne(&serordstatrectab); err != nil {
		err = errors.New(fmt.Sprintf("工单%s的序列订单%s状态记录行创建失败%v！", stasksrv.ParentTaskId, stasksrv.TaskId, err))
		return
	}
	return
}
