// form/nc/nc-work-center/nc-work-center.js
import { hasWorkInProgress, hasIncompleteWork, canStartWork, previousPersonWork, workInProgress, canStartAmount, updateCurrentAndPersonFunction } from './nc-work-center-util.js'
import { handleIncrementalResponse, initializeIncrementalUpdate, resetIncrementalUpdate } from './change-data.js'
import {
	amountStock1ToStock2,
	amountStock2ToStock1,
	amountStock1ToDesign1,
	amountStock1ToDesign2,
	amountDesign2ToStock1,
	amountDesign1ToStock1
} from '/plugin/convert/product-convert.js'

const updateCurrentAndPerson = updateCurrentAndPersonFunction(state, nc, dt, message)
const recData = nc.recData
// eslint-disable-next-line no-unused-vars
const mapHover = ref([]) // stock map svg
const mapSelected = ref([])
const findFieldAllowNull = nc.invertArray(rec.find_field_allow_null)
const gridUpdateTimeout = 50
let findText = ''
let loadCount = 0

const activeWorkStates = {
	setup: true,
	working: true,
	material: true,
	pause: true
}

// Button active state maps for proper press state handling
const buttonActiveMap = {
	// Person button active states - simplified: each state maps to its active button
	person: {
		ready: 'ready',
		edit: 'edit',
		stop: 'stop',
		setup: 'setup',
		working: 'working',
		material: 'material',
		pause: 'pause'
	},
	// Machine button active states - simplified: each state maps to its active button
	machine: {
		'': 'stop', // Empty state maps to stop
		stop: 'stop',
		material: 'material',
		setup: 'setup',
		running: 'running'
	},
	// Unproductive button active states - simplified: each state maps to its active button
	unproductive: {
		back: 'back',
		break: 'break',
		eating: 'eating',
		exit: 'exit'
	}
}

// Button array selection maps
const buttonArrayMap = {
	unproductive: 'unproductive_button_group',
	person_state_unproductive: 'person_state_unproductive',
	person: 'person_button_group',
	machine: 'machine_button_group'
}

updateCurrentAndPerson()

onMounted(() => {
	rec.view = 'main'
	rec.connection = ''
	initializeIncrementalUpdate(state)
	if (!state.grid?.nats_name) {
		update(null, null, 1) // fill the grid with data by calling the server
	}
})

const previousInputValue = {}
function setPreviousInputValue(rec, restore) {
	// return
	if (restore) {
		if (rec.current.event === 'save') {
			// returned event was save, do not restore values because they have been changed on server
			return
		}
		if (typeof previousInputValue.produced_amount === 'number') {
			setTimeout(() => {
				// timeout is needed to update after other updates
				rec.current.produced_amount = previousInputValue.produced_amount
				rec.current.failed_amount = previousInputValue.failed_amount
			}, 10)
		}
	} else {
		if (typeof rec.current.produced_amount === 'number') {
			previousInputValue.produced_amount = rec.current.produced_amount
			previousInputValue.failed_amount = rec.current.failed_amount
		}
	}
}

// eslint-disable-next-line no-unused-vars
function errorMessagePosition(property, message) {
	if (property === 'width') {
		return nc.getTextWidth(message, '7em', 'bolder')
	} else if (property === 'margin-left') {
		return `calc(63em - ${nc.getTextWidth(message, '7em', 'bolder')} / 2)`
	}
	console.error(`unknown errorMessagePosition property ${property}`)
}
// eslint-disable-next-line no-unused-vars
function gridHeight(gridId) {
	if (rec.current.person_state === 'material') {
		if (gridId === 'work') {
			// Reduce work grid to less rows when material state is on, leave 50vh to materials
			return rec.tab_open ? 'calc(50vh - 33.25em)' : 'calc(50vh - 30em)'
		}
		// if (gridId === 'material' || gridId === 'product_material' || gridId === 'product_material_actual' || gridId === 'work_phase' || gridId === 'work_phase_actual' || gridId === 'work_phase_actual_open') {
		return '50vh'
	}
	// Normal heights when material state is off
	if (gridId === 'work') {
		return rec.tab_open ? 'calc(100vh - 47.25em)' : 'calc(100vh - 44em)'
	}
	// if (gridId === 'material' || gridId === 'product_material' || gridId === 'product_material_actual' || gridId === 'work_phase' || gridId === 'work_phase_actual' || gridId === 'work_phase_actual_open') {
	return '22em'
}

function buttonGroupItem(eventGroup, value) {
	// Find button in appropriate array using map lookup
	const arrayName = buttonArrayMap[eventGroup]
	if (!arrayName) {
		return null
	}
	const array = arr[arrayName]
	return array.find(item => item.value === value)
}

function isContinue() {
	// Check for break states only (continue_break_over is not a break state)
	return rec.current.started_amount === 0 && previousPersonWork(rec) // returns previousWork if found
}
function isCancel() {
	// Check if this is a cancel scenario: started work but no production yet
	return rec.current.started_amount > 0 && rec.current.produced_amount === 0 && rec.current.failed_amount === 0
}
function isPersonEnabled() {
	// Check if person buttons should be enabled
	return rec.current.enterable_state !== 'none'
}
function isActiveWorkState() {
	// Check if person is in an active work state (not ready, edit, stop, or empty)
	return activeWorkStates[rec.current.person_state]
}
function isMachineRunning() {
	// Check if machine is currently running
	return rec.current.machine_state === 'run'
}

