// form/nc/nc-production/nc-production.js

// eslint-disable-next-line no-unused-vars
const chartRef = ref(null)
const chartOption = ref({})
const chartInitOption = ref({})
const ganttData = ref({})

let findText = ''
let areaName = 'product_need'
let findProductIdx = {}
let hasNeedWeekProductIdx = {}
let hasNeedProductIdx = {}
let productIdx = {}
let chartProductIdxArr = {}
let workEstimatedRecordIdx = {}
let needToIdxArr = {}
let needToWeekIdxArr = {}
let needFromWeekIdxArr = {}
let selectedItemArr = []
const unselectOther = true
let activatedRowIdxArr = []

let currentData, currentArea, stockChart, chartPref
let prevChartSelected = []
const chartData = []
let gridData, gridDataWeekly
let gridDataProductIdxArr, gridDataProductIdx, gridDataWeeklyProductIdx

let updateFunc
const parseBefore = peg.parseBefore

onMounted(() => {
	updateFunc = nc.debounce(updateFilter, (rec.find_delay_seconds && rec.find_delay_seconds * 1000) || 1000, false)
	if (!state.grid?.nats_name) {
		update('update') // first data load
	}
})

// eslint-disable-next-line no-unused-vars
function clickButton(action) {
	if (action === 'clear_find') {
		rec.find_text = ''
		prevCheckBoxValue = -1
		checkBoxClicked()
	} else if (action === 'add_order') {
		if (!rec.allow_save && rec.weekly_need) {
			return
		}
		selectedItemArr = nc.grid.activatedRecordArr(state, 'product_need_weekly')
		if (selectedItemArr.length > 0) {
			const saveData = []
			const companyData = {}
			for (let item of selectedItemArr) {
				if (item.order_amount > 0 && item.company_id) {
					if (!companyData[item.company_id]) {
						companyData[item.company_id] = {
							company_id: item.company_id,
							ordr: []
						}
						saveData.push(companyData[item.company_id])
					}
					companyData[item.company_id].ordr.push({ product_id: item.product_id, order_amount: item.order_amount })
				} else {
					message.setInfo(state, nc.l('Please set order amount and supplier company id to all selected rows before adding a new purchase order'))
					saveData.length = 0
					break
				}
			}
			if (saveData.length > 0) {
				nc.callServer(
					state,
					'save',
					{
						save: [{ table: 'order-purchase', save_preference: 'form/nc/nc-production/save-purchase-order.json', data: saveData }]
					},
					ret => {
						const saveArr = ret?.save && ret.save[0].data
						const orderNumArr = saveArr && saveArr.map(item => item.order_id)
						setTimeout(() => {
							// timeout is needed to show the message in the callServer callback
							if (orderNumArr && orderNumArr.length == 1) {
								message.setInfo(state, nc.l(`Saved purchase order '${orderNumArr[0]}'`))
							} else if (orderNumArr && orderNumArr.length > 1) {
								message.setInfo(state, nc.l(`Saved ${orderNumArr.length} purchase orders: ${orderNumArr.join(', ')}`))
							} else {
								message.setInfo(state, nc.l(`Purchase order save failed with error: ${ret.error}`))
							}
						}, 0)
						// update('update')
					}
				)
			}
		}
	}
}

