import React , {useMemo, useRef, useEffect, useCallback,useState} from 'react';
import { shallowEqual, connect ,useSelector,useDispatch,batch} from 'react-redux';
import * as Shared from 'placeholder-shared'
import * as Common from './common'
import MonacoEditor from 'react-monaco-editor'
import * as StepMenu from './StepMenu'
import * as Antd from 'antd'
import * as Step from './Step'
import * as Steps2 from './Steps2'
import TextField from '@material-ui/core/TextField';
import Split from 'react-split'
import * as Drag from './Drag'
import * as ModelViewer from './ModelViewer'
import * as Data from './Data'
import * as HotKeys from 'react-hotkeys-hook'
import sqlFormatter from "sql-formatter";
import useDebouncedCallback from "use-debounce/lib/useDebouncedCallback";


import {
	MenuFoldOutlined,
	MenuUnfoldOutlined,
	FormatPainterOutlined,
	PartitionOutlined
  } from '@ant-design/icons'
import ReactResizeDetector from 'react-resize-detector';

export const useFormat = () => {
	const dispatch = useDispatch()
	return useCallback(() => {
		dispatch(((dispatch, getState) =>{
			const currentStep = getState().currentStep
			const newSql = Common.FormatSQL(getState().steps[currentStep].operation.sql)
			dispatch(Shared.State.getUpdateStepOperationAction(currentStep, 'sql', newSql, false))
		}))
	}, [dispatch])
}
export const SQLStepMenuParent = (props:{}) => {
	const currentTask = useSelector(state => (state as any).currentTask)
	const dispatch = useDispatch()
	const onChange = (key)=> {
		dispatch(Shared.State.getShowStepAction(parseInt(key)))
	}
	const onEdit = (targetKey, action)=> {
		if (action == 'add') {
			dispatch((dispatch, getState) => {
				dispatch(Shared.State.changeUIState('mainWindowHide', false))
				let newStepCounter = Step.getNextID(getState().treeData[1])
				const operation = Steps2.StepDict[Shared.Runner.StepTypes.sql].newOperation()

				batch(() => {
					dispatch(Shared.State.getCreateStepAction(operation, currentTask, newStepCounter, "", true, 0, 0))
					dispatch(Shared.State.getUpdateStepOperationAction(newStepCounter, 'sql',
						`SELECT * FROM \$\{someTable\}`
						, false))
					dispatch(Shared.State.getShowStepAction(newStepCounter))
				})
			})
		}
		else if (action == 'remove') {
			dispatch(Shared.State.removeStepFromTabs(parseInt(targetKey)))
		}
	}
	const currentStep = useSelector(state => (state as any).currentStep)
	const tabs = useSelector(state => (state as any).sqlTabs)
	const steps = useSelector(state => (state as any).steps)
	return (
		/*}
		<Antd.Tabs
		animated={true}
			className='columnFlexBox flexGrow' 
			type="editable-card"
			onChange={onChange}
			activeKey={currentStep.toString()}
			onEdit={onEdit}
		>
			{tabs.map((pane) => {
				const stepData = steps[pane]
				console.log("got new tab", currentStep, pane)
				return < Antd.Tabs.TabPane tab={Shared.Runner.getName(stepData)} key={(pane).toString()} closable={true} 
				className='columnFlexBox flexGrow'
				>
			*/
		< SQLStepMenu stepCounter={currentStep}/>
		/*}

				</Antd.Tabs.TabPane>
			})
		}
		</Antd.Tabs>
		*/
	)
}

export const FullSQLStepMenu = (props:{}) => {
	const [barSplitValue, barSplitChangeLayout] = Drag.useChangeLayout(72, 'barSplit')
	const currentStep = useSelector (state => (state as any).currentStep)
	const mainChildren = <ModelViewer.ModelViewer stepCounter={currentStep}/>
	const stepCounter = useSelector(state => (state as any).currentStep)


	const sqlEditor = <Common.DivWithClassName className='backgroundWhite columnFlexBox flexGrow'>
		<SQLStepMenu stepCounter={stepCounter} />
		<StepMenu.RunPanel />
	</Common.DivWithClassName>

	const barSplitEnd= useCallback((sizes) => {
		barSplitChangeLayout(sizes[0])
	}, [barSplitChangeLayout])

	return <Common.DivWithClassName className='backgroundWhite columnFlexBox flexGrow'>
		<Common.DivWithClassName className='rowFlexBox flexGrow'>
			<Common.DivWithClassName className='flexGrow rowFlexBox height100Percent'>
					<ModelViewer.ModelViewerParent stepCounter={currentStep} />
			</Common.DivWithClassName>
			<Common.DivWithClassName className='rowFlexBox flexStay550px'>
				{sqlEditor}
			</Common.DivWithClassName>
		</Common.DivWithClassName>
		<Data.DataViewer showStepOnly={true} stepCounter={currentStep} />
	</Common.DivWithClassName>
}

