// form/nc/nc-production/nc-phase-view.js
// Phase drawing for work phase gantt chart, extracted from nc-production.js
// call initPhaseView once at startup and again when idx data changes

const maxWorkDrawCount = 12
const skipAutoPhaseIdx = { product_tree: true }
let prevChartType, prevWorkPhaseIdx
let nc, peg, state, rec, ganttData
let productIdx, currentDataByIdx, lowerIdxArr, currentData
let showUpperChain

export function initPhaseView(nc_, peg_, state_, ganttData_, productIdx_, currentDataByIdx_, lowerIdxArr_, currentData_, showUpperChain_) {
	nc = nc_
	peg = peg_
	state = state_
	rec = state_.rec
	ganttData = ganttData_
	productIdx = productIdx_
	currentDataByIdx = currentDataByIdx_
	lowerIdxArr = lowerIdxArr_
	currentData = currentData_
	showUpperChain = showUpperChain_
}

// eslint-disable-next-line no-unused-vars
export function showWorkPhase(selectedItemArr, checkTab, resetIdx) {
	if (resetIdx != null) {
		prevWorkPhaseIdx = resetIdx
	}
	let item = currentData && selectedItemArr[0]
	if (!item) {
		return
	}
	if (prevChartType == null) {
		prevChartType = rec.chart_type
	}
	if (prevWorkPhaseIdx === item.idx) {
		return
	}
	if (maxWorkDrawCount < 1) {
		return
	}
	const phasePartIdx = state.grid?.gridIdx?.phasePartIdx
	if (phasePartIdx == null) {
		console.error('show work phase: phase part index is missing')
		return
	}
	prevWorkPhaseIdx = item.idx
	const phaseSourceArray = []
	let workDrawCount = 0
	let phaseSource = null
	let phasePartRow
	function getPhasePart(row) {
		if (phasePartIdx == null) {
			return null
		}
		const key = row.phase_key
		if (key == null || key === '') {
			return null
		}
		const phases = phasePartIdx[key]
		if (phases == null) {
			return null
		}
		return phases
	}
	function findPhaseSource(row) {
		phasePartRow = getPhasePart(row)
		if (phasePartRow != null && phasePartRow.length > 0) {
			return row
		}
		return null
	}
	function addPhaseSource(source) {
		if (source == null) {
			return
		}
		if (phaseSource == null) {
			phaseSource = source
		}
		phaseSourceArray.push(source)
		workDrawCount++
	}
	let originalItem = item
	item = rec.weekly_need ? item.calc : item
	phaseSource = findPhaseSource(item)
	addPhaseSource(phaseSource)
	// search chain when phase count limit not reached
	if (showUpperChain && workDrawCount < maxWorkDrawCount) {
		let childIdx = originalItem.idx
		let currentRow = originalItem
		const visitedIdx = {}
		visitedIdx[childIdx] = true
		while (currentRow.from_row != null) {
			childIdx = currentRow.idx
			currentRow = currentDataByIdx[currentRow.from_row]
			if (currentRow == null) {
				break
			}
			if (visitedIdx[currentRow.idx]) {
				console.error('showWorkPhase: upper chain visited same idx twice', currentRow)
				break
			}
			visitedIdx[currentRow.idx] = true
			// previous source's position in this parent's BOM
			if (lowerIdxArr[currentRow.idx] != null && phaseSourceArray.length > 0) {
				let prevSource = phaseSourceArray[phaseSourceArray.length - 1]
				if (prevSource != null) {
					prevSource.material_row = lowerIdxArr[currentRow.idx].indexOf(childIdx) + 1
				}
			}
			addPhaseSource(findPhaseSource(rec.weekly_need ? currentRow.calc : currentRow))
			if (workDrawCount >= maxWorkDrawCount) {
				break
			}
		}
	}
	if (workDrawCount == 0 || (checkTab && !rec.select_phase_view && rec.chart_type !== 'work_phase')) {
		ganttData.value = {}
		if (!rec.weekly_need && checkTab && rec.select_phase_view && rec.chart_type === 'work_phase') {
			rec.chart_type = prevChartType
		}
		return
	}
	if (!rec.weekly_need && rec.select_phase_view && rec.chart_type !== 'work_phase' && !skipAutoPhaseIdx[rec.chart_type]) {
		prevChartType = rec.chart_type
		rec.chart_type = 'work_phase'
	}
	const productionOrderActual = state.grid?.gridIdx?.['production_order-actual']
	if (productionOrderActual == null) {
		console.error('show work phase: production_order-actual is missing')
		return
	}
	const workArr = []
	const sourceOrderRow = []
	rec.selected_production_lot = item.production_order_id
	if (rec.selected_production_lot == null) {
		rec.selected_production_lot = phaseSource.production_order_id
	}
	let totalHours = 0
	let totalExtraHours = 0
	let totalSetupHours = 0
	let totalAmount = 0
	let totalProducedAmount = 0
	let firstStartDt
	let workIdx = 0
	let lastWork
	let sourceIdx = 0
	let highlightDate = item.date != null ? item.date : item.start_dt
	let phase, work
	let phaseIdx, firstRealWork, highlightedWork, firstSourceRealWork
	let actual, actualPhase
	let prevSourceEnd2Dt
	let prevSourceLastWork
	let sourcePhasePart
	let sourcePhaseCount
	for (const source of phaseSourceArray) {
		lastWork = null
		sourceIdx++
		sourcePhaseCount = 0
		if (source.material_row != null) {
			sourceOrderRow[sourceIdx] = source.material_row
		} else {
			sourceOrderRow[sourceIdx] = 1
		}
		phaseIdx = 0
		let visiblePhaseIdx = 0
		firstRealWork = null
		let lastRealWork = null
		sourcePhasePart = getPhasePart(source)
		if (sourcePhasePart == null) {
			continue
		}
		for (const phaseItem of sourcePhasePart) {
			if (phaseItem == null) {
				console.error('show work phase: phase_part has undefined entry at index', phaseIdx)
				phaseIdx++
				continue
			}
			phaseIdx++
			if (rec.show_material_phase === false && phaseItem.work_phase_number === 0) {
				continue
			}
			visiblePhaseIdx++
			phase = phaseItem
			if (firstStartDt == null && phase.work_phase_number !== 0) {
				firstStartDt = Math.round(phase.start_dt) * 1000
			}
			work = nc.clone(phase)
			work.phase_key = source.phase_key
			work.parent_phase_key = source.parent_phase_key
			work.need_down = source.need_down
			work.calc = work.calc || { work_hours: phase.work_hours } //phase_part: phase.part,
			work.calc.phase_depth = sourceIdx - 1
			if (phase.error) {
				work.calc.error = phase.error
			}
			workIdx++
			work.idx = workIdx
			work.label_text = ''
			if (work.work_phase_number !== 0) {
				if (firstRealWork == null) {
					firstRealWork = work
				}
				lastRealWork = work
			}
			if (!work.production_order_id) {
				if (source.production_order_id !== '') {
					work.production_order_id = source.production_order_id
				} else {
					work.production_order_id = source.order_id + '.' + source.order_row
				}
			}
			if (sourceIdx >= 2 && prevSourceEnd2Dt != null && work.start_dt < prevSourceEnd2Dt && firstRealWork === work) {
				console.warn(
					'phase view: upper linked start is before linking end,',
					work.work_phase_number + '. ' + work.resource_id + ' ' + work.production_order_id,
					'start:',
					new Date(work.start_dt * 1000).toLocaleString(),
					'< linking end:',
					new Date(prevSourceEnd2Dt * 1000).toLocaleString()
				)
			}
			if (source.product_id !== item.product_id) {
				work.calc.upper_product_id = source.product_id
			}
			if (visiblePhaseIdx === 1) {
				work.calc.show_product_id = source.product_id
			}
			work.pr = productIdx[source.product_id]
			actual = productionOrderActual[work.production_order_id]
			actualPhase = actual?.phase?.[work.work_phase_number]
			if (actualPhase != null) {
				work.work_phase_state = actualPhase.work_phase_state
				console.debug('actual found for', work.production_order_id, 'phase:', work.work_phase_number, 'state:', work.work_phase_state, 'produced:', actualPhase.produced_amount)
			} else {
				work.work_phase_state = item.origin || source.origin ? peg.parseBefore(item.origin || source.origin, ' ') : 'unknown'
			}
			work.remaining_amount = work.amount
			work.produced_amount = 0
			totalHours += work.work_hours
			totalSetupHours += work.setup_hours
			totalExtraHours += work.extra_hours
			totalAmount += work.amount
			totalProducedAmount += work.produced_amount
			workArr.push(work)
			lastWork = work
			sourcePhaseCount++
		}
		if (sourceIdx >= 2) {
			if (firstRealWork) {
				firstRealWork.calc.highlight = highlightDate
				highlightedWork = firstRealWork
			}
		}
		if (sourceIdx === 1 && firstRealWork) {
			firstSourceRealWork = firstRealWork
		}
		if (lastWork && rec.show_total_phase !== false && (rec.show_one_phase_total || sourcePhaseCount > 1)) {
			work = nc.clone(lastWork)
			work.part = null
			workIdx++
			work.work_phase_number = work.work_phase_number + 1 // delete work.work_phase_number
			work.idx = workIdx
			work.label_text = ''
			work.start_dt = lastWork.end2_dt
			work.end_dt = work.start_dt
			work.end2_dt = work.start_dt
			work.work_hours = totalHours
			work.setup_hours = totalSetupHours
			work.extra_hours = totalExtraHours
			const actual = productionOrderActual[lastWork.production_order_id]
			const actualProduced = actual?.produced_amount || 0
			work.remaining_amount = totalAmount - actualProduced
			work.produced_amount = actualProduced
			work.calc = {
				tooltip_start_dt: firstStartDt,
				time_in_ms: false,
				show_product_id: source.product_id,
				phase_depth: sourceIdx - 1,
				upper_product_id: lastWork.calc?.upper_product_id
			}
			work.pr = productIdx[source.product_id]
			if (sourceIdx >= 2 && prevSourceLastWork != null) {
				const prevSource = phaseSourceArray[sourceIdx - 2]
				work.extend =
					'linked to ↑ ' +
					prevSourceLastWork.work_phase_number +
					'. ' +
					prevSourceLastWork.resource_id +
					', ' +
					(prevSource.production_order_id ? prevSource.production_order_id + ' | ' : '') +
					prevSource.product_id
			}
			work.production_order_id = lastWork.production_order_id
			work.resource_id = 'TOTAL'
			work.work_phase_state = 'delivery'
			work.description = 'delivery'
			workArr.push(work)
			prevSourceEnd2Dt = work.end2_dt
		} else if (lastRealWork) {
			lastRealWork.calc.show_product_id = source.product_id
		}
		prevSourceLastWork = lastWork
	}
	const maxSourceIdx = sourceIdx
	let visiblePhaseWork
	for (const workItem of workArr) {
		if (workItem.calc?.phase_depth == null) {
			continue
		}
		let itemSourceIdx = workItem.calc.phase_depth + 1
		workItem.calc.phase_prefix = buildPhasePrefix(workItem.production_order_id, sourceOrderRow, itemSourceIdx, maxSourceIdx)
		if (workItem.work_phase_number === 0) {
			continue
		}
		if (visiblePhaseWork != null) {
			visiblePhaseWork.calc.next_phase = nc.l('next: ') + workItem.work_phase_number + '. ' + workItem.resource_id + ' ' + workItem.calc.phase_prefix
		}
		visiblePhaseWork = workItem
	}
	if (highlightedWork == null && firstSourceRealWork) {
		firstSourceRealWork.calc.highlight = firstSourceRealWork.start_dt
	}
	updateGanttData(workArr)
}