// work gantt chart
// let prevChartType = rec.chart_type
function showWorkPhase(checkTab) {
	let phaseArr
	const item = currentData && selectedItemArr[0]
	let showWork
	if (item && item.phase_part) {
		phaseArr = item.phase_part
		showWork = phaseArr && phaseArr.length > 0
	} else if (item && item.from_row) {
		showWork = true
		if (showWork) {
			const parent = currentData[item.from_row - 1]
			showWork = parent && parent.phase_part
			if (showWork) {
				phaseArr = parent.phase_part // parent.phase_part[item.record_id]
				showWork = phaseArr && phaseArr.length > 0
			}
		}
	}
	if (!showWork || (checkTab && rec.chart_type !== 'work_phase')) {
		ganttData.value = {}
		/* 	if (rec.chart_type === 'work_phase') {
			rec.chart_type = prevChartType
		} */
		return
	}
	if (rec.chart_type !== 'work_phase') {
		return
		/* TODO: restore when gantt works
		prevChartType = rec.chart_type
		rec.chart_type = 'work_phase' */
	}
	const workArr = []
	if (!item.production_order_id) {
		item.production_order_id = item.order_id
	}
	rec.selected_production_lot = item.production_order_id
	let phase, work
	let totalHours = 0
	let totalExtraHours = 0
	let totalSetupHours = 0
	for (let idx = 1; idx <= phaseArr.length; idx++) {
		phase = phaseArr[idx - 1]
		if (phase) {
			work = workEstimatedRecordIdx[phase.record_id]
			work.calc = { phase_part: phase.part, work_hours: phase.work_hours }
			work.start_dt = phase.start_dt
			work.end_dt = phase.end_dt
			work.end2_dt = phase.end2_dt
			work.setup_hours = phase.setup_hours
			work.extra_hours = phase.extra_hours
			work.production_order_id = item.production_order_id
			work.work_phase_state = item.from_depth ? parseBefore(item.from_depth, ' ') : 'production'
			work.remaining_amount = phase.amount // Math.round(phase.amount) // Math.round(parent.need_down)
			work.produced_amount = 0
			totalHours += phase.work_hours
			totalSetupHours += phase.setup_hours
			totalExtraHours += phase.extra_hours
			delete work._time_in_ms // force time calculations
			workArr.push(work)
			if (idx == phaseArr.length) {
				work = nc.clone(work)
				work.calc = {}
				work.start_dt = phase.part[phase.part.length - 1].end_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
				work.work_phase_state = 'delivery'
				workArr.push(work)
			}
		}
	}
	updateGanttData(workArr)
}

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
		workArr.forEach(item => {
			if (!usedResource[item.resource_id]) {
				usedResource[item.resource_id] = true
				const res = resourceIdx[item.resource_id]
				const capArr = capacityIdx[item.resource_id]
				capacityIdx2[item.resource_id] = capArr
				resource.push({ start_dt: item.start_dt, resource_id: item.resource_id, show: res ? res.resource_name : resource, capArr: capArr })
			}
		})
		ganttData.value = { work_phase: workArr, resource: resource, capacityIdx: capacityIdx2 }
	}
}

// eslint-disable-next-line no-unused-vars
function gridRowDoubleClicked(area, rowIndex, event) {
	/* let arr = state.grid?.[area]?.data
	if (arr.length > rowIndex) {
		if (activatedRowIdxArr && activatedRowIdxArr.length > 0) {
			rowIndex = activatedRowIdxArr[0] - 1
		}
		rec.find_text = arr[rowIndex].product_id
		prevCheckBoxValue = -1
		setTimeout(checkBoxClicked, 100)
	} */
}

let activateCalled = false
// eslint-disable-next-line no-unused-vars
function gridRowClicked(areaName, rowIndex, event, selected) {
	// if (currentData && selected) {
	if (activateCalled) {
		activateCalled = false
		return
	}
	if (rowIndex == null) {
		console.debug(`🚀 ~ selectGridRow, rowIndex == null, rowIndex, selected, event`, rowIndex, selected, event)
		return
	}
	const itemArr = (selected && [event.data]) || []
	selectGridRow(areaName, itemArr)
}

function activateRows(state, areaName, activatedRowIdxArr, scrollType, unselectOther) {
	activateCalled = true // gridRowClicked will be called after activateRows and it sets activateCalled to false
	nc.grid.activateRows(state, areaName, activatedRowIdxArr, scrollType, unselectOther)
	setTimeout(() => {
		activateCalled = false // we need this to be able to select by dragging rows in the grid
	}, 50)
}