function buttonClick(eventGroup, eventState) {
	if (eventState === 'person_in_production') {
		rec.person_in_production = !rec.person_in_production
		// Store current selected work row and separate machine run state to preserve them after filtering
		if (rec.person_in_production && rec.selected_work_row > 0) {
			const currentRowData = state.grid?.work?.data?.[rec.selected_work_row - 1]
			storedFilterState = {
				currentProductionOrderId: currentRowData?.production_order_id,
				currentSeparateMachineRun: rec.separate_machine_run,
				currentErrorMessage: rec.error_message
			}
		} else {
			storedFilterState = null
		}
		updateFilter() // calls updateCurrentAndPerson()
		return
	}
	if (eventState === 'separate_machine_run') {
		if (rec.separate_machine_run > 0) {
			rec.separate_machine_run = 0
		} else {
			rec.separate_machine_run = 1
		}
		return
	}
	// If clicking the same active state, do nothing (except for continue actions)
	if (eventGroup === 'person' && rec.current.person_state === eventState) {
		return
	}
	if (eventGroup === 'machine' && rec.current.machine_state === eventState) {
		return
	}
	if (eventState === 'start_continue' && rec.current.person_state !== rec.constant.phase_state.ready) {
		// Vue bug in html, v-if button still sends click event even if not in DOM
		if (rec.person_in_production) {
			eventState = 'ready'
		} else {
			eventState = 'back'
			eventGroup = 'unproductive'
		}
	}
	// Handle continue/ready button functionality
	if (eventGroup === 'person' && (eventState === 'start_continue' || eventState === 'ready')) {
		if (eventState === 'start_continue') {
			// Default to ready for first button clicks, handle continue/start logic
			if (isContinue()) {
				// Find the previous work entry and copy started_amount
				const previousWork = previousPersonWork(rec)
				if (previousWork) {
					rec.current.started_amount = previousWork.started_amount
				} else {
					message.setWarning(state, 'No previous work found to continue')
					return
				}
			} else {
				rec.current.started_amount = rec.current.calc.can_start_amount
			}
			callEvent('update amount', rec.constant.phase_state.edit)
			return
		} else if (eventState === 'ready') {
			if (rec.current.person_state === rec.constant.phase_state.edit || rec.current.person_state === rec.constant.phase_state.ready) {
				return // click to self, no action
			}
			// Determine what state to call based on current conditions using person_state
			if (isActiveWorkState()) {
				// Person is currently in material/setup/working state -> call stop
				eventState = 'stop'
			} else if (rec.current.person_state === 'stop') {
				// Person is currently stopped -> go to ready
				eventState = 'ready'
			} else {
				console.error(`buttonClick person ready: unknown state ${rec.current.person_state}`)
			}
		}
	}
	if (eventGroup === 'unproductive') {
		if (eventState === rec.current.unproductive_state) {
			// Prevent clicking ready button when already pressed
			return
		}
		// Check if machine is running and warn for exit states
		if (eventState === 'exit' && isMachineRunning()) {
			rec.warning_message = `Cannot exit while machine is running (${rec.current.machine_state}). Please stop the machine first.`
			return
		}
		// Prevent clicking the same active state
		if (rec.current.person_state === eventState) {
			return
		}
		// Update person_state and person_state_unproductive when clicking unproductive buttons
		if (arr.unproductive_button_group.some(item => item.value === eventState)) {
			rec.current.person_state = eventState
			rec.person_state_unproductive = eventState
		}
	}
	// Handle cancel scenario in frontend
	if (eventGroup === 'save') {
		// Check if we're in cancel mode (started work but no production yet)
		if (isCancel()) {
			rec.current.started_amount = 0
			eventGroup = 'update amount'
			eventState = rec.constant.phase_state.edit
		} else if (rec.current.produced_amount + rec.current.failed_amount >= rec.current.started_amount && rec.current.started_amount > 0) {
			eventState = 'finish'
		} else {
			eventState = 'ready'
		}
	}
	callEvent(eventGroup, eventState)
}
// eslint-disable-next-line no-unused-vars
function buttonText(eventGroup, value) {
	if (eventGroup === 'save') {
		// Cancel scenario: started work but no production yet
		if (isCancel()) {
			return hdr.work_center.button.cancel
		}
		// Finish scenario: completed amounts meet or exceed started amount
		if (rec.current.produced_amount + rec.current.failed_amount >= rec.current.started_amount && rec.current.started_amount > 0) {
			const totalCompleted = rec.current.produced_amount + rec.current.failed_amount
			return hdr.work_center.button.finish + ' ' + totalCompleted
		}
		// Default save scenario
		return hdr.work_center.button.save
	}
	// Handle special cases before general buttonGroupItem lookup
	// Handle unproductive button special cases
	if (eventGroup === 'unproductive') {
		// Handle exit_reason special case
		if (value === 'exit' && rec.person_exit_reason) {
			const item = arr.person_exit_reason.find(item => item.value === rec.person_exit_reason)
			if (item && item.show) {
				return item.show
			}
		}
	}
	// Handle person button logic (both ready button and state-based placeholder)
	if (eventGroup === 'person') {
		if (value === 'start_continue') {
			// Handle continue/start button text when Start amount is 0
			const previousWork = isContinue()
			if (previousWork) {
				const amount = previousWork ? previousWork.started_amount : 0
				return hdr.work_center.button.continue + ' ' + amount
			} else {
				return hdr.work_center.button.start + ' ' + rec.current.calc.can_start_amount
			}
		} else if (value === 'ready') {
			// Auto-select state based on conditions for placeholder button
			if (rec.current.person_state === 'stop') {
				// Person is stopped -> show Stop (pressed)
				return arr.person_state_stop.find(item => item.value === 'stop').show
			} else if (isActiveWorkState()) {
				// Any person state active (material, setup, working) -> show Stop
				return arr.person_state_stop.find(item => item.value === 'stop').show
			} else if (rec.current.person_state === 'edit' && hasIncompleteWork(rec)) {
				// Backend set state to edit after stopping work, but work is still in progress -> show Edit
				return arr.person_state_stop.find(item => item.value === 'edit').show
			} else if (hasIncompleteWork(rec)) {
				// Work in progress but person not in active state -> show Edit
				return arr.person_state_stop.find(item => item.value === 'edit').show
			} else {
				// No work in progress or person is ready -> check Start amount
				// if (rec.current.calc.can_start_amount <= 0) {
				// When Start amount > 0, show Ready (pressed state)
				return arr.person_button_group.find(item => item.value === 'ready').show
				// }
			}
		}
	}
	if (eventGroup === 'machine' && value === 'stop') {
		return arr.machine_stop_type[rec.machine_stop_type_idx].show
	}
	// General buttonGroupItem lookup after special cases
	const item = buttonGroupItem(eventGroup, value)
	if (item && item.show) {
		// Handle ready→stop transformation when person is working
		if (item.show === 'Ready' && rec.person_in_production) {
			return hdr.work_center.button.stop
		}
		return item.show
	}
	console.error(`buttonText: unknown eventGroup ${eventGroup} value ${value}`)
	return value
}
// eslint-disable-next-line no-unused-vars
function buttonEnabled(eventGroup, eventState) {
	// Button pressed state disables button until server response
	if (!isPersonEnabled()) {
		rec.current.show_start_continue = false
		return false
	}
	if (eventState === 'person_in_production') {
		return true // toggle button is always active
	}
	if (eventGroup === 'unproductive') {
		// Enable unproductive buttons if person has unfinished works and is not in production
		return rec.error_message === '' // enable only when no error
	}
	// Check if a work row is selected - disable all buttons if no row is selected
	if (rec.selected_work_row <= 0) {
		rec.current.show_start_continue = false
		return false
	}
	if (rec.person_in_production) {
		// Disable all buttons when in unproductive state (not back), except unproductive buttons themselves
		// 'back' state should allow productive buttons to be clicked
		if (rec.current.unproductive_state && rec.current.unproductive_state !== 'back' && eventGroup !== 'unproductive') {
			return false
		}
	} else {
		rec.current.show_start_continue = false
	}
	// Show start/continue button
	if (eventGroup === 'save') {
		// Enable save when started_amount > 0 and amounts are valid
		if (rec.current.produced_amount + rec.current.failed_amount > rec.current.started_amount) {
			return false
		}
		if (rec.current.started_amount === 0) {
			return false
		}
		return true
	}
	if (eventState === 'separate_machine_run') {
		if (rec.current.separate_machine_run > 0) {
			return true
		}
		return false
	}
	if (eventGroup === 'machine' && eventState !== 'separate_machine_run') {
		// Enable machine buttons only when person is enabled AND separate machine run is enabled
		// Machine buttons should NOT be disabled by unfinished work phase errors
		return isPersonEnabled() && rec.separate_machine_run
	}
	// All person buttons (including stop) use same logic
	if (eventGroup === 'person') {
		if (eventState === 'ready') {
			rec.current.work_in_progress = workInProgress(rec)
			if (rec.current.work_in_progress) {
				rec.current.show_start_continue = false
			} else {
				if (isContinue()) {
					// eventState 'ready' is ready, stop, edit, continue button
					rec.current.show_start_continue = true
					return true // enable continue button
				}
				rec.current.show_start_continue = canStartWork(rec)
			}
		}
		// When started_amount is 0 (start or continue is active), disable other person buttons
		if (rec.current.started_amount === 0) {
			return false
		}
		return canStartWork(rec) || hasWorkInProgress(rec)
	}
	return true
}
// eslint-disable-next-line no-unused-vars
function buttonClass(eventGroup, eventState) {
	// allowed event_group is in plugin/nc-calc-server/constant.json
	if (eventGroup === 'unproductive') {
		// Use person_state for unproductive buttons to get proper active state
		const activeButton = buttonActiveMap.unproductive[rec.current.unproductive_state]
		return activeButton === eventState ? 'active' : ''
	}
	if (eventGroup === 'person') {
		if (eventState === 'person_in_production') {
			return rec.person_in_production ? 'active' : ''
		}
		// Handle first button (ready value) - represents dynamic ready/edit/stop button
		if (eventState === 'ready') {
			// Special case for Ready button when Start amount > 0
			const activeButton = buttonActiveMap.person[rec.current.person_state]
			return rec.constant.person_event_stopped[activeButton] ? 'active' : ''
		}
		// Regular person buttons - check against person_state
		const activeButton = buttonActiveMap.person[rec.current.person_state]
		return activeButton === eventState ? 'active' : ''
	}
	if (eventGroup === 'machine') {
		if (eventState === 'separate_machine_run') {
			return rec.separate_machine_run > 0 ? 'active' : ''
		}
		// Use machine_state for machine buttons
		// Handle empty state by mapping to 'stop' first, then check against eventState
		const machineState = rec.current.machine_state || 'stop'
		const activeButton = buttonActiveMap.machine[machineState]
		return activeButton === eventState ? 'active' : ''
	}
	if (eventGroup === 'save') {
		// Cancel scenario: started work but no production yet
		if (isCancel()) {
			return '' // Cancel button uses default style (not primary)
		}
		// Finish scenario: completed amounts meet or exceed started amount
		if (rec.current.produced_amount + rec.current.failed_amount >= rec.current.started_amount && rec.current.started_amount > 0) {
			return 'primary' // Finish button uses primary style
		}
		// Regular save scenario
		return 'primary' // Save button uses primary style
	}
	console.error(`unknown buttonClass eventGroup ${eventGroup} eventState ${eventState}`)
}
// eslint-disable-next-line no-unused-vars
function buttonIcon(eventGroup, value) {
	if (eventGroup === 'save') {
		// Cancel scenario: started work but no production yet
		if (isCancel()) {
			return rec.button_icon.cancel
		}
		// Finish scenario: completed amounts meet or exceed started amount
		if (rec.current.produced_amount + rec.current.failed_amount >= rec.current.started_amount && rec.current.started_amount > 0) {
			return rec.button_icon.finish
		}
		return rec.button_icon.done
	}
	if (eventGroup === 'separate_machine_run') {
		return rec.separate_machine_run === 1 ? 'unlock' : 'lock'
	}
	// Handle special cases before general buttonGroupItem lookup
	// Handle person buttons (first button) - same logic as buttonText
	if (eventGroup === 'person' && value === 'ready') {
		// Auto-select value based on conditions (same as buttonText logic)
		if (rec.current.person_state === 'stop') {
			// Person is stopped -> show Stop icon
			const stopItem = arr.person_state_stop && arr.person_state_stop.find(item => item.value === 'stop')
			return stopItem?.icon || rec.button_icon.stop
		} else if (isActiveWorkState()) {
			// Any person state active (material, setup, working) -> show Stop icon
			const stopItem = arr.person_state_stop && arr.person_state_stop.find(item => item.value === 'stop')
			return stopItem?.icon || rec.button_icon.stop
		} else if (rec.current.person_state === 'edit' && hasIncompleteWork(rec)) {
			// Backend set state to edit after stopping work, but work is still in progress -> show Edit icon
			const editItem = arr.person_state_stop && arr.person_state_stop.find(item => item.value === 'edit')
			return editItem?.icon || rec.button_icon.edit
		} else if (hasIncompleteWork(rec)) {
			// Work in progress but person not in active state -> show Edit icon
			const editItem = arr.person_state_stop && arr.person_state_stop.find(item => item.value === 'edit')
			return editItem?.icon || rec.button_icon.edit
		} else {
			// No work in progress or person is ready -> check Start amount
			if (rec.current.calc.can_start_amount <= 0) {
				// When Start amount > 0, show Ready icon (pressed state)
				const readyButtonItem = arr.person_button_group && arr.person_button_group.find(item => item.value === 'ready')
				return readyButtonItem?.icon || rec.button_icon.ready
			}
			// Default Ready icon for continue/start scenario
			const readyItem = arr.person_state_stop && arr.person_state_stop.find(item => item.value === 'ready')
			return readyItem?.icon || rec.button_icon.ready
		}
	}
	if (eventGroup === 'machine' && value === 'stop') {
		return arr.machine_stop_type[rec.machine_stop_type_idx]?.icon || rec.button_icon.stop
	}
	// Honor person_state_unproductive dropdown selection: if the unproductive button corresponds to
	// the selected person_state_unproductive, use its icon.
	if (eventGroup === 'unproductive') {
		if (rec.person_state_unproductive) {
			const item = arr.person_state_unproductive && arr.person_state_unproductive.find(item => item.value === rec.person_state_unproductive)
			if (item && value === rec.person_state_unproductive && item.icon) {
				return item.icon
			}
		}
		// If Exit button and an exit reason is selected, use reason icon when available
		if (value === 'exit' && rec.person_exit_reason) {
			const item = arr.person_exit_reason && arr.person_exit_reason.find(item => item.value === rec.person_exit_reason)
			if (item && item.icon) {
				return item.icon
			}
		}
	}
	// General buttonGroupItem lookup after special cases
	const item = buttonGroupItem(eventGroup, value)
	if (item && item.icon) {
		// Handle ready→stop transformation when person is working
		if (item.value === 'ready' && rec.person_in_production) {
			// Find stop item in person_state_stop array
			const stopItem = arr.person_state_stop && arr.person_state_stop.find(i => i.value === 'stop')
			return stopItem?.icon || rec.button_icon.stop
		}
		return item.icon
	}
	// Fallback: try direct lookup from button arrays
	if (eventGroup === 'person' && value) {
		const personItem = arr.person_button_group && arr.person_button_group.find(i => i.value === value)
		if (personItem && personItem.icon) {
			return personItem.icon
		}
	}
	if (eventGroup === 'machine' && value) {
		const machineItem = arr.machine_button_group && arr.machine_button_group.find(i => i.value === value)
		if (machineItem && machineItem.icon) {
			return machineItem.icon
		}
	}
	// Last fallback
	return value
}

