// form/nc/nc-input-form/nc-input-form.js

const mainState = nc.mainState()
const changeRecord = ref({})
const saveEnabled = ref(false)
let loadedRec = {}
let listData = []
let listRec = {}
let listDataRec = null
let tableCodeRecord = null
let listRow = ref(0) // not js 0-based but 1-based
const inputComponent = ref('')
const isObject = nc.isObject
let sync

const previousEnabled = computed(() => {
	return saveEnabled.value === false && listData.length > 1 && listRow.value > 1
})

const nextEnabled = computed(() => {
	return saveEnabled.value === false && listData.length > 1 && listRow.value < listData.length
})

onLoad()

/* watchEffect(() => {
	debugger
	if (mainState.rec.table !== '' && rec.table !== mainState.rec.table) {
		rec.table = mainState.rec.table
		onLoad()
	}
}) */

function setSaveEnabled(value) {
	saveEnabled.value = value
	mainState.input.save_enabled = value
}

function clearChangeRecord() {
	loadedRec = nc.clone(rec)
	tableCodeRecord = { [rec.table_prefix]: loadedRec }
	changeRecord.value = {}
	setSaveEnabled(false)
}

function setRecByRow() {
	if (listData && listRow.value > 0 && listRow.value <= listData.length) {
		listDataRec = listData[listRow.value - 1]
	} else {
		listDataRec = null
	}
	rec[rec.table_prefix] = listDataRec
	/* for (var key in listDataRec) {
				if (isObject(listDataRec[key])) {
					// current state.rec table recs must change to new default from list, it will be updated by input -rest call
					if (rec[key] == null) {
						rec[key] = {} // changeRecord
					}
					nc.copyDeepRec(listDataRec[key], rec[key], rec.table_prefix) // rec[key] = nc.clone(listDataRec[key])
				}
			} */
	clearChangeRecord()
}

// eslint-disable-next-line no-unused-vars
function linkDialog(status, value) {
	// debugger
	rec.link_grid.show = false
	if (status === 'ok' && value && rec.link_grid.link_field && rec.link_grid.data_field) {
		const val = nc.recData(value, rec.link_grid.data_field)
		if (val != null) {
			nc.setRec(state, rec.table_prefix + '.' + rec.link_grid.link_field, val)
		}
	}
	/*
	"v-if": "rec.link_grid && rec.link_grid.show",
  ":ok-text": "hdr.dialog.ok",
  ":cancel-text": "hdr.dialog.cancel",
  ":content": "rec.link_grid.content",
  ":header": "rec.link_grid.header",
  ":show": "rec.link_grid.show",
  "v-on:modal-cancel": "dialog",
  "v-on:modal-ok": "dialog"
	*/
}

function onLoad() {
	mainState.rec.in_form = 'input'
	mainState.inputValueChanged = inputValueChanged // inputValueChanged is function to be called from state rec change using nc.setRec()
	mainState.input = { save_enabled: saveEnabled.value }
	if (mainState.list) {
		listData = mainState.list.grid && mainState.list.grid.list && mainState.list.grid.list.data
		listRec = mainState.list.rec
		if (listData && listRec.row_index == null) {
			listRec.row_index = 1
		}
		listRow.value = listRec.row_index
		rec.table_prefix = listRec.table_prefix
		rec.table = listRec.table
		if (!rec.table_prefix) {
			message.setError(`rec.table_prefix '${rec.table_prefix}' is missing`)
		} else if (!rec.table) {
			message.setError(`rec.table '${rec.table}' is missing`)
		}
		setRecByRow()
		if (rec.table !== listRec.table) {
			rec.input_component = null // do not draw previous table input
		}
		if (mainState.list.arr && mainState.list.arr.table) {
			// set table dropdown
			mainState.arr.table = mainState.list.arr.table
		} else {
			mainState.arr.table = []
		}
	}
	if (rec.input_component_path) {
		// first load of nc-input-form -component and it's table page -component
		// when loading input form dynamically first time in nc-dynamic-component.js has set these call params, find 'form/nc/nc-input-form'
		serverCallback(state)
	}
	if (listData && listRow.value > 0) {
		getServerRec()
		// } else {
		// message.setWarning(state, `mainState.list data -array is missing`)
	}
}

// first load must be done before activated for rec.input_component to be set correctly and subcomponent to be drawn
//	- otherwise component(:is='rec.input_component') does not detect change and does not draw subcomponent