function selectGridRow(areaName, itemArr, scrollType) {
	rec.allow_save = false
	selectedItemArr = itemArr
	if (currentData) {
		activatedRowIdxArr.length = 0
		selectedItemArr.forEach(item => {
			if (!item?.idx) {
				console.debug(`🚀 ~ selectGridRow ~ item is null?:`, item)
				return
			} else {
				activatedRowIdxArr.push(item.idx)
				activateUpper(item, activatedRowIdxArr)
			}
		})
		rec.allow_save = rec.weekly_need && selectedItemArr.length > 0
		showWorkPhase()
		activatedRowIdxArr = activateLower(activatedRowIdxArr)
		if (scrollType !== 'scroll-first') {
			/*	scrollType = 'top' // top does not work
		} else { */
			scrollType = 'no-scroll'
		}
		activateRows(state, areaName, activatedRowIdxArr, scrollType, unselectOther) // scroll-last
		updateChartProduct(activatedRowIdxArr)
	}
}

function updateChart() {
	chartOption.value = null // trigger nc-chart option watch
	if (rec.chart_type === 'work_phase') {
		showWorkPhase()
	} else {
		setTimeout(() => {
			chartOption.value = chartPref[rec.chart_type]
		}, 0)
	}
}

// nc-svg-map
let mapSelected = ref([])

// eslint-disable-next-line no-unused-vars
let mapHover = ref([])

// eslint-disable-next-line no-unused-vars
function mapToggleSelected(param) {
	console.debug('map click', param.id, param.event)
	const i = mapSelected.value.indexOf(param.id)
	if (i === -1) mapSelected.value.push(param.id)
	else mapSelected.value.splice(i, 1)
}

// eslint-disable-next-line no-unused-vars
function mapDoubleClick(param) {
	console.debug('map double click', param.id, param.event)
}

// nc-gantt needs function mousemove
// eslint-disable-next-line no-unused-vars
function mousemove(id, e) {
	mouse.value.mousemove = `${id}: ${Math.floor(e.offsetX)}x${Math.floor(e.offsetY)}`
}
const mouse = ref({
	mouseover: null,
	mouseout: null,
	mousemove: null,
	click: null,
	drag: null,
	dragend: null
})

// nc-chart stock balance
let defaultDataset, defaultSeries
function updateChartProduct(selectedArray) {
	let chartMin = 0
	let chartMax = 0
	let item, pr
	stockChart.dataset.length = 2
	stockChart.series.length = 1
	let chartId = 0
	if (!defaultDataset) {
		defaultDataset = nc.clone(stockChart.dataset[0])
		defaultSeries = nc.clone(stockChart.series[0])
	}
	const chartSelected = []
	if (selectedArray.length === 0) {
		stockChart.dataset[chartId] = null
		stockChart.series[chartId] = null
	}
	const count = rec.chart_current ? 1 : selectedArray.length < chartPref.max_product_count ? selectedArray.length : chartPref.max_product_count
	for (let i = 0; i < count; i++) {
		item = currentData[selectedArray[i] - 1]
		pr = productIdx[item.product_id]
		if ((pr && pr.hasBalance) || i === 0) {
			chartSelected.push(item.product_id)
			if (pr) {
				if (pr.minBalance < chartMin) {
					chartMin = pr.minBalance
				}
				if (pr.maxBalance > chartMax) {
					chartMax = pr.maxBalance
				}
			}
			if (stockChart.dataset[chartId] == null) {
				stockChart.dataset[chartId] = nc.clone(defaultDataset)
			}
			stockChart.dataset[chartId].source = chartProductIdxArr[item.product_id] || []
			if (stockChart.series[chartId] == null) {
				stockChart.series[chartId] = nc.clone(defaultSeries)
			}
			stockChart.series[chartId].name = chartPref.show_product_name ? item.name || productIdx[item.product_id]?.name || item.product_id : item.product_id
			stockChart.series[chartId].datasetIndex = chartId
			chartId++
		}
	}
	if (chartSelected.length === prevChartSelected.length && nc.isEqual(chartSelected, prevChartSelected)) {
		return
	}
	prevChartSelected = chartSelected
	chartMin = Math.floor(chartMin)
	if (chartMin < 0) {
		chartMin = chartMin - 1
	}
	chartMax = Math.floor(chartMax) + 1
	stockChart.yAxis[0].min = chartMin
	stockChart.yAxis[1].min = chartMin
	stockChart.yAxis[0].max = chartMax
	stockChart.yAxis[1].max = chartMax
	updateChart()
}