// Called from layout dropdown change when unproductive dropdown selection changes
function personStateUnproductiveChanged() {
	// keep current UI state in sync for immediate feedback
	if (rec.person_state_unproductive != null) {
		rec.current.person_state = rec.person_state_unproductive
		const item = arr.person_state_unproductive && arr.person_state_unproductive.find(item => item.value === rec.person_state_unproductive)
		if (item?.icon) {
			rec.person_state_icon = item.icon
		} else if (item?.value) {
			rec.person_state_icon = item.value
		}
	}
}

// Called from layout dropdown change when exit reason changes
function personExitReasonChanged() {
	// selecting an exit reason should reflect in the Exit button icon if applicable
	if (rec.person_exit_reason) {
		const item = arr.person_exit_reason && arr.person_exit_reason.find(item => item.value === rec.person_exit_reason)
		if (item?.icon) {
			rec.person_state_icon = item.icon
		} else {
			rec.person_state_icon = 'right-from-bracket'
		}
	}
}
// eslint-disable-next-line no-unused-vars
function inputEnabled(inputId) {
	if (rec.current.enterable_state === 'none') {
		return false
	}
	// Check if a work row is selected - disable all inputs if no row is selected
	if (rec.selected_work_row <= 0 || rec.error_message) {
		return false
	}
	// Disable all inputs when in unproductive state (not ready)
	if (rec.current.unproductive_state && rec.current.unproductive_state !== 'back') {
		return false
	}
	if (inputId === 'started_amount') {
		return rec.current.enterable_state === 'started' || rec.current.enterable_state === 'all'
	}
	if (inputId === 'produced_amount' || inputId === 'failed_amount') {
		// Enable fields only if started amount > 0, work is in progress, OR if user needs to correct amounts
		const needsCorrection = rec.current.produced_amount + rec.current.failed_amount > rec.current.started_amount
		return (
			rec.current.started_amount > 0 &&
			(rec.current.enterable_state === 'started' || rec.current.enterable_state === 'produced_failed' || rec.current.enterable_state === 'all' || needsCorrection)
		)
	}
	if (inputId === 'info') {
		if (rec.current.person_state !== 'ready' && rec.focus_field === '') {
			return true
		}
		return (
			rec.current.enterable_state === 'started' ||
			rec.current.enterable_state === 'produced_failed' ||
			rec.current.enterable_state === 'info' ||
			rec.current.enterable_state === 'all'
		)
	}
	console.error(`unknown inputEnabled inputId ${inputId}`)
}
// eslint-disable-next-line no-unused-vars
function inputClass(inputId) {
	if (inputId === 'started_amount') {
		return (rec.current.enterable_state !== 'none' && rec.current.person_state !== 'ready' && rec.current.started_amount == '') ||
			(rec.current.calc.can_start_amount < 0 && rec.current.started_amount > 0)
			? 'not-valid-input'
			: ''
	}
	if (inputId === 'produced_amount') {
		return rec.current.started_amount > 0 && rec.current.produced_amount > 0 && rec.current.started_amount < rec.current.produced_amount + rec.current.failed_amount
			? 'not-valid-input'
			: ''
	}
	if (inputId === 'failed_amount') {
		return rec.current.started_amount > 0 && rec.current.failed_amount > 0 && rec.current.started_amount < rec.current.produced_amount + rec.current.failed_amount
			? 'not-valid-input'
			: ''
	}
	console.error(`unknown inputClass inputId ${inputId}`)
}
// eslint-disable-next-line no-unused-vars
function inputFocus(inputId) {
	rec.focus_field = inputId
}
// eslint-disable-next-line no-unused-vars
function inputBlur(inputId) {
	rec.focus_field = ''
	inputChange(inputId, true)
	if (rec.person === '' || rec.current.started_amount === '') {
		updateCurrentAndPerson()
		return
	}
	// Only callEvent if started_amount changed (triggers state update)
	if (inputId === 'info' || (inputId === 'started_amount' && rec.current.started_amount !== rec.current.prev_started_amount)) {
		callEvent('update amount', rec.constant.phase_state.edit)
	}
}
function inputChange(inputId, zero) {
	// called from layout
	if (zero || rec.current.started_amount !== '') {
		rec.current.started_amount = parseInt(rec.current.started_amount, 10) // we optionally could allow floats (we need rec pref for that) - how to we parse them with decimal multiple separators?
		if (isNaN(rec.current.started_amount)) {
			if (zero) {
				rec.current.started_amount = 0
			} else {
				rec.current.started_amount = ''
			}
		} else if (!rec.allow_negative_amount && rec.current.started_amount < 0) {
			rec.current.started_amount = 0
		}
	}
	if (zero || rec.current.produced_amount !== '') {
		rec.current.produced_amount = parseInt(rec.current.produced_amount, 10)
		if (isNaN(rec.current.produced_amount)) {
			if (zero) {
				rec.current.produced_amount = 0
			} else {
				rec.current.produced_amount = ''
			}
		} else if (!rec.allow_negative_amount && rec.current.produced_amount < 0) {
			rec.current.produced_amount = 0
		}
	}
	if (zero || rec.current.failed_amount !== '') {
		rec.current.failed_amount = parseInt(rec.current.failed_amount, 10)
		if (isNaN(rec.current.failed_amount)) {
			if (zero) {
				rec.current.failed_amount = 0
			} else {
				rec.current.failed_amount = ''
			}
		} else if (!rec.allow_negative_amount && rec.current.failed_amount < 0) {
			rec.current.failed_amount = 0
		}
	}
	if (inputId === 'started_amount') {
		// Recalculate can_start_amount when started_amount changes to prevent false warnings
		// This simulates the server-side calculation from nc-calc-server-work-center.lua
		const rowRec = nc.grid.activatedRow(state, 'work')
		if (rowRec && rowRec.calc) {
			if (rowRec.calc.can_start_amount_orig == null) {
				// The can_start_amount_orig is the original server value before any started_amount deductions
				rowRec.calc.can_start_amount_orig = rowRec.calc.can_start_amount + rec.current.prev_started_amount
			}
			rec.current.calc.can_start_amount = rowRec.calc.can_start_amount_orig - rec.current.started_amount
			rowRec.calc.can_start_amount = rec.current.calc.can_start_amount
			// nc.grid.redrawRows(state, 'work')
			nc.grid.refreshCells(state, 'work')
			canStartAmount(state) // sets or clears warning message
		}
	}
	if (inputId === 'produced_amount' || inputId === 'failed_amount') {
		setPreviousInputValue(rec, false) // Save previous values after making changes to get int values
	}
	if (rec.current.started_amount === 0 || rec.current.started_amount === '' || rec.current.started_amount < rec.current.produced_amount + rec.current.failed_amount) {
		rec.current.startable = false
	} else {
		rec.current.startable = true
	}
}

