// form/nc/nc-work-center/nc-work-center.js
import { updateCurrentAndPersonFunction } from './nc-work-center-util.js'
import { handleIncrementalResponse, initializeIncrementalUpdate, resetIncrementalUpdate } from './change-data.js'
// eslint-disable-next-line no-unused-vars
import { setClickButton, buttonAction } from './nc-work-center-button.js'

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

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
	}
})

function fixAmount(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 = ''
			}
		}
	}
	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 = ''
			}
		}
	}
	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 = ''
			}
		}
	}
	if (rec.current.started_amount === 0 || rec.current.started_amount === '' || rec.current.started_amount < rec.current.produced_amount + rec.current.failed_amount) {
		rec.current.can_start = false
		rec.current.can_run = false
	} else {
		rec.current.can_start = true
		rec.current.can_run = true
	}
}

// eslint-disable-next-line no-unused-vars
function amountAndInfo() {
	rec.focus_field = ''
	fixAmount(true)
	if (rec.person === '' || rec.current.started_amount === '') {
		updateCurrentAndPerson()
		return
	}
	if (
		rec.current.started_amount !== rec.current.prev_started_amount ||
		rec.current.produced_amount !== rec.current.prev_produced_amount ||
		rec.current.failed_amount !== rec.current.prev_failed_amount ||
		(rec.current.info !== rec.current.prev_info && rec.focus_field === '')
	) {
		// debugger
		// todo: enable callEvent('update amount', rec.constant.phase_state.edit)
	}
}

// eslint-disable-next-line no-unused-vars
function clickButton(buttonId, buttonState) {
	/* if (buttonId === 'open') {
		rec.info_open = !rec.info_open
		return
	}
	if (buttonId !== 'unproductive') {
		rec.info_open = true
	} */
	buttonAction(buttonId, buttonState)
	callEvent(buttonId, buttonState)
}
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() {
	// Reset incremental update to force full reload
	resetIncrementalUpdate(state)
	update(null, null, true)
}

// --- all clicks and events go to server ---
function callEvent(buttonId, buttonState) {
	if (!rec.person) {
		message.setWarning(state, 'Please select an person first')
		return
	}
	const rowRec = nc.grid.activatedRow(state, 'work')
	if (!rowRec && buttonId !== '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, { button_action: buttonId, button_state: buttonState, 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
	}
	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 || ''
	}
}

function update(area, rowRec, reload, eventParam) {
	// called also from layout
	if (area === 'work' && rowRec == null) {
		rec.separate_machine_run = rec.keep_running_load_group[rec.resource]
	}
	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)
		}
	)
}

// eslint-disable-next-line no-unused-vars
function emptyPersonState() {
	rec.person = ''
	updateCurrentAndPerson()
	if (rec.current.unproductive !== '') {
		rec.current.unproductive = ''
	}
}

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 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 !== '' // todo: rec.create_schedule?
}
// eslint-disable-next-line no-unused-vars
function doesExternalFilterPass(node) {
	if (rec.show_done === false && node.data.work_phase_state === rec.constant.phase_state.done) {
		return false
	}
	if (rec.show_active === true && node.data.calc.can_start_amount <= 0) {
		return false
	}
	if (rec.virtual_production_order === false && node.data.virtual) {
		return false // todo: rec.create_schedule?
	}
	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
	} */
	return false
}

// eslint-disable-next-line no-unused-vars
function storageMapToggleSelected(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 storageMapDoubleClick(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() {
	if (rec.tab_document === 'storage_map') {
		rec.tab_document = prevTabDocument
	} else {
		prevTabDocument = rec.tab_document
		rec.tab_document = 'storage_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') {
		/* if (!rec.info_open) {
			if (doubleClick) {
				rec.info_open = true // !rec.info_open
			} else if (rec.person) {
				const rowRec = event?.data || nc.grid.activatedRow(state, 'work')
				if (rowRec?.calc?.started_person && nc.recordArrayRecord(rowRec.calc.started_person, 'person_id', rec.person)) {
					rec.info_open = true
				}
			}
		} */
		if (!selected) {
			rowIndex = 0
			rec.selected_work_row = rowIndex
			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 (doubleClick && area === 'product_material') {
		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()
		} */
		if (rec.tab_material === 'collect') {
			swapTabDocument()
		} 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) {
	loadCount++
	// Handle incremental response
	const wasIncremental = handleIncrementalResponse(state, ret)
	if (!ret) {
		return
	}
	// 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
		const rowRec = state.grid.work.data[rec.selected_work_row - 1]
		updateCurrentAndPerson(rowRec)
		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')
		}
	}
	const rowRec = state.grid.work.data[rec.selected_work_row - 1] // nc.grid.activatedRow(state, 'work')
	updateCurrentAndPerson(rowRec)
}

// 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 = ''
}