export const SQLStepMenu = (props:{stepCounter:number}) => {
	const dispatch = useDispatch()
	const currentStep = props.stepCounter
	const code = useSelector(state => (state as any).steps[currentStep].operation.sql)
	const [codeScratch, setCodeScratch] = useState(code) 
	const tableRefs= useSelector(state => (state as any).tableRefs[currentStep])
	const emptyNameActivated = useSelector(state => (state as any).UIState.emptyNameActivated)
	const [debouncedFunction, cancel, callPending] = useDebouncedCallback(() => {
		dispatch(Shared.State.getUpdateStepOperationAction(currentStep, 'sql',codeScratch, false))
	  }, 100);
	
	  useEffect(() =>  {
		  return () => {
			  callPending()
		  }
	  },[callPending])
	const previousTableRefs = useRef(tableRefs)
	useEffect(() => {
		const config:Shared.Runner.IConfig = Shared.Runner.getConfigFromSQLString(code)
		
		//console.log("got table refs", config.tableRefs)
		if (!Shared.Common.isEquivalent(config.tableRefs,previousTableRefs.current)) {
			//console.log("dispatching table ref update", previousTableRefs.current, config.tableRefs)
			dispatch(Shared.State.getUpdateTableRefAction(currentStep, config.tableRefs))
		}
		previousTableRefs.current = config.tableRefs;
	},[code,dispatch,currentStep])
	const [size, setSize] = useState({
		editorWidth: 0, editorHeight: 0
	})
    const onResize = (width:number, height:number) => {

		//console.log("got resize", width, height)
		setSize({editorWidth:width, editorHeight:height})
		editor.current && editor.current.layout({width:width, height:height})
	}
	const onChange = (newValue, e) => {
		setCodeScratch(newValue)
		debouncedFunction()
	}
	const formatCode =  useFormat()

	const editor = useRef(null)
	const editorDidMount = useCallback((editorToUse,monaco)=> {
		editor.current = editorToUse;
		editorToUse.addAction({
			// An unique identifier of the contributed action.
			id: 'preview',
		
			// A label of the action that will be presented to the user.
			label: 'Run Preview',
		
			// An optional array of keybindings for the action.
			keybindings: [
				// chord
				monaco.KeyMod.CtrlCmd|  monaco.KeyCode.Enter
			],
		
			// A precondition for this action.
			precondition: null,
		
			// A rule to evaluate on top of the precondition in order to dispatch the keybindings.
			keybindingContext: null,
		
			contextMenuGroupId: 'navigation',
		
			contextMenuOrder: 1.5,
		
			// Method that will be executed when the action is triggered.
			// @param editor The editor instance is passed in as a convinience
			run: function(ed) {
				callPending()
				dispatch(Shared.Runner.runStepPreview(currentStep))
				

			}
		});
		editorToUse.addAction({
			// An unique identifier of the contributed action.
			id: 'format code',
		
			// A label of the action that will be presented to the user.
			label: 'Format Code',
		
			// An optional array of keybindings for the action.
			keybindings: [
				// chord
				monaco.KeyMod.Shift|  monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_F
			],
		
			// A precondition for this action.
			precondition: null,
		
			// A rule to evaluate on top of the precondition in order to dispatch the keybindings.
			keybindingContext: null,
		
			contextMenuGroupId: 'navigation',
		
			contextMenuOrder: 1.5,
		
			// Method that will be executed when the action is triggered.
			// @param editor The editor instance is passed in as a convinience
			run: function(ed) {
				formatCode()
			}
		});
		editorToUse.addAction({
			// An unique identifier of the contributed action.
			id: 'Toggle Tabs',
		
			// A label of the action that will be presented to the user.
			label: 'Toggle Tabs',
		
			// An optional array of keybindings for the action.
			keybindings: [
				// chord
				monaco.KeyMod.CtrlCmd| monaco.KeyCode.KEY_E
			],
		
			// A precondition for this action.
			precondition: null,
		
			// A rule to evaluate on top of the precondition in order to dispatch the keybindings.
			keybindingContext: null,
		
			contextMenuGroupId: 'navigation',
		
			contextMenuOrder: 1.5,
		
			// Method that will be executed when the action is triggered.
			// @param editor The editor instance is passed in as a convinience
			run: function(ed) {
			dispatch(Shared.State.previousTabAction())
			}
		});		

		editor.current.layout({width:size.editorWidth, hegiht:size.editorHeight})
	},[currentStep,size.editorHeight, callPending,formatCode,size.editorWidth, dispatch])
	const getNameModal = (child) => {
		return child;
		return (<><Common.DivWithClassName onClick={() => {
		}}
			className={Common.classNames({
				emptyNameActivated
			})}
		>
		</Common.DivWithClassName>
			{child}
		</>)
	}
	const activeKey = useSelector(state => (state as any).UIState.SQLStepMenuActiveKey)
	const runResultStatus = useSelector(state => (state as any).runResults[currentStep].status)
	useEffect(() => {
		if (runResultStatus == 'changed'){
			dispatch((dispatch, getState) => {
				const gFinal = getState().edges.gFinal
				if (gFinal){
					let successors = []
					const processNode = (stepCounter) => {
						const thisP = gFinal.successors(stepCounter)
						if (thisP) {
							thisP.forEach((singleP) => {
								successors.push(parseInt(singleP))
								processNode(singleP)
							})
						}
					}
					processNode(currentStep)
					dispatch(Shared.State.getSetChanged(successors))
				}
			})
		}
	}, [dispatch,runResultStatus,currentStep])
	return <Common.DivWithClassName className='padding5px columnFlexBox flexGrow'>
		<Common.DivWithClassName className='flexStay rowFlexBox'>
			{getNameModal(<MemoStepName stepCounter={currentStep} />
			)}
		</Common.DivWithClassName>
		<Common.DivWithClassName className='columnFlexBox flexGrow'>
			<Antd.Tabs key="tabs_key" type='card' activeKey={activeKey} className='columnFlexBox flexGrow SQLStepMenuTabs' size='small' defaultActiveKey='SQL' tabBarExtraContent={<SQLAction />}>
			<Antd.Tabs.TabPane key='SQL' tab='SQL' className='columnFlexBox flexGrow'>
				<Common.DivWithClassName className='flexGrow positionRelative EditorContainer'>
					<ReactResizeDetector handleWidth handleHeight onResize={onResize}>
						<Common.DivWithClassName className='positionAbsolute'>
							<MonacoEditor key={currentStep}
								width={size.editorWidth}
								height={size.editorHeight}
								language="sql"
								theme="vs-light"
								value={codeScratch}
								options={Common.editorOptions as any}
								onChange={onChange}
								editorDidMount={editorDidMount}
							/>
						</Common.DivWithClassName>
					</ReactResizeDetector>
				</Common.DivWithClassName>
			</Antd.Tabs.TabPane>
			<Antd.Tabs.TabPane tab='Tests' key='Tests'>
			</Antd.Tabs.TabPane>
			</Antd.Tabs>
	</Common.DivWithClassName>
	</Common.DivWithClassName >
}