let loadDone = true
onActivated(() => {
	if (loadDone) {
		mainState.rec.in_form = 'input'
		loadDone = false // for next activated
	} else {
		onLoad()
	}
})

onMounted(() => {
	onLoad()
})

onDeactivated(() => {
	mainState.rec.in_form = ''
	onUnload()
})

function onUnload() {
	mainState.rec.in_form = ''
	delete mainState.inputValueChanged
	const listTableRec = listDataRec // && listDataRec[rec.table_prefix]
	const tableRec = rec[rec.table_prefix]
	nc.copyDeepRec(tableRec, listTableRec, rec.table_prefix) // copy back to list only values originally in list
	if (listTableRec) {
		listRec.record_id = listTableRec.record_id
	}
	setSync(false)
}

async function serverCallback(ret, callOption) {
	// debugger
	if (callOption && callOption.direct_callback) {
		const listTableRec = listDataRec // && listDataRec[rec.table_prefix]
		const tableRec = ret.rec && ret.rec[rec.table_prefix]
		if (listTableRec && tableRec && listTableRec.record_id !== tableRec.record_id) {
			// console.debug('*** skip input callback ***')
			return false // fast click with arrows over this record, cancel running this callback
		}
		return true // call this again when normal state values has been set
	}
	if (rec && ret.rec == null && ret.code_rec && ret.code_rec[rec.table_prefix]) {
		const keysCopied = []
		const keysToCopy = null
		const retRec = rec[rec.table_prefix]
		nc.copyDeepRec(ret.code_rec[rec.table_prefix], retRec, rec.table_prefix, keysToCopy, keysCopied)
		ret.rec = { [rec.table_prefix]: retRec }
	}
	if (isObject(ret) && ret.rec && rec) {
		if (ret.rec.input_component_path) {
			inputComponent.value = peg.parseAfterLast(ret.rec.input_component_path, '/')
			// debugger
			const url = window.location.protocol + `//` + window.location.host + (ret.base_path || ret.rec?.base_path) + 'plugin/' + ret.rec.input_component_path + '.js'
			delete rec.input_component_path
			rec.input_component = null // will trigger view change later
			await import(/* @vite-ignore */ url) // we must import components in the right order, that's why we need await here
				.then(mod => {
					const component = mod.default
					if (component == null) {
						message.setError(state, `importing component from url '${url}' failed, returned module is '${mod}'`)
						return
					} else if (component.error) {
						message.setError(state, `importing component from url '${url}', component error '${component.error}'`)
						return
					}
					nc.updateDynamicComponent(component, false)
					if (!mainState.arr.input_component_arr.includes(component.name)) {
						mainState.arr.input_component_arr.push(component.name)
					}
					nc.setArr(mainState, 'input_component_arr', mainState.arr.input_component_arr) // for debug
					setTimeout(() => {
						rec.input_component = inputComponent.value // refresh the view to use new component
					}, 0)
				})
				.catch(err => {
					console.error(`Error in loading dynamic component: ${url}, error: ${err.message}`)
				})
		}
		const listTableRec = listDataRec // && listDataRec[rec.table_prefix]
		const tableRec = ret.rec[rec.table_prefix]
		if (tableRec.link_grid) {
			if (state.grid == null) {
				state.grid = {}
			}
			state.grid.link_grid = tableRec.link_grid.grid
			rec.link_grid = tableRec.link_grid
			delete rec.link_grid.grid
			delete tableRec.link_grid
			rec.link_grid.show = true // shows the modal
		}
		tableCodeRecord = { [rec.table_prefix]: nc.clone(tableRec) }
		if (listDataRec && listDataRec.changeRecord && !nc.isEmptyObject(listDataRec.changeRecord)) {
			// copy not-saved modified values from list to input, but what if list rec is outdated?
			// todo: copy only modified values by list changeRec
			const keysCopied = []
			nc.copyDeepRec(listTableRec, tableRec, rec.table_prefix, listDataRec.changeRecord, keysCopied)
			clearChangeRecord()
			if (keysCopied.length > 0) {
				// do we need this? - then you can't move to next record
				// setSaveEnabled(true)
			}
		}
	}
	setSync()
}