function updateData(ret) {
	if (!state.grid) {
		message.setError(state, "state grid is not defined, can't update data")
		return
	}
	let chartStartDate, chartEndDate
	chartProductIdxArr = {}
	workEstimatedRecordIdx = nc.createIndex(state.grid['product_work-estimated'].data, 'record_id').index
	let data = ret?.data?.chart_option
	if (data) {
		chartPref = data
		stockChart = chartPref.stock_balance // chartPref[rec.chart_type]
		chartStartDate = new Date()
		chartStartDate.setHours(0, 0, 0, 0)
		chartStartDate = chartStartDate.getTime()
		chartEndDate = new Date()
		chartEndDate.setHours(0, 0, 0, 0)
		chartEndDate.setDate(chartEndDate.getDate() + chartPref.chart_days || 60)
		chartEndDate = chartEndDate.getTime()
		stockChart.xAxis.min = chartStartDate
		stockChart.xAxis.max = chartEndDate
		chartStartDate = chartStartDate / 1000
		chartEndDate = chartEndDate / 1000
		chartInitOption.value = chartPref.init_option
		chartOption.value = stockChart
		chartData.length = 0
	}
	let pr, item
	const grid = ret.grid
	data = grid?.product?.data
	if (data) {
		productIdx = nc.createIndex(data, 'product_id')
		if (productIdx.warning) {
			message.setWarning(state, `duplicate id's when creating product id index: ${JSON.stringify(productIdx.warning)}`)
		}
		productIdx = productIdx.index
	}
	hasNeedWeekProductIdx = {}
	hasNeedProductIdx = {}
	needToIdxArr = {}
	needToWeekIdxArr = {}
	rec.error_count_all = 0
	rec.error_count_week = 0
	gridData = grid?.product_need?.data
	if (gridData) {
		data = gridData
		let prevItem, item2
		for (let idx = 1; idx <= data.length; idx++) {
			item = data[idx - 1]
			if (item.first_row) {
				pr = productIdx[item.product_id]
				pr.minBalance = 0
				pr.maxBalance = 0
				if (item.last_row && item.balance === 0) {
					item.is_empty = true
				}
				if (pr.error && pr.error !== item.error) {
					if (item.error) {
						item.error += '\n' + pr.error
					} else {
						item.error = pr.error
					}
				}
			}
			if (item.error) {
				rec.error_count_all++
			}
			item.pr = pr
			if (pr && item.balance && item.balance !== 0) {
				pr.hasBalance = true
				if (item.balance < pr.minBalance) {
					pr.minBalance = item.balance
				}
				if (item.balance > pr.maxBalance) {
					pr.maxBalance = item.balance
				}
			}
			if (!hasNeedProductIdx[item.product_id] && (item.balance < 0 || (item.need_down && item.need_down > 0))) {
				hasNeedProductIdx[item.product_id] = true
			}
			if (item.from_row) {
				if (!needToIdxArr[item.from_row]) {
					needToIdxArr[item.from_row] = [item.idx]
				} else {
					needToIdxArr[item.from_row].push(item.idx)
				}
			}
			if (item.date && item.date >= 0) {
				if (item.date < chartStartDate) {
					item.before_today = true
				}
				if (stockChart) {
					/* if (item.product_id === 'SSH 1,5*1250*2500 ELECTROGALV.') {
						item.date = item.date + 0
					} */
					if (item.first_row) {
						item.balance_date = chartStartDate * 1000
					} else if (item.date > chartStartDate && item.date < chartEndDate) {
						item.balance_date = item.date * 1000 // to js milliseconds day
					} else if (item.date >= chartEndDate) {
						item.balance_date = chartEndDate * 1000 // show future events then end of the chart
					} else {
						item.balance_date = chartStartDate * 1000 // first row balance date is always today = chart start date
					}
					if (item.first_row && item.last_row) {
						item.balance_date = chartStartDate * 1000
						chartData.push(item)
						item2 = { product_id: item.product_id, name: item.name || item.pr?.name, balance_date: chartEndDate * 1000, balance: item.balance }
						chartData.push(item2)
					} else if (!item.first_row) {
						if (prevItem.first_row && item.balance_date >= prevItem.balance_date && item.product_id === prevItem.product_id) {
							prevItem.balance_date = chartStartDate * 1000
							item2 = { product_id: item.product_id, name: prevItem.name || prevItem.pr?.name, balance_date: chartStartDate * 1000, balance: prevItem.balance }
							chartData.push(item2)
						}
						if (item.balance_date > prevItem.balance_date && prevItem.balance !== item.balance && item.product_id === prevItem.product_id) {
							// balance chart lines must not be in angles but straight up or down, previous item balance continues until next item changes it
							item2 = { product_id: prevItem.product_id, name: prevItem.name || prevItem.pr?.name, balance_date: item.balance_date, balance: prevItem.balance }
							chartData.push(item2)
							chartData.push(item)
						} else {
							chartData.push(item)
						}
						if (item.last_row && prevItem.balance_date < chartEndDate * 1000 && item.balance !== prevItem.balance) {
							// continue last balance to end of the graph
							item2 = { product_id: item.product_id, name: item.name || item.pr?.name, balance_date: chartEndDate * 1000, balance: item.balance }
							chartData.push(item2)
						}
					}
					prevItem = item
				}
			}
		}
		gridData.forEach(item => {
			pr = productIdx[item.product_id]
			if (needToIdxArr[item.idx] && pr && (pr.needToIdxArr == null || pr.needToIdxArr.length < needToIdxArr[item.idx].length)) {
				pr.needToIdxArr = needToIdxArr[item.idx]
			}
			if (item.from_row && pr && pr.needFromIdx == null) {
				pr.needFromIdx = item.from_row
			}
		})
		chartProductIdxArr = nc.createIndexArray(chartData, 'product_id')
		if (stockChart) {
			currentData = gridData
			updateChartProduct([])
		}
	}
	state.grid.gridIdx['company-supplier-company_id'] = nc.createIndex(state.grid['company-supplier'].data, 'company_id').index
	const companyIdx = state.grid.gridIdx['company-supplier-company_id']
	gridDataWeekly = grid?.product_need_weekly?.data
	if (gridDataWeekly) {
		data = gridDataWeekly
		for (let idx = 1; idx <= data.length; idx++) {
			item = data[idx - 1]
			item.calc = gridData[item.calc_idx - 1]
			item.calc.week_idx = idx
			item.product_id = item.calc.product_id
			item['co-supplier'] = companyIdx[item.company_id] // TODO: move index creation and this to nc-grid-common.js
			pr = productIdx[item.product_id]
			item.pr = pr
			if (pr.error && pr.error !== item.error) {
				if (item.error) {
					item.error += '\n' + pr.error
				} else {
					item.error = pr.error
				}
			}
			if (item.error) {
				rec.error_count_week++
			}
			if (!hasNeedWeekProductIdx[item.product_id] && (item.amount > 0 || (pr && (pr.going_amount < 0 || pr.free_amount < 0)))) {
				hasNeedWeekProductIdx[item.product_id] = true
			}
			if (item.calc.first_row && item.calc.last_row && item.balance === 0) {
				if (pr && pr.json_data.coming_amount === 0 && pr.json_data.going_amount === 0 && pr.json_data.free_amount === 0) {
					item.is_empty = true
				}
			}
			if (item.date && item.date >= 0 && item.date < chartStartDate) {
				item.before_today = true
			}
		}
	}
	gridDataProductIdxArr = nc.createIndexArray(gridData, 'product_id')
	gridDataProductIdx = nc.createIndex(gridData, 'product_id', null, 'no-warning').index // create first product index
	gridDataWeeklyProductIdx = nc.createIndex(gridDataWeekly, 'product_id', null, 'no-warning').index
	if (gridDataWeekly) {
		let item, rec, arrCalc
		for (let idx = 1; idx <= gridDataWeekly.length; idx++) {
			item = gridDataWeekly[idx - 1]
			pr = productIdx[item.product_id]
			arrCalc = pr && pr.needToIdxArr
			if (arrCalc) {
				const newArr = []
				const newArrIdx = {}
				arrCalc.forEach(rowIdx => {
					rec = gridData[rowIdx - 1]
					if (!newArrIdx[rec.product_id] && gridDataWeeklyProductIdx[rec.product_id]) {
						newArrIdx[rec.product_id] = true
						newArr.push(gridDataWeeklyProductIdx[rec.product_id].idx)
					}
				})
				needToWeekIdxArr[idx] = newArr
			}
			if (pr && pr.needFromIdx) {
				const newArr = []
				rec = gridData[pr.needFromIdx - 1]
				if (!gridDataWeeklyProductIdx[rec.product_id]) {
					newArr.push(gridDataWeeklyProductIdx[rec.product_id].idx)
				}
				needFromWeekIdxArr[idx] = newArr
			}
		}
	}
	rec.error_count = rec.weekly_need ? rec.error_count_week : rec.error_count_all
	needViewChanged()
}