// eslint-disable-next-line no-unused-vars
function dropdownChange(field) {
	// Generic handler for dropdown change events — centralizes common actions
	if (!field) {
		return
	}
	if (field === 'person') {
		// Selecting a person should update current person context and reload work row
		gridRowClicked('work', null, null, true)
		return
	}
	if (field === 'machine') {
		// Machine change affects work grid
		update('work')
		return
	}
	if (field === 'resource') {
		update('work')
		return
	}
	if (field === 'person_state_unproductive') {
		// Keep index in sync and update UI immediate feedback
		rec.person_state_unproductive_idx = nc.recordArrayIndex(arr.person_state_unproductive, 'value', rec.person_state_unproductive)
		personStateUnproductiveChanged()
		return
	}
	if (field === 'person_exit_reason') {
		rec.person_exit_reason_idx = nc.recordArrayIndex(arr.person_exit_reason, 'value', rec.person_exit_reason)
		if (typeof personExitReasonChanged === 'function') {
			personExitReasonChanged()
		}
		return
	}
	if (field === 'machine_stop_type') {
		rec.machine_stop_type_idx = nc.recordArrayIndex(arr.machine_stop_type, 'value', rec.machine_stop_type)
		return
	}
	// default: no-op for unknown field
}

let prevVirtual = rec.virtual_production_order
// eslint-disable-next-line no-unused-vars
function refresh() {
	if (rec.virtual_production_order && rec.virtual_production_order !== prevVirtual) {
		update(null, null, true)
	}
	prevVirtual = rec.virtual_production_order
	nc.grid.updateFilter(state, 'work') // causes a call to isExternalFilterPresent() and then to doesExternalFilterPass()
}

// eslint-disable-next-line no-unused-vars
function forceFullReload() {
	// todo: remove?
	// Reset incremental update to force full reload
	resetIncrementalUpdate(state)
	update(null, null, true)
}

// --- all clicks and events go to server ---
function callEvent(eventGroup, eventState) {
	if (!rec.person) {
		message.setWarning(state, 'Please select an person first')
		return
	}
	const rowRec = nc.grid.activatedRow(state, 'work')
	if (!rowRec && eventGroup !== 'unproductive') {
		message.setWarning(state, 'Please select a work row first')
		return // TODO: should not be able to click (disable button) if row is not selected
	}
	update('work', rowRec, false, { event_group: eventGroup, event_state: eventState, row_rec: rowRec })
}

function callParameter(reload, rowRec) {
	if (rowRec == null && reload !== 1) {
		rowRec = nc.grid.activatedRow(state, 'work')
	}
	const current = nc.clone(rec.current)
	if (current.started_amount === '') {
		current.started_amount = 0
	}
	current.separate_machine_run = rec.separate_machine_run // for backend parameter
	return {
		organization_id: connection(),
		prev_organization_id: rec.prev_organization_id,
		selected_work_row: rec.selected_work_row,
		current: current,
		create_schedule: rec.create_schedule,
		virtual_production_order: rec.virtual_production_order,
		old_work_date: rec.old_work_date,
		reload_data: reload != null ? reload : rec.reload_data,
		prev_resource: rec.prev_resource,
		selected_org_id_resource: rec.selected_org_id_resource,
		person: rec.person,
		production_order_id: (rowRec && rowRec.production_order_id) || '',
		resource: rec.resource,
		last_modify_id: rec.last_modify_id || '',
		return: {
			grid: [
				{
					name: 'work',
					tab: 'work-center'
				}
			]
		}
	}
}

function update(area, rowRec, reload, eventParam) {
	// called also from layout
	if (area != null) {
		if (rowRec == null) {
			rowRec = nc.grid.activatedRow(state, area)
		}
		if (rowRec != null) {
			rec.selected_work_row = rowRec.idx
		}
		const param = {
			rec: callParameter(reload, rowRec)
		}
		if (eventParam) {
			param.event = eventParam
		}
		nc.callServer(state, 'calc/work-center', param, updateAfter)
		return
	}
	// initial and update calls
	const prevReload = rec.reload_data
	const param = callParameter(reload, rowRec)
	nc.callServer(
		state,
		'calc/work-center',
		{
			rec: param
		},
		ret => {
			if (reload) {
				rec.reload_data = prevReload
			}
			updateAfter(ret)
		}
	)
}
function connection() {
	if (rec.connection === '') {
		return nc.currentOrganizationId() || ''
	}
	return rec.connection
}
function queryTab() {
	const call = { name: 'form/nc/nc-work-center/grid.json' }
	call.return = {
		grid: [
			/* {
					name: 'local',
					tab: rec.tab
				}, */
			{
				name: 'external',
				tab: rec.tab,
				organization_id: connection()
			}
		]
	}
	nc.callServer(state, 'query', call, ret => {
		console.debug(`🚀 ~ queryTab ~ ret:`, ret)
	})
}
// eslint-disable-next-line no-unused-vars
function emptyPersonState() {
	rec.person = ''
	updateCurrentAndPerson()
}
// eslint-disable-next-line no-unused-vars
function tabChanged(action) {
	if (action == 'open') {
		rec.tab_open = !rec.tab_open
		if (rec.tab_open === false) {
			rec.tab = 'work-center'
		}
		return
	}
	if (rec.tab === 'work-center') {
		update(null, null, true)
	} else {
		queryTab()
	}
}

// --- find ---
function updateFilter() {
	nc.grid.updateFilter(state, 'work') // triggers isExternalFilterPresent() and doesExternalFilterPass()
}
// eslint-disable-next-line no-unused-vars
function findChanged(text, action) {
	if (action === 'clear_find') {
		rec.find_text = ''
		findText = ''
		updateFilter()
		return
	}
	findText = (text && text.toString().toLowerCase()) || ''
	updateFilter()
}
// eslint-disable-next-line no-unused-vars
function isExternalFilterPresent() {
	return rec.virtual_production_order === false || rec.show_done === false || findText !== '' || !rec.person_in_production
}
// Variable to track filtering state and restoration
let storedFilterState = null
// eslint-disable-next-line no-unused-vars
function externalFilterAfter(node) {
	console.debug(`🚀 ~ externalFilterAfter`)
	// This function is called after external filtering is complete
	// Restore the previously selected row and separate machine run state after filtering
	if (storedFilterState) {
		const { currentProductionOrderId, currentSeparateMachineRun, currentErrorMessage } = storedFilterState
		let sameWorkOrderRestored = false
		if (state.grid?.work?.data && state.grid.work.data.length > 0) {
			if (currentProductionOrderId) {
				// Try to find the row with the same production_order_id after filtering
				const restoredIndex = state.grid.work.data.findIndex(row => row.production_order_id === currentProductionOrderId)
				if (restoredIndex >= 0) {
					rec.selected_work_row = restoredIndex + 1
					// Re-activate the row in the grid
					nc.grid.activateRows(state, 'work', rec.selected_work_row)
					sameWorkOrderRestored = true
				}
			}
			// If original row was not found, just set selected_work_row without activating to avoid state changes
			if (!sameWorkOrderRestored && state.grid.work.data.length > 0) {
				rec.selected_work_row = 1
			}
		}
		// Restore separate machine run state if it was lost
		if (currentSeparateMachineRun > 0 && rec.separate_machine_run === 0) {
			rec.separate_machine_run = currentSeparateMachineRun
		}
		// Restore error message if it was lost
		if (currentErrorMessage && !rec.error_message) {
			rec.error_message = currentErrorMessage
		}
		// Clear the stored state
		storedFilterState = null
	}
	updateCurrentAndPerson()
}
// eslint-disable-next-line no-unused-vars
function doesExternalFilterPass(node) {
	// For stock_location grid, apply material filtering
	if (node.grid?.name === 'stock_location') {
		return filterStockGrid(node)
	}
	let result = true
	if (rec.show_done === false && node.data.work_phase_state === rec.constant.phase_state.done) {
		result = false
	}
	if (rec.show_active === true && node.data.calc.can_start_amount <= 0) {
		result = false
	}
	if (rec.virtual_production_order === false && node.data.virtual) {
		result = false // todo: rec.create_schedule?
	}
	// When person is not in production, show only their relevant work orders
	if (!rec.person_in_production && result) {
		// Show work orders that the person has started (from work_phase_actual_open data)
		result = node.data.calc.started_person_array?.find(item => item.person_id === rec.person)
		/* const workPhaseActualOpen = state.data?.work_phase_actual_open || []
		const personStartedWorkOrderIdArr = []
		for (const workPhase of workPhaseActualOpen) {
			if (workPhase.person_id === rec.person && workPhase.production_order_id !== rec.constant.unproductive_id) {
				// Show all work phases the person has started, not just active ones
				personStartedWorkOrderIdArr.push(workPhase.production_order_id)
			}
		}
		const result = personStartedWorkOrderIdArr.includes(node.data.production_order_id)
		return result */
	}
	if (result) {
		let value
		for (const key of rec.find_field) {
			value = recData(node.data, key)
			if (typeof value !== 'string') {
				if (!findFieldAllowNull[key]) {
					console.error(`find field '${key}' is not a string, fix rec.find_field`)
				}
			} else if (value.toLowerCase().includes(findText)) {
				return true
			}
		}
		/* if (!findText) {
			// should not come here if findText is empty because isExternalFilterPresent() should return false
		} */
		result = false
	}
	return result
}