function getServerRec() {
	setRecByRow()
	const tblRec = rec[rec.table_prefix]
	if (!tblRec) {
		message.setWarning(state, `record '${rec.table_prefix}' data does not exist`)
	} else if (tblRec.record_id) {
		// record_id != ""
		// debugger
		if (listDataRec) {
			const param = {
				input_component_arr: mainState.arr.input_component_arr, // array of loaded components
				input_component: inputComponent.value,
				table: listRec.table || mainState.rec?.table,
				table_rec_type: listRec.table_rec_type || mainState.rec?.table_rec_type,
				schema: listRec.schema || mainState.rec?.schema,
				page: listRec.page // TODO: where this is set?
			}
			console.debug(`🚀 ~ getServerRec ~ param.listRec:`, param.listRec)
			// copy table record_id to rest call param[tblPrefix].record_id
			// param[rec.table_prefix] = { record_id: rec.record_id }
			param[rec.table_prefix] = { record_id: tblRec.record_id }
			setSync(false)
			nc.callServer(state, 'input', param, serverCallback, { direct_callback: true }) // direct_callback = do not set state variables
		} else {
			message.setWarning(state, `mainState.list row_index data does not exist`)
		}
	}
}

function detectChange(fieldName, newValue) {
	const origValue = nc.recData(loadedRec, fieldName)
	if (origValue !== newValue) {
		nc.recDataSet(changeRecord.value, fieldName, newValue)
	} else {
		const clearEmptySubkeyLevel = 2 // tbl.json_data.xxx
		nc.recDataSet(changeRecord.value, fieldName, undefined, clearEmptySubkeyLevel)
	}
	const tblRec = changeRecord.value[rec.table_prefix]
	setSaveEnabled(tblRec && !nc.isEmptyObject(tblRec))
}

function runTableCodeOnServer(fieldName, newValue) {
	const tblRec = rec[rec.table_prefix]
	if (!tblRec) {
		message.setWarning(state, `record '${rec.table_prefix}' data does not exist`)
		syncId = 'error'
	} else if (tblRec.record_id) {
		syncId = 'nc-input-form.' + rec.table + '.' + tblRec.record_id
		const param = {
			field_name: fieldName, // array of loaded components
			new_value: newValue,
			table: listRec.table,
			schema: listRec.schema,
			table_prefix: rec.table_prefix,
			table_rec_type: rec.table_rec_type,
			page: listRec.page,
			modify_rec: tblRec
		}
		// copy table record_id to rest call param[tblPrefix].record_id
		// param[rec.table_prefix] = { record_id: tblRec.record_id }
		setSync(false)
		nc.callServer(state, 'input/table-code', param, serverCallback, { direct_callback: true }) // direct_callback = do not set state variables
	}
}

function inputValueChanged(fieldName, newValue, event) {
	detectChange(fieldName, newValue)
	// TODO: call server and/or input form script

	if (fieldName == 'per.first_name') {
		if (newValue && rec.per.last_name) {
			rec.per.full_name = peg.concatWith(' ', newValue, rec.per.last_name)
			detectChange('per.full_name', rec.per.full_name)
		}
	} else if (fieldName == 'per.last_name') {
		if (newValue && rec.per.first_name) {
			rec.per.full_name = peg.concatWith(' ', rec.per.first_name, newValue)
			detectChange('per.full_name', rec.per.full_name)
		}
	}

	if (event && event.type === 'blur') {
		const prevValue = nc.recData(tableCodeRecord, fieldName)
		if (prevValue != newValue) {
			runTableCodeOnServer(fieldName, newValue)
		}
	}
	/*  	const param = {
				modify_rec: rec, // nc.recToServerRec(rec),
				field_name: fieldName,
				prev_value: previousValue,
				new_value: newValue,
				table: listRec.table || ''
			}
			nc.callServer(state, 'input/table-code', param, ret => {
				detectChange(fieldName, newValue)
				if (ret.link_grid) {
					if (ret.link_grid.dialog_closed === true) {
						if (ret.link_grid.selected != null) {
							previousValue = rec[ret.link_grid.linking_field]
							newValue = ret.link_grid.selected
							rec[ret.link_grid.linking_field] = newValue
							inputValueChanged(fieldName, newValue, previousValue) // recursive call, run script after succesfull select
						}
						ref = mainScope.changeRecord.value
						for (key in ref) {
							if (key === inputName) {
								func = ref[key]
								if (typeof func === 'from_function') {
									func('change') // call element changed() function to update prev value
								}
							}
						}
						// break
						document.getElementById('rec.' + ret.link_grid.linking_field).focus()
					}
				} else {
					let ref = mainScope.changeRecord.value
					for (key in ref) {
						func = ref[key]
						if (key === inputName) {
							if (typeof func === 'from_function') {
								func('change') // call element changed() function to update prev value
							}
						}
					}
				}
			})*/
}