function findAllUpper(upperProductIdx) {
	if (rec.weekly_need || rec.select_upper !== true) {
		return
	}
	let foundUpper = false // add upper-level products and repeat until no upper-level is found
	let arr, item2
	const alreadyFound = {}
	do {
		foundUpper = false
		for (const productId in upperProductIdx) {
			if (!alreadyFound[productId]) {
				alreadyFound[productId] = true
				arr = gridDataProductIdxArr[productId]
				arr.forEach(item => {
					if (item.from_row) {
						item2 = currentData[item.from_row - 1]
						if (!upperProductIdx[item2.product_id]) {
							foundUpper = true
							upperProductIdx[item2.product_id] = true
						}
					}
				})
			}
		}
	} while (foundUpper === true)
}

function setTreeChart(treeArr) {
	if (treeArr.length > 1) {
		let longestChildrenItem // find treeArr item with most children and use only it
		treeArr.forEach(item => {
			if (!longestChildrenItem) {
				longestChildrenItem = item
				if (!longestChildrenItem.children) {
					longestChildrenItem.children = []
				}
			} else if (item.children && item.children.length > longestChildrenItem.children.length) {
				longestChildrenItem = item
			}
		})
		treeArr = [longestChildrenItem]
	}
	chartPref.product_tree.series.length = 1
	chartPref.product_tree.series[0].left = '15%'
	// chartPref.product_tree.series[0].right = '15%'
	chartPref.product_tree.series[0].data = treeArr
	updateChart()
}