// eslint-disable-next-line no-unused-vars
function stockMapToggleSelected(param) {
	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 stockMapDoubleClick(param) {
	// console.debug('map double click', param.id, param.event)
}

// eslint-disable-next-line no-unused-vars
function importFile(fileRec, data) {
	if (fileRec.name == null) {
		return
	}
	let productId
	if (rec.selected_work_row > 0 && state.grid.work.data) {
		const phase = state.grid.work.data[rec.selected_work_row - 1]
		if (phase) {
			productId = phase.product_id
		}
	}
	if (!productId) {
		message.setWarning(state, `Import from file '${fileRec.name}' can't be done, please select a work row first`)
		return
	}
	let type = fileRec.type
	if (type === '') {
		type = peg.parseAfterLast(fileRec.name, '.')
		if (type === 'txt') {
			type = 'text/plain'
		} else if (type === 'md') {
			type = 'text/markdown'
		}
	}
	if (rec.allow_document_type && !rec.allow_document_type.includes(type)) {
		message.setWarning(state, `Import from file '${fileRec.name}' can't be done because it's type '${type}' is not allowed type`)
		return
	}
	/* Parent and child relation is 1 to 1, but only in name.
	The document can have more than one link. */
	const link = {
		parent_table: 'product',
		parent_id: productId,
		child_table: 'attachment'
		// child_id: att.record_id // set in form/nc/nc-document/save-link.json
		// info: ''
	}
	const attachment = {
		name: fileRec.name,
		attachment_type: 'document', // peg.parseAfter(type, '/'),
		// attachment_status: '',
		// file_version: '',
		// document_path: '',
		json_data: { content: data, file_size: fileRec.size, mime_type: type, last_modified: fileRec.lastModified, product_id: productId }, // fileRec.size is not the same as txt.length
		lnk: link
	}
	const param = {
		save: [{ table: 'attachment', data: [attachment], save_preference: 'form/nc/nc-document/save-document.json' }]
	}
	nc.callServer(state, 'save', param, ret => {
		let txt = data
		if (typeof txt == 'object' && txt[0]) {
			const length = txt.length
			txt = txt[0]
			if (txt[0]) {
				txt = txt[0]
			}
			if (typeof txt == 'object') {
				txt = JSON.stringify(txt)
			} else {
				txt = txt.toString()
			}
			txt = `${length} rows, data: [${txt.substring(0, 40)}, ...]`
		} else {
			txt = txt.toString()
			if (txt.length > 40) {
				txt = txt.substring(0, 40) + '...'
			}
		}
		message.setInfo(state, `Import from file '${fileRec.name}' to product '${productId}' was successful, content: ${txt}`)
		if (state.grid?.document?.data && ret?.save?.[0]?.data?.length > 0) {
			state.grid.document.data.unshift(ret.save[0].data[0]) // add to first row
			nc.grid.dataChanged(state.grid.document, state.grid.document.data)
			// state.grid.document = Object.assign({}, state.grid.document) // force redraw
			nc.grid.activateRows(state, 'document', 1)
		}
	})
}

// grid ui
let prevDocumentRow = -1
function setDocumentRow() {
	setTimeout(() => {
		// we need timeout to wait for grid to draw itself after the preview
		nc.grid.activateRows(state, 'document', prevDocumentRow)
	}, gridUpdateTimeout)
}
let documentShown = ''
function showDocumentPreview(viewType) {
	rec.document_preview_modal = false
	setTimeout(() => {
		if (viewType === 'modal') {
			rec.document_preview_modal = true
		} else {
			rec.document_preview_modal = false
		}
		documentShown = rec.document_path
	}, gridUpdateTimeout)
}

// eslint-disable-next-line no-unused-vars
function documentPreviewDoubleClick() {
	rec.document_preview_modal = 0
	openDocumentPreview('modal')
}

function openDocumentPreview(viewType, rowRec) {
	if (viewType === 'close') {
		viewType = 'preview'
	} else if (rec.tab_document === 'document_preview') {
		viewType = 'modal'
	}
	if (viewType !== 'preview' && !rec.document_preview_modal === false) {
		if (prevDocumentRow > 0) {
			setDocumentRow()
		}
		return
	}
	if (viewType === 'preview') {
		rec.document_preview_modal = false
	}
	if (!rowRec) {
		rowRec = nc.grid.activatedRow(state, 'document')
		if (!rowRec) {
			return
		}
	}
	prevDocumentRow = rowRec.idx
	if (rowRec.document_path === rec.document_path && rec.document_data.data !== '') {
		showDocumentPreview(viewType)
		return
	}
	if (rowRec.document_data?.data && rowRec.document_data.data !== '') {
		rec.document_path = rowRec.document_path
		rec.document_data = rowRec.document_data
		showDocumentPreview(viewType)
		return
	}
	let jsonData = rowRec.json_data
	if (typeof jsonData === 'string') {
		jsonData = JSON.parse(jsonData)
	}
	nc.callServer(
		state,
		'calc/work-center-document',
		{
			rec: {
				document_data: { data: '', viewer_show_type: rec.document_data.viewer_show_type, mime_type: jsonData.mime_type, document_name: rowRec.name },
				document_path: rowRec.document_path
			}
		},
		ret => {
			if (ret.rec?.document_data) {
				rowRec.document_data = ret.rec.document_data
			}
			setDocumentRow()
			showDocumentPreview(viewType)
		}
	)
}

let prevTabWorkPhase = rec.tab_work_phase
let prevTabDocument = rec.tab_document
function swapTabDocument(setStockMap) {
	if (!setStockMap && rec.tab_document === 'stock_map') {
		rec.tab_document = prevTabDocument
	} else {
		if (rec.tab_document !== 'stock_map') {
			prevTabDocument = rec.tab_document
		}
		rec.tab_document = 'stock_map'
	}
}

function gridRowClicked(area, rowIndex, event, selected, doubleClick) {
	if (area === 'document') {
		const rowRec = event?.data || nc.grid.activatedRow(state, 'document')
		if (rowRec && selected) {
			if (documentShown === rowRec.name) {
				return
			}
			if (rec.tab_work_phase !== 'document') {
				prevTabWorkPhase = rec.tab_work_phase
			}
			rec.tab_work_phase = 'document'
			rec.document_data.document_name = rowRec.name
			if (doubleClick) {
				openDocumentPreview('modal', rowRec)
			} else {
				openDocumentPreview('preview', rowRec)
			}
		} else {
			rec.document_data.document_name = ''
			rec.document_preview_modal = false
			rec.tab_work_phase = prevTabWorkPhase
		}
		return
	} else if (area === 'work') {
		rec.current.produced_amount = 0
		rec.current.failed_amount = 0
		setPreviousInputValue(rec, false)
		if (!selected) {
			rowIndex = 0
			updateAfter()
			return
			// } else if (area === 'local_field' || area === 'external_field') {
			// 	// selectMatchField(area, rowIndex)
		}
		if (rowIndex == null) {
			rowIndex = rec.selected_work_row
		}
		rec.selected_work_row = rowIndex
		updateCurrentAndPerson()
		update('work', event?.data, false)
	} else if (area === 'stock_location') {
		// Update stock info when stock location row is selected
		if (selected && rec.current.person_state === 'material') {
			updateMaterialCollectionInfo()
		}
	} else if (area === 'product_material') {
		// Update material info when material rows are selected
		if (selected && rec.current.person_state === 'material') {
			rec.collect.material_info = getMaterialInfo()
			// Update collect units and visibility when material selection changes
			updateCollectUnitsAndVisibility()
			// Update stock grid filter when material selection changes
			if (state.grid?.stock_location?.api) {
				nc.grid.updateFilter(state, 'stock_location')
			}
		}
		if (doubleClick) {
			swapTabDocument()
		}
	}
}

// eslint-disable-next-line no-unused-vars
function gridRowDoubleClicked(area, rowIndex, event) {
	if (area === 'document') {
		const rowRec = event?.data
		openDocumentPreview('modal', rowRec)
	} else if (area === 'work_phase' || area === 'work_phase_actual' || area === 'work_phase_actual_open') {
		rec.show_work_phase_wide = !rec.show_work_phase_wide
	} else {
		gridRowClicked(area, rowIndex, event, 'selected', true)
	}
}

let prevTab = 'product_material'
function setGrid(area, doubleClick) {
	if (area == 'product_material') {
		if (prevTab === 'collect') {
			swapTabDocument()
		} /* else if (doubleClick) {
			swapTabDocument()
		} */
		// Auto-load stock locations and set collect tab when entering material state
		if (rec.tab_collect === 'order_row_purchase') {
			loadIncomingStock() // Load incoming stock orders for materials
			return
		}
		if (rec.tab_material === 'collect') {
			nc.grid.dataChanged(state.grid.product_material, setMaterialIndex(state.data, state.data.product_material))
			swapTabDocument(true)
			materialCollect('load') // Load stock locations when switching to collect tab
		} else if (rec.tab_material === 'material_actual') {
			nc.grid.dataChanged(state.grid.product_material_actual, setMaterialIndex(state.data, state.data.product_material_actual))
		} else {
			//  if (rec.tab_material === 'material_estimated')
			nc.grid.dataChanged(state.grid.product_material, setMaterialIndex(state.data, state.data.product_material))
		}
	} else if (area === 'work_phase') {
		prevTabWorkPhase = rec.tab_work_phase
		if (doubleClick) {
			rec.show_work_phase_wide = !rec.show_work_phase_wide
		}
		if (rec.tab_work_phase === 'work_phase_actual') {
			nc.grid.dataChanged(state.grid.work_phase_actual, setIndex(state.data, state.data.work_phase_actual))
		} else if (rec.tab_work_phase === 'work_phase_actual_open') {
			nc.grid.dataChanged(state.grid.work_phase_actual_open, setIndex(state.data, state.data.work_phase_actual_open))
		} else {
			nc.grid.dataChanged(state.grid.work_phase, setIndex(state.data, state.data.work_phase))
			if (rec.selected_work_row > 0 && state.grid.work.data && state.grid.work_phase.data) {
				const phase = state.grid.work.data[rec.selected_work_row - 1]
				if (phase) {
					setTimeout(() => {
						const index = state.grid.work_phase.data.findIndex(item => item.work_phase_number === phase.work_phase_number) // todo: remove findIndex(), create a real index
						console.debug(`🚀 ~ setTimeout ~ index:`, index)
						nc.grid.activateRows(state, 'work_phase', index + 1)
					}, gridUpdateTimeout)
				}
			}
		}
	} else {
		message.setError(state, `unknown area '${area}' in setGrid()`)
	}
	prevTab = rec.tab_material
}

function setIndex(data, arr) {
	if (!nc.isArray(arr)) {
		message.setError(state, 'set index parameter is not an array')
		return []
	}
	if (arr.length === 0) {
		return arr
	}
	const productIdx = data.dataIdx['product']
	const productionOrderIdx = data.dataIdx['production_order']
	arr.forEach(item => {
		if (item.product_id && productIdx[item.product_id]) {
			item.pr = productIdx[item.product_id]
		} else {
			item.pr = { json_data: {} }
		}
		if (item.production_order_id && productionOrderIdx[item.production_order_id]) {
			item.pro = productionOrderIdx[item.production_order_id]
		} else {
			item.pro = { json_data: {} }
		}
	})
	return arr
}

function setMaterialIndex(data, arr) {
	if (!nc.isArray(arr)) {
		message.setError(state, 'set material index parameter is not an array')
		return []
	}
	if (arr.length === 0) {
		return arr
	}
	const productIdx = data.dataIdx['product']
	arr.forEach(item => {
		if (item.material_id && productIdx[item.material_id]) {
			item.pr = productIdx[item.material_id]
		} else {
			item.pr = { json_data: {} }
		}
	})
	return arr
}

function setPerson(data, showAll) {
	arr.person.length = 1
	data.forEach(per => {
		if (
			(rec.person_state_active[per.person_state] || rec.person_state_active[toString(per.person_state)]) &&
			(showAll || rec.resource === 'all' || per.json_data.employee_group.includes(rec.resource))
		) {
			if (per.person_id !== 'X') {
				// test for x or X, x is a special value for no person
				arr.person.push({ value: per.person_id, show: per.full_name + ' (' + per.person_id + ')' })
			}
		}
	})
	if (!showAll && arr.person.length < 2) {
		setPerson(data, true)
	}
}

function updateAfter(ret) {
	// Reset pressed state for all buttons after server response
	if (rec.buttonPressed) {
		Object.keys(rec.buttonPressed).forEach(k => {
			rec.buttonPressed[k] = false
		})
	}
	loadCount++
	// Handle incremental response
	const wasIncremental = handleIncrementalResponse(state, ret)
	if (!ret) {
		updateCurrentAndPerson()
		return
	}
	setPreviousInputValue(rec, 'restore')
	// For incremental updates, we only need to update specific grids that changed
	if (wasIncremental) {
		// Update grids based on the changes applied
		if (ret.changes) {
			for (const tableName in ret.changes) {
				switch (tableName) {
					case 'product_work-schedule':
						if (state.data.work) {
							nc.grid.dataChanged(state.grid.work, setIndex(state.data, state.data.work))
						}
						break
					case 'product_work-actual':
						if (state.data.work_phase_actual) {
							nc.grid.dataChanged(state.grid.work_phase_actual, state.data.work_phase_actual)
						}
						if (state.data.work_phase_actual_open) {
							nc.grid.dataChanged(state.grid.work_phase_actual_open, state.data.work_phase_actual_open)
						}
						break
					case 'product_material-estimated':
						if (state.data.product_material) {
							nc.grid.dataChanged(state.grid.product_material, setMaterialIndex(state.data, state.data.product_material))
						}
						break
					case 'product_material-actual':
						if (state.data.product_material_actual) {
							nc.grid.dataChanged(state.grid.product_material_actual, setMaterialIndex(state.data, state.data.product_material_actual))
						}
						break
					case 'person-employee':
						if (state.data.person) {
							setPerson(state.data.person)
						}
						break
				}
			}
		}
		// Update current person data - use only actual work phase data, not schedule data
		updateCurrentAndPerson()
		if (rec.tab_collect === 'collect' || (rec.current.person_state === 'material' && rec.previous_person_state !== 'material')) {
			// Auto-select collect tab when material button is pressed
			if (rec.tab_material !== 'collect') {
				rec.tab_material = 'collect'
			}
			if (rec.tab_document !== 'stock_map') {
				rec.tab_document = 'stock_map'
			}
			// Load stock locations for materials
			// setTimeout(() => {
			materialCollect('load')
			// }, 100) // Small delay to ensure grids are updated
			// Update material collection info
			setTimeout(() => {
				updateMaterialCollectionInfo()
			}, 150) // Slightly longer delay to ensure stock_location grid is loaded
		}
		return
	}
	// Full data update (original logic)
	if (!ret?.data) {
		if (state.grid) {
			nc.grid.dataChanged(state.grid.product_material_actual, [])
			nc.grid.dataChanged(state.grid.product_material, [])
			nc.grid.dataChanged(state.grid.work_phase_actual, [])
			nc.grid.dataChanged(state.grid.work_phase_actual_open, [])
			nc.grid.dataChanged(state.grid.work_phase, [])
		}
	} else {
		if (ret.data.person) {
			setPerson(ret.data.person)
		}
		if (ret.data.work) {
			// setupStartedPerson(ret.data.work)
			nc.grid.dataChanged(state.grid.work, setIndex(ret.data, ret.data.work))
			if (loadCount === 1 && rec.selected_work_row == 1) {
				nc.grid.activateRows(state, 'work', rec.selected_work_row) // first load
			}
		}
		if (ret.data.work_phase || ret.data.work_phase_actual) {
			setGrid('work_phase')
		}
		if (ret.data.product_material || ret.data.product_material_actual) {
			setGrid('product_material')
		}
	}
	updateCurrentAndPerson()
}

function shouldLoadStockLocations() {
	return rec.tab_material === 'collect' || (rec.current.person_state === 'material' && rec.previous_person_state !== 'material')
}

// eslint-disable-next-line no-unused-vars
function passwordCallback(reason) {
	return reason
}

/* Why is this code in here and not in css?
I could have put the stylings that doesn't require dynamic values to css, but because of their size I don't think it would have made the code any easier to manage. The reason why this cannot be put in css file is because the height of the element needs to be declared dynamically. And why didn't I use css dynamic values, because they have no effect. Is there a better way? Probably, but at the moment I don't have better approach in mind. */

// eslint-disable-next-line no-unused-vars
function slideDownBeforeEnter(el) {
	el.style.height = '0'
	el.style.overflow = 'hidden'
}
// eslint-disable-next-line no-unused-vars
function slideDownEnter(el) {
	el.style.height = el.scrollHeight + 'px'
	el.style.overflow = 'inherit'
}
// eslint-disable-next-line no-unused-vars
function slideDownAfterEnter(el) {
	el.style.height = 'auto'
	el.style.overflow = 'inherit'
}
// eslint-disable-next-line no-unused-vars
function slideDownBeforeLeave(el) {
	el.style.height = el.scrollHeight + 'px'
	el.style.overflow = 'hidden'
}
function forceReflow(el) {
	return el.offsetHeight // https://stackoverflow.com/questions/21664940/force-browser-to-trigger-reflow-while-changing-css
}
// eslint-disable-next-line no-unused-vars
function slideDownLeave(el) {
	forceReflow(el) // Force reflow
	el.style.height = '0'
}
// eslint-disable-next-line no-unused-vars
function slideDownAfterLeave(el) {
	el.style.height = ''
}

function updateCollectUnitsAndVisibility() {
	// Update collect unit visibility and labels based on selected material
	if (rec.current.person_state !== 'material') {
		return
	}
	const materialRow = nc.grid.activatedRow(state, 'product_material')
	if (!materialRow || !materialRow.pr) {
		// Reset collect state if no material selected
		rec.collect.design_unit_1 = ''
		rec.collect.design_unit_2 = ''
		rec.collect.stock_unit_2 = ''
		rec.collect.unit = ''
		rec.collect.unit2 = ''
		rec.collect.show_design_unit_1 = false
		rec.collect.show_design_unit_2 = false
		rec.collect.show_stock_unit_2 = false
		rec.collect.total_amount = 0
		rec.collect.total_amount_2 = 0
		rec.collect.design_amount_1 = 0
		rec.collect.design_amount_2 = 0
		rec.collect.collect_amount_1 = 0
		rec.collect.collect_amount_2 = 0
		rec.collect.collect_amount_stock = 0
		return
	}
	const pr = materialRow.pr
	// Set unit labels based on product data
	rec.collect.unit = pr.design_unit || ''
	rec.collect.unit2 = pr.json_data?.stock_unit_2 || pr.stock_unit || ''
	rec.collect.design_unit_1 = pr.design_unit || ''
	rec.collect.design_unit_2 = pr.json_data?.design_unit_2 || ''
	rec.collect.stock_unit_2 = pr.json_data?.stock_unit_2 || pr.stock_unit || ''
	// Always show design unit 1 (stock unit 1 in nc-stock terms)
	rec.collect.show_design_unit_1 = true
	// Show design unit 2 if it exists and is different from design unit 1
	if (pr.json_data?.design_unit_2 && pr.json_data.design_unit_2 !== pr.design_unit) {
		rec.collect.show_design_unit_2 = true
	} else {
		rec.collect.show_design_unit_2 = false
		rec.collect.design_amount_2 = 0
	}
	// Store the selected material for conversion calculations
	rec.collect.selected_material = materialRow
}

// eslint-disable-next-line no-unused-vars
function updateCollectAmount(changedField) {
	if (rec.current.person_state !== 'material') {
		return
	}
	const materialRow = rec.collect.selected_material
	if (!materialRow || !materialRow.pr) {
		return
	}
	const pr = materialRow.pr
	// Handle the new collect amount fields
	if (changedField === 'collect_amount_1') {
		// Convert collect amount 1 to stock unit 1 and update all amounts
		const amountInStock1 = amountDesign1ToStock1(pr, rec.collect.collect_amount_1)
		rec.collect.total_amount = amountInStock1
		rec.collect.total_amount_2 = amountStock1ToStock2(pr, amountInStock1)
		rec.collect.collect_amount_2 = amountStock1ToStock2(pr, amountInStock1)
		rec.collect.collect_design_amount_1 = rec.collect.collect_amount_1
		rec.collect.collect_design_amount_2 = amountStock1ToDesign2(pr, amountInStock1)
	} else if (changedField === 'collect_amount_2') {
		// Convert collect amount 2 to stock unit 1 and update all amounts
		const amountInStock1 = amountStock2ToStock1(pr, rec.collect.collect_amount_2)
		rec.collect.total_amount = amountInStock1
		rec.collect.total_amount_2 = rec.collect.collect_amount_2
		rec.collect.collect_amount_1 = amountStock1ToStock2(pr, amountInStock1)
		rec.collect.collect_design_amount_1 = amountStock1ToDesign1(pr, amountInStock1)
		rec.collect.collect_design_amount_2 = amountStock1ToDesign2(pr, amountInStock1)
	} else if (changedField === 'collect_design_amount_1') {
		// Convert collect design amount 1 to stock unit 1 and update all amounts
		const amountInStock1 = amountDesign1ToStock1(pr, rec.collect.collect_design_amount_1)
		rec.collect.total_amount = amountInStock1
		rec.collect.total_amount_2 = amountStock1ToStock2(pr, amountInStock1)
		rec.collect.collect_amount_1 = amountStock1ToDesign1(pr, amountInStock1)
		rec.collect.collect_amount_2 = amountStock1ToStock2(pr, amountInStock1)
		rec.collect.collect_design_amount_2 = amountStock1ToDesign2(pr, amountInStock1)
	} else if (changedField === 'collect_design_amount_2') {
		// Convert collect design amount 2 to stock unit 1 and update all amounts
		const amountInStock1 = amountDesign2ToStock1(pr, rec.collect.collect_design_amount_2)
		rec.collect.total_amount = amountInStock1
		rec.collect.total_amount_2 = amountStock1ToStock2(pr, amountInStock1)
		rec.collect.collect_amount_1 = amountStock1ToDesign1(pr, amountInStock1)
		rec.collect.collect_amount_2 = amountStock1ToStock2(pr, amountInStock1)
		rec.collect.collect_design_amount_1 = amountStock1ToDesign2(pr, amountInStock1)
	}
}

function updateMaterialCollectionInfo() {
	// Update material collection amounts when in material state
	if (rec.current.person_state !== 'material') {
		return
	}
	// Calculate total material need from product_material grid
	const productMaterialData = state.data?.product_material || []
	let totalNeed = 0
	let totalCollected = 0
	productMaterialData.forEach(item => {
		if (item.amount && rec.current.started_amount) {
			// Need = started_amount * prma.amount (need per unit)
			totalNeed += rec.current.started_amount * item.amount
		}
		if (item.prma?.calc?.collected) {
			totalCollected += item.prma.calc.collected || 0
		}
	})
	const remaining = Math.max(0, totalNeed - totalCollected)
	// Update reactive variables
	rec.material_need = totalNeed
	rec.material_collected = totalCollected
	rec.material_remaining = remaining
	// Update stock info based on selected stock location row
	const stockRow = nc.grid.activatedRow(state, 'stock_location')
	if (stockRow) {
		rec.stock_info = `Product: ${stockRow.product_id || 'N/A'}, Location: ${stockRow.location_id || 'N/A'}, Amount: ${stockRow.amount || 0}, Batch: ${
			stockRow.material_batch || 'N/A'
		}`
	} else {
		rec.stock_info = 'No stock parcel selected'
	}
}

function updateProductMaterialCanStart() {
	// Calculate material_can_start for product_material grid based on available stock
	if (!state.data?.product_material || !state.data?.stock_location) {
		return
	}
	const productMaterialData = state.data.product_material
	const stockLocationData = state.data.stock_location
	// Create product stock lookup for efficient searching
	const productStockLookup = {}
	stockLocationData.forEach(stock => {
		if (!productStockLookup[stock.product_id]) {
			productStockLookup[stock.product_id] = 0
		}
		productStockLookup[stock.product_id_id] += stock.amount
	})
	// Update each product_material row with calculated material_can_start
	productMaterialData.forEach(item => {
		if (item.material_id) {
			const availableStock = productStockLookup[item.material_id] || 0
			const totalNeeded = item.amount * (rec.current.started_amount || 0)
			// Calculate material_can_start: true if enough stock available
			const canStart = availableStock >= totalNeeded
			if (item.prma) {
				item.prma.calc = item.prma.calc || {}
				item.prma.calc.material_can_start = canStart
			}
		}
	})
	// Update the product_material grid with the calculated values
	nc.grid.dataChanged(state.grid.product_material, setMaterialIndex(state.data, state.data.product_material))
}

function getMaterialInfo() {
	// Get selected material row from product_material grid
	const materialRow = nc.grid.activatedRow(state, 'product_material')
	if (!materialRow) {
		return 'No material selected'
	}
	const productName = materialRow.pr?.name || materialRow.material_id || 'Unknown product'
	const needPerUnit = materialRow.amount || 0
	const totalRequired = rec.current.started_amount ? rec.current.started_amount * needPerUnit : 0
	const collectedAmount = materialRow.prma?.calc?.collected || 0
	const missingAmount = Math.max(0, totalRequired - collectedAmount)
	// Format numbers with thousand separators
	const formatNumber = num => {
		return new Intl.NumberFormat('fi-FI').format(num)
	}
	// Color code the missing amount
	const missingColor = missingAmount > 0 ? '#dc3545' : '#28a745' // Red for missing, green for complete
	const missingText =
		missingAmount > 0
			? `Missing: <span style="color: ${missingColor}; font-weight: bold;">${formatNumber(missingAmount)}</span>`
			: `<span style="color: ${missingColor}; font-weight: bold;">Complete</span>`
	return `<strong>${productName}</strong> - Need per unit: ${formatNumber(needPerUnit)}, Total required: ${formatNumber(totalRequired)}, Collected: ${formatNumber(
		collectedAmount
	)}, ${missingText}`
}

function materialCollect(action) {
	let stockRecord = nc.grid.activatedRow(state, 'stock_location')
	if (!stockRecord) {
		// If no stock record is selected, try to select the first one
		if (state.grid?.stock_location?.data && state.grid.stock_location.data.length > 0) {
			stockRecord = state.grid.stock_location.data[0]
			// Activate the first row in the stock_location grid
			nc.grid.activateRows(state, 'stock_location', 1)
		} else if (action !== 'load') {
			// message.setWarning(state, 'No stock location selected')
			return
		}
	}
	if (action === 'load') {
		// Load stock locations for materials needed in current work
		const productMaterialData = state.data?.product_material || []
		const productIdArr = []
		// Extract unique product IDs from product_material grid
		productMaterialData.forEach(item => {
			if (item.material_id && !productIdArr.includes(item.material_id)) {
				productIdArr.push(item.material_id)
			}
		})
		if (productIdArr.length === 0) {
			message.setWarning(state, 'No materials found to load')
			return
		}
		// Update the stock_location grid query with product IDs
		const param = {
			search_product: productIdArr
		}
		nc.callServer(
			state,
			'query',
			{
				name: 'form/nc/nc-work-center/grid.json',
				parameter: param,
				return: { grid: [{ name: 'stock_location', tab: 'collect' }] }
			},
			ret => {
				if (ret.grid?.stock_location?.data) {
					let stockLocationData = ret.grid.stock_location.data
					// Add generated 'stock' row when rec.tab_material === 'collect'
					if (rec.tab_material === 'collect') {
						addGeneratedStockRow(stockLocationData)
					}
					nc.grid.dataChanged(state.grid.stock_location, stockLocationData)
					// message.setInfo(state, `Loaded ${stockLocationData.length} stock records`)
					updateProductMaterialCanStart()
					updateMaterialCollectionInfo() // Update material collection info after loading stock locations
				} else {
					message.setWarning(state, 'No stock records found for materials')
				}
			}
		)
	} else if (action === 'collect' && stockRecord) {
		// Collect material from stock location
		if (!stockRecord.stock_id) {
			message.setError(state, 'Invalid stock record - missing stock_id')
			return
		}
		const workRow = nc.grid.activatedRow(state, 'work')
		if (!workRow) {
			message.setWarning(state, 'Please select a work row first')
			return
		}
		// Prepare collection data
		const collectData = {
			stock_id: stockRecord.stock_id,
			product_id: stockRecord.product_id,
			location_id: stockRecord.location_id,
			material_batch: stockRecord.material_batch,
			amount: stockRecord.amount,
			event_time: stockRecord.event_time,
			collect_person: rec.person,
			collect_time: new Date().toISOString(),
			work_id: workRow.work_id,
			production_order_id: workRow.production_order_id
		}
		// Save collection to stock record's json_data
		const param = {
			save: [
				{
					table: 'stock',
					data: [
						{
							record_id: stockRecord.stock_id,
							json_data_path: ['collect'],
							json_data: [collectData]
						}
					],
					save_preference: 'form/nc/nc-work-center/save/stock.json'
				}
			]
		}
		nc.callServer(state, 'save', param, ret => {
			if (ret.save?.[0]?.data?.length > 0) {
				message.setInfo(state, `Collected ${stockRecord.amount} units of product ${stockRecord.product_id}`)
				// Refresh stock_location grid to update available amounts
				materialCollect('load')
				updateProductMaterialCanStart()
				// Update material collection info after collecting
				setTimeout(() => {
					updateMaterialCollectionInfo()
				}, 100)
			} else {
				message.setError(state, 'Failed to collect material')
			}
		})
	} else if (action === 'remove' && stockRecord) {
		// Remove material collection from stock record
		if (!stockRecord.stock_id) {
			message.setError(state, 'Invalid stock record - missing stock_id')
			return
		}
		// Find and remove the collection entry
		const param = {
			save: [
				{
					table: 'stock',
					data: [
						{
							record_id: stockRecord.stock_id,
							json_data_path: ['collect'],
							json_data_remove: true
						}
					],
					save_preference: 'form/nc/nc-work-center/save/stock.json'
				}
			]
		}
		nc.callServer(state, 'save', param, ret => {
			if (ret.save?.[0]?.data?.length > 0) {
				message.setInfo(state, `Removed collection for stock record ${stockRecord.stock_id}`)
				// Refresh stock_location grid to update available amounts
				materialCollect('load')
				updateProductMaterialCanStart()
				// Update material collection info after removing
				setTimeout(() => {
					updateMaterialCollectionInfo()
				}, 100)
			} else {
				message.setError(state, 'Failed to remove collection')
			}
		})
	} else if (action === 'confirm') {
		// Confirm work start - creates new stock records for collected and left amounts
		const workRow = nc.grid.activatedRow(state, 'work')
		if (!workRow) {
			message.setWarning(state, 'Please select a work row first')
			return
		}
		if (rec.current.person_state !== 'material') {
			message.setWarning(state, 'Material collection is not active')
			return
		}
		// Get all stock records with collections
		const stockLocationData = state.data?.stock_location || []
		const collectedStocks = []
		const updatedStocks = []
		stockLocationData.forEach(stock => {
			if (stock.json_data?.collect && stock.json_data.collect.length > 0) {
				const collectedAmount = stock.json_data.collect.reduce((sum, collect) => sum + (collect.amount || 0), 0)
				const remainingAmount = stock.amount - collectedAmount
				// Update original stock record with end_time
				updatedStocks.push({
					record_id: stock.stock_id,
					end_time: new Date().toISOString()
				})
				// Create new stock record for collected amounts if any
				if (collectedAmount > 0) {
					collectedStocks.push({
						product_id: stock.product_id,
						location_id: rec.machine, // Move to selected machine
						amount: collectedAmount,
						event_time: new Date().toISOString(),
						end_time: 0,
						event_type: 'TR', // Transfer
						material_batch: stock.material_batch,
						info: `Collected for work ${workRow.work_id} by ${rec.person}`,
						json_data: {
							collect: stock.json_data.collect,
							original_stock_id: stock.stock_id
						}
					})
				}
				// Create new stock record for remaining amounts if any
				if (remainingAmount > 0) {
					updatedStocks.push({
						product_id: stock.product_id,
						location_id: stock.location_id, // Stay in same location
						amount: remainingAmount,
						event_time: new Date().toISOString(),
						end_time: 0,
						event_type: 'LI', // Load In
						material_batch: stock.material_batch,
						info: `Remaining after collection for work ${workRow.work_id}`,
						json_data: {
							original_stock_id: stock.stock_id
						}
					})
				}
			}
		})
		if (collectedStocks.length === 0 && updatedStocks.length === 0) {
			message.setWarning(state, 'No collected materials found to process')
			return
		}
		// Save all stock changes
		const param = {
			save: [
				{
					table: 'stock',
					data: updatedStocks,
					save_preference: 'form/nc/nc-work-center/save/stock.json'
				},
				{
					table: 'stock',
					data: collectedStocks,
					save_preference: 'form/nc/nc-work-center/save/stock.json'
				}
			]
		}
		nc.callServer(state, 'save', param, ret => {
			const totalCreated = (ret.save?.[0]?.data?.length || 0) + (ret.save?.[1]?.data?.length || 0)
			message.setInfo(state, `Created ${totalCreated} new stock records for collected materials`)
			// Clear stock_location grid after processing
			nc.grid.dataChanged(state.grid.stock_location, [])
			// Continue with normal work start process
			buttonClick('person', 'working')
		})
	} else {
		message.setError(state, `Invalid materialCollect action: ${action}`)
	}
}

function loadIncomingStock() {
	// Load incoming stock orders (purchase order rows) for materials needed in current work
	const productMaterialData = state.data?.product_material || []
	const productIdArr = []
	// Extract unique product IDs from product_material grid
	productMaterialData.forEach(item => {
		if (item.material_id && !productIdArr.includes(item.material_id)) {
			productIdArr.push(item.material_id)
		}
	})
	if (productIdArr.length === 0) {
		// message.setWarning(state, 'No materials found to load incoming stock')
		return
	}
	// Update the stock_row grid query with product IDs
	const param = {
		search_product: productIdArr
	}
	nc.callServer(
		state,
		'query',
		{
			name: 'form/nc/nc-work-center/grid.json',
			parameter: param,
			return: { grid: [{ name: 'order_row_purchase', tab: 'incoming' }] }
		}
		/* ret => {
			if (ret.grid?.order_row_purchase?.data) {
				nc.grid.dataChanged(state.grid.order_row_purchase, ret.grid.order_row_purchase.data)
				// message.setInfo(state, `Loaded ${ret.grid.order_row_purchase.data.length} incoming stock orders`)
			}
		} */
	)
}

function addGeneratedStockRow(stockLocationData) {
	// Generate product list only from product_material data
	const productStock = {}
	const productMaterialData = state.data?.product_material || []
	const productIdx = state.data?.dataIdx?.product || {}
	for (let i = 0; i < productMaterialData.length; i++) {
		const material = productMaterialData[i]
		if (material.material_id) {
			const productId = material.material_id
			if (!productStock[productId]) {
				productStock[productId] = productIdx[productId]?.json_data?.stock_balance || 0
			}
		}
	}
	// Check if 'stock' row already exists for each product and add if missing
	for (const productId in productStock) {
		let stockRowExists = false
		for (let i = 0; i < stockLocationData.length; i++) {
			const stock = stockLocationData[i]
			if (stock.product_id === productId && stock.location_id === 'stock') {
				stockRowExists = true
				break
			}
		}
		if (!stockRowExists) {
			// Create generated 'stock' row
			const stockRow = {
				idx: 1, // Will be recalculated when grid is rendered
				stock_id: '', // Empty for generated row
				product_id: productId,
				location_id: 'stock',
				material_batch: '',
				event_time: new Date().toISOString(),
				event_type: rec.constant.stock_event_type.balance, // Generated type
				amount: productStock[productId],
				unit_price: 0,
				currency_id: '',
				info: 'Stock balance'
			}
			stockLocationData.push(stockRow)
		}
	}
	stockLocationData.sort((a, b) => {
		// Sort by product_id, then by location_id ('stock' first), then by event_time
		if (a.product_id < b.product_id) return -1
		if (a.product_id > b.product_id) return 1
		if (a.location_id === 'stock' && b.location_id !== 'stock') return -1
		if (a.location_id !== 'stock' && b.location_id === 'stock') return 1
		if (a.event_time < b.event_time) return -1
		if (a.event_time > b.event_time) return 1
		return 0
	})
	// Recalculate idx values to ensure proper ordering
	for (let i = 0; i < stockLocationData.length; i++) {
		stockLocationData[i].idx = i + 1
	}
}

function filterStockGrid(node) {
	// Filter stock_location grid to show only materials selected in product_material grid
	const stockLocationGrid = state.grid?.stock_location
	if (!stockLocationGrid?.data || !stockLocationGrid.api) {
		return true // No filtering if stock_location grid not available
	}
	const selectedMaterialArray = selectedMaterialIdArray()
	if (selectedMaterialArray.length === 0) {
		return true // Show all if no materials selected
	}
	const stockProductId = node.data?.st?.product_id
	const stockLocationId = node.data?.st?.location_id
	// Always show generated 'stock' rows if their product is in selected materials
	if (stockLocationId === 'stock' && selectedMaterialArray.includes(stockProductId)) {
		return true
	}
	return selectedMaterialArray.includes(stockProductId)
}

function selectedMaterialIdArray() {
	// Get array of unique material IDs from selected product_material grid rows
	let selectedRowArray = nc.grid.activatedRecordArr(state, 'product_material')
	if (selectedRowArray.length === 0) {
		selectedRowArray = state.data.product_material
	}
	const materialIdArray = selectedRowArray.map(item => item.material_id)
	return materialIdArray
}