// functions called from form
// eslint-disable-next-line no-unused-vars
function newRecord() {
	listRow.value = -1
	listDataRec = null
	nc.setRec(state, rec.table_prefix, nc.clearDeepRec(nc.clone(rec[rec.table_prefix]))) // nc.clone(rec)
	clearChangeRecord()
}

// eslint-disable-next-line no-unused-vars
function arrow(move) {
	if (move === 'first' && previousEnabled) {
		listRow.value = 1
	} else if (move === 'previous' && previousEnabled) {
		listRow.value--
	} else if (move === 'next' && nextEnabled) {
		listRow.value++
	} else if (move === 'last' && nextEnabled) {
		listRow.value = listData.length
	} else {
		return
	}
	listRec.row_index = listRow.value
	getServerRec()
}

// eslint-disable-next-line no-unused-vars
function tabClicked() {
	console.log(`🚀 ~ file: nc-input-form.js ~ tabClicked ~ rec.input_tab:`, rec.input_tab)
	/* if (state.arr.tab) {
					// this should run only when state.arr.tab changes
					state.arr.tab = state.arr.tab.map(item => {
						if (item.query_name) item.icon = 'filter'
						return item
					})
				} */
	/* // updateToolbar(callParam, ret.arr)
				if (rec.table && rec.table !== callParam.rec.table) {
					mainState.rec.table = rec.table
				}
				setCallParamRec(callParam)
				if (!nc.isEqual(callParam.rec, prevCallParamRec)) {
					saveParam()
				} */
}
// eslint-disable-next-line no-unused-vars
function callServer(action) {
	if (action === 'duplicate' && saveEnabled.value === true) {
		// duplicate button disable is not always fast enough
		return
	}
	let call
	// debugger
	let param = {
		table: listRec.table,
		schema: listRec.schema,
		table_rec_type: rec.table_rec_type,
		[rec.table_prefix]: rec[rec.table_prefix]
	}
	if (action) {
		param.action = action
	}
	if (action === 'init') {
		return // table change
	} else if (action === 'save') {
		if (rec.confirm_save) {
			const save = confirm('Tallennetaanko tehdyt muutokset?')
			if (save !== true) {
				return
			}
		}
		call = 'save'
		const tblRec = nc.clone(rec[rec.table_prefix])
		const jsonDataIsString = typeof tblRec.json_data === 'string'
		if (jsonDataIsString) {
			tblRec.json_data = nc.fromJson(tblRec.json_data)
		}
		delete param[rec.table_prefix]
		param.data = [{ [rec.table_prefix]: tblRec }]
		param = { save: [param] }
	} else if (action === 'revert') {
		// call = 'input/duplicate'
		let revert = true
		if (rec.confirm_revert) {
			revert = confirm('Perutaanko tehdyt muutokset?')
		}
		if (revert == true) {
			nc.setRec(state, rec.table_prefix, loadedRec[rec.table_prefix])
			clearChangeRecord()
		}
		return
	} else if (action === 'duplicate') {
		call = 'duplicate'
	} else {
		message.setWarning(state, `action '${action} is not valid`)
		return
	}
	let grid = null
	if (call === 'input/revert' || call === 'input/duplicate') {
		// call === 'input/save' ||
		if (grid) {
			param.grid_data = {}
			for (let key in grid) {
				let val = ref[key]
				if (val && val.table != null && val.data != null) {
					param.grid_data[val.table] = val.data
				}
			}
		}
	}
	// param.table = listRec.table
	// param.tab = rec.tab # just return it back - is this needed?
	setSync(false)
	nc.callServer(state, call, param, ret => {
		if (call === 'save') {
			for (const tbl of ret.save) {
				if (!nc.isArray(tbl.data)) {
					return message.setWarning(state, `Tallennuksen tulos ei sisällä 'data' -taulukkoa`)
				}
				for (const item of tbl.data) {
					if (action === 'save') {
						if (item == null) {
							return message.setWarning(state, `Tallennuksen tulos ei sisällä '${rec.table_prefix}' -objektia`)
						}
						rec[rec.table_prefix] = item[rec.table_prefix]
						const listTableRec = listDataRec && listDataRec[rec.table_prefix]
						const tableRec = rec[rec.table_prefix]
						nc.copyDeepRec(tableRec, listTableRec, rec.table_prefix)
					}
				}
			}
		} else if (call === 'input/save' || call === 'input/revert' || call === 'input/duplicate') {
			/* ref = mainScope.recChanged
					for (key in ref) {
						func = ref[key]
						if (typeof func === 'from_function') {
							func('clear') // call element changed() function to update to new initial value
						}
					}
					mainScope.recChanged = {}
					if (call === 'input/duplicate') {
						mainScope.recChanged.duplicate = true // set main rec save + ok to non.disabled
					} */
		}
		if (ret.grid_data && !nc.isEmptyObject(ret.grid_data)) {
			/* 	ref1 = ret.grid_data
					for (tbl in ref1) {
						arr = ref1[tbl]
						ref2 = grid
						for (gridName in ref2) {
							grid = ref2[gridName]
							if (grid.table === table) {
								if (nc.isEmptyObject(arr)) {
									grid.data.splice(0, grid.data.length)
								} else if (arr.length < grid.data.length) {
									toDelete = arr.length - grid.data.length
									grid.data.splice(arr.length, toDelete)
								}
								row = 0
								for (i = 0, len = arr.length; i < len; i++) {
									rec = arr[i]
									if (!grid.data[row]) {
										grid.data[row] = {}
									}
									nc.textRecToRec(rec, grid.data[row], 'js_date')
									row = row + 1
								}
								grid.refreshRows()
								break
							}
						}
					} */
		}
		if (call === 'input/save' || call === 'input/duplicate') {
			// addToSelection(call, ret.rec)
		}
		if (ret.status === 'ok') {
			clearChangeRecord()
		}
		setSync()
	})
}