function activateUpper(item, arr, arrIdx) {
	if (rec.select_upper !== true) {
		return
	}
	if (arrIdx == null) {
		arrIdx = {}
	}
	let fromArr
	if (rec.weekly_need === true) {
		fromArr = needFromWeekIdxArr[item.idx]
	} else {
		fromArr = item.from_row && [item.from_row]
	}
	if (fromArr) {
		let item2
		fromArr.forEach(idx => {
			if (!arrIdx[idx]) {
				arrIdx[idx] = true
				arr.push(idx)
				item2 = currentData[idx - 1]
				if (item2.from_row) {
					activateUpper(item2, arr, arrIdx) // recursive call
				}
			}
		})
	}
}

function activateLower(arr) {
	if (rec.select_lower !== true) {
		return arr
	}
	let idxArr
	if (rec.weekly_need === true) {
		idxArr = needToWeekIdxArr
	} else {
		idxArr = needToIdxArr
	}
	const newArr = []
	const newIdx = {}

	function addChildren(arr, parent) {
		let child, item
		arr.forEach(idx => {
			if (!newIdx[idx]) {
				newIdx[idx] = true
				newArr.push(idx)
			}
			if (!parent.children) {
				parent.children = []
			}
			item = currentData[idx - 1]
			child = { name: item.pr.name + ' (' + item.product_id + ')', value: nc.round(item.balance, 2), parent: parent.product_id }
			parent.children.push(child)
			if (idxArr[idx]) {
				// idxArr[idx] is array of child indexes
				addChildren(idxArr[idx], child) // recursive call, child is new parent
			}
		})
	}

	const treeArr = []
	arr.forEach(idx => {
		const tree = { children: [] }
		addChildren([idx], tree) // add children if original array element has children
		treeArr.push(tree.children[0])
	})
	setTreeChart(treeArr)
	newArr.sort((a, b) => a - b) // sort by number, not necessary for slickgrid but may be needed other grids
	return newArr
}