function buildPhaseRowPath(sourceOrderRow, sourceIdx, maxSourceIdx) {
	let path = ''
	for (let idx = maxSourceIdx - 1; idx >= sourceIdx; idx--) {
		if (sourceOrderRow[idx] == null) {
			continue
		}
		path = path ? path + '.' + sourceOrderRow[idx] : '' + sourceOrderRow[idx]
	}
	return path
}

function buildPhasePrefix(productionOrderId, sourceOrderRow, sourceIdx, maxSourceIdx) {
	let prefix = ''
	let indentCount = maxSourceIdx - sourceIdx
	for (let idx = 0; idx < indentCount; idx++) {
		prefix += '▸'
	}
	if (sourceIdx > 1) {
		prefix += '↑'
	}
	let rowPath = buildPhaseRowPath(sourceOrderRow, sourceIdx, maxSourceIdx)
	return prefix + productionOrderId + (rowPath ? '.' + rowPath : '')
}

function updateGanttData(workArr) {
	if (state?.grid?.gridIdx?.resourceIdx) {
		const capacityIdx = state.grid.gridIdx.capacityIdx
		const resourceIdx = state.grid.gridIdx.resourceIdx
		const usedResource = {}
		const resource = [] // create new array, do not change original data
		const capacityIdx2 = {} // create new array, do not change original data
		let prevItem, res, capArr
		workArr.forEach(item => {
			if (item == null || item.resource_id == null) {
				console.error('show work phase: work array has invalid item', item)
				return
			}
			if (!usedResource[item.resource_id]) {
				usedResource[item.resource_id] = true
				res = resourceIdx[item.resource_id]
				capArr = capacityIdx[item.resource_id]
				if ((!res || !capArr) && prevItem) {
					res = resourceIdx[prevItem.resource_id]
					capArr = capacityIdx[prevItem.resource_id]
				}
				capacityIdx2[item.resource_id] = capArr
				resource.push({ start_dt: item.start_dt, resource_id: item.resource_id, resource_name: res ? res.resource_name : '', capArr: capArr })
				prevItem = item
			}
		})
		ganttData.value = { work_phase: workArr, resource: resource, capacityIdx: capacityIdx2 }
	}
}