function setSyncRec(toRec, fromRec) {
	for (let key in fromRec) {
		if (Object.prototype.hasOwnProperty.call(fromRec, key)) {
			if (isObject(fromRec[key]) && toRec[key]) {
				setSyncRec(toRec[key], fromRec[key])
			} else if (key !== 'sync' && toRec[key] !== fromRec[key]) {
				// console.log(`🚀 sync key '${key}' change '${toRec[key]}' -> '${fromRec[key]}'`)
				toRec[key] = fromRec[key]
			}
		}
	}
}

let updateSyncRec = true
let updateStateRec = true
watch(state.rec, () => {
	// debugger
	if (updateStateRec && rec.sync === true && sync) {
		const syncRec = state.sync.rec
		if (syncRec && rec[rec.table_prefix]) {
			updateSyncRec = false // prevent update loop
			// console.log(`🚀 rec changed`, syncRec, rec)
			setSyncRec(syncRec, rec[rec.table_prefix])
		}
	}
	updateStateRec = true
})

let syncId = ''
function setSyncOn() {
	const tblRec = rec[rec.table_prefix]
	if (!tblRec) {
		message.setWarning(state, `record '${rec.table_prefix}' data does not exist`)
		syncId = 'error'
	} else if (tblRec.record_id) {
		syncId = 'nc-input-form.' + rec.table + '.' + tblRec.record_id
	}
	console.log(`🚀  set sync On, id:`, syncId)
	sync = state.getSyncState(syncId)
	state.sync = { rec: sync.data.rec }
	sync.connect()
	// updateStateRec = false // no initial sync
	watch(state.sync.rec, () => {
		// debugger
		if (updateSyncRec && rec.sync === true) {
			if (rec[rec.table_prefix]) {
				updateStateRec = false // prevent update loop
				const newRec = nc.clone(state.sync.rec)
				// console.log(`🚀 syncRec changed`, newRec, rec)
				setSyncRec(rec[rec.table_prefix], newRec)
			}
		}
		updateSyncRec = true
	})
}

function setSync(value) {
	if (sync && sync.disconnect) {
		console.log(`🚀  set sync Off, id:`, syncId)
		sync.disconnect()
		sync = null
		syncId = ''
	}
	if (value === true || rec.sync === true) {
		setSyncOn()
	}
}