// eslint-disable-next-line no-unused-vars
function doesExternalFilterPass(node) {
	return itemFound(node.data)
}

let findTextArr
function createFindTextArr(text) {
	findTextArr = text
		.toLowerCase()
		.split(' ')
		.map(text => text.trim())
		.filter(text => text.length > 0)
}

function itemFound(item) {
	let allow = true
	if (rec.has_error && !item.error) {
		allow = false
	}
	if (allow && rec.only_need) {
		if (rec.weekly_need) {
			allow = hasNeedWeekProductIdx[item.product_id]
		} else {
			allow = hasNeedProductIdx[item.product_id]
		}
	}
	if (allow && rec.hide_empty && item.is_empty) {
		allow = false
	}
	if (allow && !rec.weekly_need && findProductIdx[item.product_id]) {
		return true
	}
	if (allow && findText) {
		if (!findTextArr) {
			createFindTextArr(findText)
		}
		for (let text of findTextArr) {
			allow =
				item.product_id.toLowerCase().includes(text) ||
				(item.name && item.name.toLowerCase().includes(text)) ||
				(item.calc?.name && item.calc.name.toLowerCase().includes(text)) ||
				(item.production_order_id && item.production_order_id.toLowerCase().includes(text)) ||
				(item.order_id && item.order_id.toLowerCase().includes(text)) ||
				(item.company_id && item.company_id.toLowerCase().includes(text)) ||
				false
			if (allow) {
				return true
			}
		}
	}
	return allow === true
}

let prevFindText = ''
let prevCheckBoxValue = 0
function updateFilter(findText_, checkBoxValue) {
	// console.time('* find products')
	findText = findText_.toLowerCase()
	if (prevFindText !== findText) {
		createFindTextArr(findText)
	}
	if (rec.weekly_need === true) {
		nc.grid.updateFilter(state, currentArea)
		return
	}
	let found = false
	const doFind = currentData && findText.length >= rec.find_min_characters
	const activatedRowIdxArr = []
	findProductIdx = {}
	if (doFind) {
		let item
		for (let idx = 1; idx <= currentData.length; idx++) {
			item = currentData[idx - 1]
			item.found = false
			if (itemFound(item)) {
				item.found = true
				findProductIdx[item.product_id] = true
				if (activatedRowIdxArr.length <= rec.max_activate_row_count + 1) {
					activatedRowIdxArr.push(idx) // item.idx is not set if called from callback, use idx instead
				}
				if (found === false) {
					if (item.level > 1) {
						found = true
					} else {
						found = 0
					}
				}
			} else if (item.from_row_text && item.from_row_text.toLowerCase().includes(findText)) {
				item.found = true
				findProductIdx[item.product_id] = true
			}
		}
	}
	if (found === true) {
		findAllUpper(findProductIdx)
	}
	// console.timeLog('* find products')
	// if (doFind || !findText || (findText.length >= rec.find_min_characters && findText !== prevFindText)) {
	nc.grid.updateFilter(state, currentArea)
	if (activatedRowIdxArr.length > 0 && activatedRowIdxArr.length <= rec.max_activate_row_count) {
		activateRows(state, currentArea, activatedRowIdxArr, 'no-scroll', unselectOther) // scroll-last
	}
	prevFindText = findText
	prevCheckBoxValue = checkBoxValue
	// console.timeEnd('* find products')
}