const isPresetName = (name) => {
	const r=  /NewModel\d+/
	return r.test(name)
}
export const StepName = (props:{stepCounter:number})=> {
	const currentStep = props.stepCounter
	const name = useSelector(state => (state as any).steps[currentStep].operation.name)
	const dispatch = useDispatch()
	const nameRef = useRef(null)
	const [scratchName, setScratchName] = useState(-1);

	//const emptyNameActivated = useSelector(state => (state as any).UIState.emptyNameActivated)
	useEffect(() => {
		if (isPresetName(name)){
			nameRef.current.select()
			nameRef.current.focus()
		}
	}, [name])
	const nameExistsCB = StepMenu.useNameAlreadyExistsCB()

	const onChangeName = useCallback((e) => {
		setScratchName(e.target.value.trim())
		//dispatch(Shared.State.getUpdateStepOperationAction(currentStep, 'name', e.target.value, false))
	}, [setScratchName])

	//console.log("using name", scratchName, name)
	return <Step.StepField 
		onFocus={(e: any) => {
			console.log("ran on focus", scratchName)
			if (scratchName == -1)
				setScratchName(name)
		}}
		onBlur={(e: any) => {
			const [nameExists, conflictingName] = nameExistsCB(currentStep, e.target.value)
			if (nameExists) {
				Antd.message.error(<span>Cannot change Model name to <b>{e.target.value}</b>. {conflictingName} already exists in the graph</span >)
				e.preventDefault()
				setScratchName(name)
				return;
			} else if (e.target.value == "") {
				Antd.message.error(<span>Model name cannot be empty</span >);
				e.preventDefault()
				setScratchName(name)
			} else {
				batch(() => {
					dispatch(Step.renameSuccessorsThunk(currentStep, e.target.value))
					dispatch(Shared.State.getUpdateStepOperationAction(currentStep, 'name', e.target.value, false))
				})
				setScratchName(-1)
			}
		}}

		onKeyUp={(ev) => {
			if (ev.key === 'Escape' && !name.length) {
				setScratchName(-1)
				ev.preventDefault();
			}
			if (ev.key === 'Escape' && name.length) {
				setScratchName(-1)
				ev.preventDefault();
			}
		}}
		onKeyPress={(ev) => {
			//console.log(`Pressed keyCode ${ev.key}`);
			if (ev.key === 'Enter' && (scratchName as any).length) {
				// Do code here
				const [nameExists, conflictingName] = nameExistsCB(currentStep, scratchName)
				if (nameExists) {
					Antd.message.error(<span>Cannot change Model name to <b>{scratchName}</b>. {conflictingName} Already exists in the graph</span >)
					ev.preventDefault()
					return;
				}
				nameRef.current.blur()
				ev.preventDefault();
			}
		}}
		nameRef={nameRef} id="standard-basic" label="Model Name"  value={
			scratchName != -1? scratchName :
				name}
		onChange={onChangeName}
	/>
}
const  MemoStepName = React.memo(StepName)
const SQLAction = (props: {}) => {
	const currentStep = useSelector(state => (state as any).currentStep)
	const dispatch = useDispatch()

	const onFormat = useFormat()

	return <Common.DivWithClassName className='flexStay floatRight'>
		<StepMenu.FormatButton onClick={onFormat} text='Format SQL (⇧⌘F)'/>
	</Common.DivWithClassName>
}