function needViewChanged(callCheckBoxClicked) {
	rec.weekly_need = rec.need_view == 'balance' ? true : false
	if (rec.weekly_need) {
		currentArea = 'product_need_weekly'
		currentData = gridDataWeekly
	} else {
		currentArea = 'product_need'
		currentData = gridData
	}
	if (callCheckBoxClicked !== false) {
		checkBoxClicked()
	}
}

let prevWeeklyNeed = rec.weekly_need
function checkBoxClicked() {
	needViewChanged(false)
	const checkBoxValue = rec.hide_empty + rec.only_need * 10 + rec.has_error * 100 + rec.weekly_need * 1000
	if (rec.weekly_need) {
		rec.error_count = rec.error_count_week
	} else {
		rec.error_count = rec.error_count_all
	}
	if (prevWeeklyNeed !== rec.weekly_need) {
		prevWeeklyNeed = rec.weekly_need
		updateFilter(rec.find_text, checkBoxValue) // update immediately
		/* if (rec.weekly_need === true) {
			return
		} */
		if (selectedItemArr[0]) {
			const productIdArr = selectedItemArr.map(item => item.product_id)
			let idx
			if (rec.weekly_need) {
				idx = gridDataWeeklyProductIdx
			} else {
				idx = gridDataProductIdx
			}
			selectedItemArr.length = productIdArr.length
			for (let index = 0; index < productIdArr.length; index++) {
				selectedItemArr[index] = idx[productIdArr[index]]
			}
			selectGridRow(areaName, selectedItemArr, 'scroll-first') // we are selecting only product's first balance row, they never cause lower or upper level selection
		}
	} else if (checkBoxValue !== prevCheckBoxValue) {
		updateFilter(rec.find_text, checkBoxValue) // do not wait for debounce, update immediately
	} else if (rec.find_text !== prevFindText) {
		updateFunc(rec.find_text, checkBoxValue)
	}
}

// eslint-disable-next-line no-unused-vars
function updateLower() {
	if (rec.weekly_need) {
		areaName = 'product_need_weekly'
	} else {
		areaName = 'product_need'
	}
	selectedItemArr = nc.grid.activatedRecordArr(state, areaName)
	if (selectedItemArr.length > 0) {
		if (rec.select_lower == false) {
			selectedItemArr.length = 1
		}
		selectGridRow(areaName, selectedItemArr)
	}
}

function update(action, reload) {
	const prevReload = state.rec.reload_data
	if (reload) {
		state.rec.reload_data = true
	}
	if (action === 'update') {
		if (rec.view === 'product-need') {
			nc.callServer(
				state,
				'calc/product-need',
				{
					parameter: {
						reload_data: rec.reload_data,
						create_schedule: rec.create_schedule,
						virtual_production_order: rec.virtual_production_order,
						estimate_order: rec.estimate_order,
						old_work_date: rec.old_work_date,
						max_need_down_loop_count: rec.max_need_down_loop_count,
						minimum_stock: rec.minimum_stock,
						action_limit: rec.action_limit,
						negative_start_balance: rec.negative_start_balance,
						negative_end_balance: rec.negative_end_balance,
						critical_date: rec.critical_date,
						only_sales_need: rec.only_sales_need
					}
				},
				ret => {
					if (reload) {
						state.rec.reload_data = prevReload
					}
					updateData(ret)
					updateFilter(prevFindText, prevCheckBoxValue)
				}
			)
		}
	}
}
