import React, {useMemo, useCallback, useState, useEffect, useRef, useLayoutEffect} from 'react';
import { Provider,shallowEqual, connect ,useSelector,useDispatch,batch} from 'react-redux';
import {applyMiddleware, combineReducers, createStore} from 'redux'

import Card from '@material-ui/core/Card';

import * as Immer from 'immer'
import * as Redux from 'redux'
import * as Antd from 'antd'
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from "redux-thunk";
import { createLogger } from 'redux-logger'

import LinearProgress from '@material-ui/core/LinearProgress';


import TextField from '@material-ui/core/TextField';
import FormHelperText from '@material-ui/core/FormHelperText';

import Alert from '@material-ui/lab/Alert';

import Stepper from '@material-ui/core/Stepper';
import Step from '@material-ui/core/Step';
import StepLabel from '@material-ui/core/StepLabel';
import StepContent from '@material-ui/core/StepContent';
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';

import InputLabel from '@material-ui/core/InputLabel';

import Checkbox from '@material-ui/core/Checkbox';

import { withStyles,makeStyles, useTheme } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';

import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';

import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormControl from '@material-ui/core/FormControl';
import FormLabel from '@material-ui/core/FormLabel';

import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
import IconButton from '@material-ui/core/IconButton';

import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableContainer from '@material-ui/core/TableContainer';
import TableHead from '@material-ui/core/TableHead';
import TablePagination from '@material-ui/core/TablePagination';
import TableRow from '@material-ui/core/TableRow';
import Toolbar from '@material-ui/core/Toolbar';

import CachedIcon from '@material-ui/icons/Cached';

import server from './serverProxy'

import * as _ from 'lodash'
import * as Axios from 'axios'

import throttle from 'lodash.throttle';


export function checkKeys(obj, keyPath) {
	return _.has(obj,keyPath)
}
export function getValue(obj, keyPath) {
	return _.get(obj,keyPath)
}

export function getValueSafe(obj, keyPath, ifFalseReturn) {
	let result = checkKeys(obj, keyPath)
	if (!result) {
		return ifFalseReturn
	}
	else{
		return getValue(obj, keyPath)
	}
}

export const loadState = () => {
	try {
		const serializedState = localStorage.getItem('state');
		if (serializedState === null) {
			return undefined;
		}
		return JSON.parse(serializedState);
	} catch (err) {
		return undefined;
	}
}; 

export const saveState = (state) => {
	try {
		const serializedState = JSON.stringify(state);
		localStorage.setItem('state', serializedState);
	} catch {
		// ignore write errors
	}
};

export const useChangeOperationArray = (stepCounter, keyPathHoldingArray) => {
    const dispatch = useDispatch()
    const arr = useSelector(state => {
        return getValueSafe((state ).steps[stepCounter].operation, keyPathHoldingArray, null)
    })
    return (modifyCB) => {
        const newOps = modifyCB(arr)
        dispatch(getChangeSyncOperationAction(1, keyPathHoldingArray, newOps))
    }
}


const handshake = 'f5fa88b55b5e249d396621f6eb45d0995bf0e7d892f079c9909cfc586073cdd2cf0539df263fa091c41d46aca102b16606e5efc4a0d9844b36b13103b8bf22fbf3bb31eb83dfc244e2e00013054c704f'
function generateQueryString(data) {
	const params = [];
	for (var d in data){
		let dataToPush = data[d]
		if (typeof(data[d]) == 'object') {
			dataToPush = JSON.stringify(dataToPush)
		}
		params.push(encodeURIComponent(d) + '=' + encodeURIComponent(dataToPush));
	}
	return  params.join('&');
 }

const getConfigFromState = (state) => {
	const syncOperation = state.syncOperations[1]
	return syncOperation;
}

const executeQuery = (endpoint, query, body) => {
	return new Promise((resolve, reject) => {
		if (!server) {
			console.log("posting query", endpoint, query, body)
			Axios.post(`/api/${endpoint}`, {},
				{
					data: body,
					params: query,
					headers: {
						handshake
					}
				}
			).then((result) => {
				console.log("got result", result)
				resolve(result.data)
			}).catch((error) => {
				console.log("got error result", error)
				reject({ error: error, errorMessage: error.message })
				//dispatch(getNewConnectionResultAction(error))
			})
		}
		else {
			console.log("posting apps scripts query", query, body )
			const queryString = generateQueryString(query)
			console.log("posting apps scripts query", query, queryString, body)
			server.doQuery(endpoint, queryString, JSON.stringify(body), 'post').then((r) => {
				console.log("got result",r )
				resolve(r)
			}).catch((error) => {
				reject(error)
			})
		}
	})
}

const startSync = () => {
	return (dispatch,getState)=> {
		getNamedRanges().then((r) => {
			dispatch(getNewNamedRangesAction(r))
			const currentState = getState()
			const syncOperation = currentState.syncOperations[1]
			const namedRange = syncOperation.namedRange
			const namedRangeData = currentState.namedRanges[syncOperation.namedRange]
			return new Promise((resolve, reject) => {
				/*
				if (!server) {
					*/
				console.log("posting query", currentState, namedRange, namedRangeData)
				executeQuery('query', {
					type: 'snowflake',
					config: getConfigFromState(currentState),
					job: 'sync'
				}, {
					data: namedRangeData,
					namedRange,
					syncOperation: syncOperation
				})
					.then((result) => {
						console.log("got result", result)
						dispatch(getNewSyncResultAction(result.syncResultID))
					}).catch((error) => {
						dispatch(getNewSyncResultAction(error))
					})
					resolve({})
					/*
				}
				else {
					execute
					server.doQuery('query', generateQueryString({
						config: JSON.stringify(getConfigFromState(getState())),
						type: 'snowflake'
					}), 'post').then((r) => {
						dispatch(getNewConnectionResultAction(r))
					})
					resolve({})
				}
				*/
			})
		})
	}
}

const updateSyncResultThunk = () => {
	return (dispatch, getState) => {
		const currentState = getState()
		return new Promise((resolve, reject) => {
			const currentSyncResult = currentState.currentSyncResult
			executeQuery('query',
				{
					type: 'syncResult',
					syncResultID: currentSyncResult
				}, {

			}).then((result) => {
				console.log("got update syncresult", result)
				dispatch(getUpdateSyncResult(currentSyncResult, result))

			}).catch((error) => {
				console.log("got error update syncresult", error)
				dispatch(getUpdateSyncResult(currentSyncResult, error))
			})
			resolve({})
		})
	}
}

const confirmConnection = () => {
	return (dispatch, getState) =>{
		return new Promise((resolve, reject) => {
			executeQuery('query', {
							type: 'snowflake',
							config: getConfigFromState(getState()),
						}, 
						{}
				).then((result) => {
					dispatch(getNewConnectionResultAction(result.data))
				}).catch((error) => {
					dispatch(getNewConnectionResultAction(error))
				})
				resolve({})
			})
	}
}

const applyBanding = (namedRange) => {
	return (dispatch, getState) => {
		return new Promise((resolve, reject) => {
			if (!server) {
				console.log("no server not applying banding")
				resolve({})
			}
			else {
				console.log("applying banding", namedRange)
				return server.setBanding(namedRange).then(() => {
					console.log("banding set", namedRange)
					resolve({})
				}).catch(reject)
			}
		})
	}
}

function getNamedRanges() {
	return new Promise((resolve, reject) => {
		if (!server) {
			console.log("failed to use server")
			resolve(JSON.parse(JSON.stringify(
				{
					"NamedRange1": [[{ "datatype": "string", "raw": "string" }, { "datatype": "string", "raw": "integer" }, { "datatype": "string", "raw": "date" }, { "datatype": "string", "raw": "number" }, { "datatype": "string", "raw": "boolean" }, { "datatype": "string", "raw": "f" }, { "datatype": "string", "raw": "g" }, { "datatype": "string", "raw": "h" }, { "datatype": "string", "raw": "baz2" }],
					[{ "datatype": "string", "raw": "string" }, { "datatype": "string", "raw": "integer" }, { "datatype": "string", "raw": "date" }, { "datatype": "string", "raw": "number" }, { "datatype": "string", "raw": "boolean" }, { "datatype": "string", "raw": "f" }, { "datatype": "string", "raw": "g" }, { "datatype": "string", "raw": "h" }, { "datatype": "number", "raw": 2 }]
					]
				}))
			)
			return;
		}
			console.log("using server")
		return server.getNamedRangesData().then((r) => {
			debugger;
			console.log("got named ranges", r, google)
			window.r = r;
			resolve(r)
		}).catch(reject)
	})
}

const swallowLogs = false;
const loggerMiddleware = createLogger({
	predicate: (getState, action) => {
		if (swallowLogs) {
            return true;
		}
		return true;
	},

	'stateTransformer':(state) => {
		return state
		var newState = JSON.parse(JSON.stringify(state))
		//newState['specs'] = null
		return newState
	}	
})

const middleware = [
    thunk ,
	loggerMiddleware
];

const composeEnhancers = composeWithDevTools({
})
//export const myStore = createStore(rootReducer, composeWithDevTools(applyMiddleware(...middleware)))
export const myMiddleware = (applyMiddleware(...middleware))
export const createSStore= (rootReducer) => {
	return createStore(rootReducer, (applyMiddleware(...middleware)))
}

const rootReducer = (state, action) => {
  return myAppReducers(state, action);
}

export const getNewNamedRangesAction = (namedRanges) => {
	return {
		type: NEW_NAMED_RANGES, namedRanges
	}
}

export const getNewConnectionResultAction = (connectionResult) => {
	return {
		type:NEW_CONNECTION_RESULT,
		connectionResult
	}
}

export const NEW_CONNECTION_RESULT ='NEW_CONNECTION_RESULT'
export const connectionResultReducer = (state = null, action) => {
	switch (action.type) {
		case NEW_CONNECTION_RESULT:
			return action.connectionResult;
		case CHANGE_UI_STATE:
			if (action.key == 'activeStep' && action.value == 1)
				return null;
			else {
				return state
			}
		default:
			return state;
	}
}

export const NEW_NAMED_RANGES='NEW_NAMED_RANGES'
export const NamedRangesReducer = (state = {}, action) => {
	switch (action.type) {
		case NEW_NAMED_RANGES:
			return action.namedRanges;
		default:
			return state;
	}

}

export const CHANGE_SYNC_OPERATION='CHANGE_SYNC_OPERATION'
export const getChangeSyncOperationAction = (syncID, key,value) => {
	return {
		type:CHANGE_SYNC_OPERATION, key,value,syncID
	}
}

export const SyncOperationReducer= (state = {}, action) => {
	switch (action.type) {
		case CHANGE_SYNC_OPERATION:{
			let keyPath = action.key;
			if (keyPath == 'namedRange') {
				let newState = Immer.produce(state, (draftState) => {
					draftState[action.syncID] = {
						...draftState[action.syncID],
						namedRange: action.value,
						fields:null
					}
				}) //reset the sync operation state
				return newState
			}
			if (!Array.isArray(action.key)) {
				keyPath = [action.key]
			}
			const last = keyPath.pop()
			let newState = Immer.produce(state, (draftState) => {
				if (!draftState[action.syncID]) { //create on first set
					draftState[action.syncID] = {}
				}
				let syncOperation = draftState[action.syncID]
				let obj = syncOperation;
				for (const key of keyPath) {
					obj = obj[key]
				}
				obj[last] = action.value;
			})
			return newState;
		}
		default:
			return state;
	}

}

export const CHANGE_UI_STATE='CHANGE_UI_STATE'
export const getChangeUIStateAction = (key,value) => {
	return {
		type:CHANGE_UI_STATE,key,value
	}
}
export const UIStateReducer = (state = {activeStep:0}, action) => {
	switch (action.type) {
		case CHANGE_UI_STATE:
			return {
				...state,
				[action.key]: action.value
			}
		default:
			return state
	}
}

export const NEW_SYNC_RESULT = 'NEW_SYNC_RESULT'
export const UPDATE_SYNC_RESULT= 'UPDATE_SYNC_RESULT'
export const getNewSyncResultAction= (syncResultID) => {
	return { type: NEW_SYNC_RESULT, syncResultID}
}

export const getUpdateSyncResult = (syncResultID, syncResult) => {
	return {
		type: UPDATE_SYNC_RESULT,
		syncResultID, syncResult
	}
}

export const syncResultsReducer = (state = {}, action) => {
	switch (action.type) {
		case NEW_SYNC_RESULT: {
			return { ...state, [action.syncResultID]: {} }
		}
		case UPDATE_SYNC_RESULT:
			return { ...state, [action.syncResultID]: action.syncResult }
		default:
			return state;
	}
}

export const currentSyncResult = (state = {}, action) => {
	switch (action.type) {
		case CHANGE_UI_STATE:
			if (action.key == 'activeStep' && action.value == 2){
				return null;
			}
			else{
				return state;
			}
		case NEW_SYNC_RESULT: {
			return action.syncResultID;
		}
		default:
			return state;
	}
}

export const myAppReducers = combineReducers({
	namedRanges: NamedRangesReducer,
	syncOperations: SyncOperationReducer,
	connectionResult: connectionResultReducer,
	syncResults:syncResultsReducer,
	currentSyncResult:currentSyncResult,
	uiState:UIStateReducer
})

export const myStore = createSStore(rootReducer)

export const Styles = (props) => {
	return <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
}

export const Sheet = (props) => {
	return <Provider store={store}>
		<Styles />
		<Home />
	</Provider>
}

const StyledTableCell = withStyles((theme) => ({
	head: {
	  backgroundColor: theme.palette.common.black,
	  color: theme.palette.common.white,
	},
	body: {
	  fontSize: 14,
	},
  }))(TableCell);
const useStyles = makeStyles((theme) => ({
  root: {
	  minHeight:0,
	display:'flex',
	flexDirection:'column'
  },
  button: {
    marginTop: theme.spacing(1),
    marginRight: theme.spacing(1),
  },
  actionsContainer: {
    marginBottom: theme.spacing(2),
  },
  resetContainer: {
    //padding: theme.spacing(1),
  },
}));

function getSteps() {
  return ['Sheets Data', 'Target Database', 'Sync'];
}

  
export function FullWidthTabs() {
	const classes = useStyles();
	const [value, setValue] = React.useState(0);

	const handleChange = (event, newValue) => {
		setValue(newValue);
	};

	const handleChangeIndex = (index) => {
		setValue(index);
	};

	return (
		<div className={classes.root}>
			<AppBar position="static" color="default">
				<Tabs
					value={value}
					onChange={handleChange}
					indicatorColor="primary"
					textColor="primary"
					centered
				>
					<Tab label="Sync" />
					<Tab label="History" />
				</Tabs>
			</AppBar>
		</div>
	);
}

export const SelectRange = (props) => {
	const dispatch = useDispatch()
	const namedRanges = useSelector(state => state.namedRanges)

	let done = false;
	const namedRange = useSelector(state => {
		return state.syncOperations[1] ? state.syncOperations[1].namedRange : ''
	}
	)
	const timer = useRef(null)

	useEffect(() => {
		const start = () => {
			timer.current = setTimeout(() => {
				getNamedRanges().then((r) => {
					dispatch(getNewNamedRangesAction(r))
					if (!done)
						start()
				})
			}, 100);
		}
		start()
		return () => {
			console.log("clearing timeout")
			done = true
			clearTimeout(timer.current);
		}
	}, [dispatch]);
	/*
	useEffect(() => {
		getNamedRanges().then((r) => {
			dispatch(getNewNamedRangesAction(r))
		})
	}, [dispatch])
	*/
	const options = Object.keys(namedRanges).map(d => {
		return {
			key: d,
			label: d,
		}
	})
	if (!namedRanges)
		return null;
	return <Box flexWrap='wrap'>	
		<Box py={1} display='flex'>
			<SelectType onSelect={(newNamedRange) => {
				console.log("got new named range", newNamedRange, "old named range", namedRange)
				if (newNamedRange && namedRange != newNamedRange) {
					dispatch(getChangeSyncOperationAction(1, 'namedRange', newNamedRange))
				}
				dispatch(applyBanding(newNamedRange))
			}}
			label='Data Range' value={
				namedRange}
				values={options} />
				{/*}
			<IconButton style={{ marginLeft: 'auto',position:'relative', top:'8px' }}>
				<CachedIcon />
			</IconButton>
			*/}
		</Box>
	</Box> 

}

export const SelectSheet = (props) => {
	return <Select>
	</Select>
}
export const SelectType = (props) =>
{
	const {label, value, values, onSelect, error, helperText} = props
	const [open, setOpen] = useState(false)

	return <FormControl fullWidth >{label != null && <InputLabel>{props.label}</InputLabel>}
		<Select open={open} onOpen={() => setOpen(true)} onClose={(e) => {
			console.log("GOT NEW TARGET VALUE", e, e.target, e.target.value)
			onSelect(e.target.innerText)
			setOpen(false)
		}} error={error}  value={value} fullWidth label="Select Data" >
			{values.map(v => {
				return <MenuItem value={v.key} key={v.key}>{v.label}</MenuItem>
			})}
		</Select>
		{helperText && <FormHelperText>{helperText}</FormHelperText>}
	</FormControl>
}

export const SelectType2 = (props) =>
{
	const {label, value, values, onSelect, error, helperText} = props

	return <FormControl fullWidth >{label != null && <InputLabel>{props.label}</InputLabel>}
		<Select onChange={(e) => onSelect(e.target.value)} error={error} value={value} fullWidth label="Select Data" >
			{values.map(v => {
				return <MenuItem value={v.key} key={v.key}>{v.label}</MenuItem>
			})}
		</Select>
		{helperText && <FormHelperText>{helperText}</FormHelperText>}
	</FormControl>
}
export const CheckdownType = (props) => {
	const {variant,label, value, handleChange}  = props
	return <FormControlLabel
	variant={variant}
		value="IgnoreEmptyRows"
		control={<Checkbox color="default" />}
		label="Ignore Empty Rows"
		labelPlacement="end"
	/>
}
export const DataType = (props) => {
	const { dataName} = props;
	const dispatch = useDispatch()

	const classes = useStyles();
	const syncOperation = useSelector(state => state.syncOperations[1])
	const data = useSelector(state => state.namedRanges[syncOperation.namedRange])
	const getHeaders = () => {
		if (!syncOperation.fields)
			return []
		return syncOperation.fields.map((f, index) => {
			return {
				datatype:f.datatype,
				name:f.name
			}
		})
	}
	const getNonmatchingRows = useCallback((index, type) => {
		let nonMatchingRows = []
		data.slice(1).forEach((row, rowNumber) => {
			if (row[index].datatype != type)
				nonMatchingRows.push(rowNumber + 1)
		})
		return nonMatchingRows.length ? nonMatchingRows : null;
	},[data])
	const fieldNonmatchingRows = useMemo(() => {
		if (!syncOperation.fields || !data || syncOperation.fields.length != data[0].length) {
			console.log("not computing nonmatching rows", syncOperation,data)
			return []
		}
		return data[0].map((d, index) => {
			const proposedType = syncOperation.fields[index].datatype
			const nonmatchingRows = getNonmatchingRows(index, proposedType)
			return nonmatchingRows
		})
	}
		, [data, getNonmatchingRows, syncOperation])

	const getColumnSelect = (h, index) => {
		let errorText = null
		if (data[0][index].raw != h.name) {
			errorText = `non-matching column name ${data[0][index].raw} - expected ${h.name}`
		}
		return <FormControl error={errorText}>
			{h.name}
			{errorText && <FormHelperText error={errorText}>{errorText}</FormHelperText>}
		</FormControl>
	}

	const getTypeSelect = (h, index) => {
		let error = null, errorText = null;
		if (h.datatype == '' || fieldNonmatchingRows[index]) {
			error = true;
			errorText = 'values not matched'
		}
		const options = [{ key: 'object', label: 'Date' },
		{ key: 'number', label: 'Number' },
		{ key: 'string', label: 'Text' },
		{ key: 'boolean', label: 'Boolean' }]
			const onSelect = (type) =>{
				dispatch(getChangeSyncOperationAction(1, ['fields', index, 'datatype'], type))
			}
		return <SelectType2 onSelect={onSelect}
			value={h.datatype} error={error} helperText={errorText} values={options}
		/>
	}
	const detectTypesCB = () => {
		if (data.length < 2) {

		}
		else {
			const fields = data[0].map((d, index) => {
				debugger;
				const proposedType = data[1][index].datatype;
				const nonmatchingRows = getNonmatchingRows(index, proposedType)
				return {
					name: d.raw,
					datatype: !nonmatchingRows? proposedType:''
				}
			})
			dispatch(getChangeSyncOperationAction(1, 'fields', fields))
		}
	}

	if (!data)
		return null;
	const tableHeader = <TableHead>
		<TableRow>
			{/*}
			<TableCell padding='checkbox'>
				<Checkbox
					color='default'
					size='small'
					checked={true}
				/>
			</TableCell>
*/}
			<TableCell>
				<Typography variant='subtitle2'>Column Name</Typography>
			</TableCell>
			<TableCell>
				<TableCell>
					<Typography variant='subtitle2'>Column Type</Typography>
				</TableCell>
				</TableCell>
		</TableRow>
	</TableHead>
	const getColumnWarning = () => {
		if (!syncOperation.fields)
			return null;
		const expectedLength = syncOperation.fields.length
		const dataLength = data[0].length
		if (expectedLength == dataLength) {
			return null;
		}
		else if (expectedLength < dataLength) {
			const errorText = 'google sheet has more columns than the result table - Click Detect Types to accept new columns.' 
			return <FormControl error={errorText}>
			{errorText && <FormHelperText error={errorText}>{errorText}</FormHelperText>}
		</FormControl>
		}
		else {
			const errorText = 'google sheet has fewer columns than the result table - Click Detect Types to reinitialize based on current sheets data.'
			return <FormControl error={errorText}>
				{errorText && <FormHelperText error={errorText}>{errorText}</FormHelperText>}
			</FormControl>
		}
	}

	return <Paper className={classes.root} elevation={3} mt={1} mb={2} p={0} pb={2}>
		<Box my={1}>
			<Box p={1} flexDirection='column'>
				<Box justifyContent='center' display='flex' flex='1 1 auto' flexDirection='row'>
					<Typography variant='h6'>{dataName}</Typography>
					{/*}
					<IconButton style={{ marginLeft: 'auto', position:'relative', top:'-8px' }}>
						<CachedIcon />
					</IconButton>
*/}
				</Box>
				<Box display='flex' flexDirection='row'>
					{/*}
					<Box py={1} >
						<CheckdownType variant={'outlined'} label='Ignore empty rows' value={true} />
					</Box>
					*/}
				</Box>
				<Box py={1} display='flex' flexDirection='row' justifyContent='center'>
					<Button onClick={detectTypesCB} size={'small'} variant={'contained'} >Detect Types</Button>
				</Box>
				{getColumnWarning()}
			</Box>
		</Box>
		<Box className={classes.root} style={{overflowY:'scroll'}}>
		<Table size="small">
			{tableHeader}
			{getHeaders().map((h, index) => {
				if (index > data[0].length - 1){
					return null;
				}
				return <TableRow key={index} role={undefined} dense button onClick={null}>
				{/*}
				<TableCell padding="checkbox">
					<Checkbox
					color='default'
					size='small'
						checked={true}
					/>
				</TableCell>
			*/}
				<TableCell >
						<Typography variant='body2'>
							{getColumnSelect(h, index)}
					</Typography>
				</TableCell>
				<TableCell >
						{getTypeSelect(h,  index)}
				</TableCell>
				</TableRow>
			})}
			</Table>
		</Box>
	</Paper>
}

export const ConfigureData=(props) => {
	const classes = useStyles();
	const handleChange = (e) => {
	}
	const dataType = 'namedRange'
	const syncOperation = useSelector(state => state.syncOperations? state.syncOperations[1]: null)
	return <Box className={classes.root} margin={'normal'} mb={5}>
		<Box py={1}>
		{/*}
			<SelectType label='Range Type' value={dataType} values={
				[
					{
						key: 'namedRange', label: 'Named Range'
					},
					{
						key: 'sheet', label: 'Entire Sheet'
					}
				]
			} />
		*/}
			{dataType == 'namedRange' && <SelectRange />}
			{dataType == 'sheet' && <SelectSheet />}
		</Box>
		{syncOperation != null &&
			<DataType dataName={syncOperation.namedRange} />
		}
	</Box>
	  /*
	return <Box  boxShadow={2} py={5} p={3} mb={5}>
        <InputLabel id="demo-simple-select-label">Database Type</InputLabel>
		<Select p={5}
		labelId="demo-simple-select-label"
		id="demo-simple-select"
		value={'snowflake'}
		onChange={handleChange}
	>
		<MenuItem value={'snowflake'}>Snowflake</MenuItem>
		<MenuItem value={'redshift'}>Redshift</MenuItem>
		<MenuItem value={'bigquery'}>BigQuery</MenuItem>
	</Select>
	</Box>
	*/
}

export const ConfigureSnowflake = (props) => {
	const dispatch = useDispatch()

	const snowflakeAccount = useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakeAccount'], ''))
	const snowflakeUsername = useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakeUsername'], ''))
	const snowflakePassword = useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakePassword'], ''))
	const snowflakeDatabase = useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakeDatabase'], ''))
	const snowflakeSchema = useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakeSchema'], ''))
	const snowflakeWarehouse= useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakeWarehouse'], ''))
	const snowflakeRole= useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakeRole'], ''))
	const onTestConnectionCB = (e) => {
		console.log("testing connection")
		dispatch(confirmConnection())
	}
	const onEditTextCB = (key, value) => {
		dispatch(getChangeSyncOperationAction(1, key, value))
	}
	return <><Box p={2} pt={0}>
		<form noValidate autoComplete="off">
			<TextField onChange={(e) => onEditTextCB('snowflakeAccount', e.target.value)} label="Account" value={snowflakeAccount} />
			<TextField onChange={(e) => onEditTextCB('snowflakeUsername', e.target.value)}
			label="Username" value={snowflakeUsername} />
			<TextField onChange={(e) => onEditTextCB('snowflakePassword', e.target.value)}label="Password" type='password' value={snowflakePassword} />
			<TextField onChange={(e) => onEditTextCB('snowflakeDatabase', e.target.value)}label="Database (Optional)" value={snowflakeDatabase} />
			<TextField onChange={(e) => onEditTextCB('snowflakeSchema', e.target.value)}label="Schema (Optional)" value={snowflakeSchema} />
			<TextField onChange={(e) => onEditTextCB('snowflakeWarehouse', e.target.value)}label="Warehouse (Optional)" value={snowflakeWarehouse} />
			<TextField onChange={(e) => onEditTextCB('snowflakeRole', e.target.value)}label="Role (Optional)" value={snowflakeRole} />
		</form>
	</Box>
		<Button onClick={onTestConnectionCB} size='small' variant="contained">Test Connection</Button>
	</>
}

export const DBStatus = (props) => {
	const dispatch = useDispatch()
	const connectionResult  = useSelector(state => state.connectionResult)
	if (!connectionResult)
		return null;
	if (connectionResult && 'error' in connectionResult){
		return <Alert severity='error'>{connectionResult.errorMessage}</Alert>
	}
	return <Alert onClose={() => { dispatch(getNewConnectionResultAction(null)) }}>Successfully connected.</Alert>
}

export const ConfigureDB= (props) => {
	const classes = useStyles();
	return <Box className={classes.root} margin='normal' mb={5} >
		<Box py={1}>
			<Box flexWrap='wrap'>
				<Box py={1} display='flex'>
					<SelectType value={'snowflake'}
						values={[
							{ key: 'snowflake', label: 'Snowflake' }
						]}
					/>
				</Box>
			</Box>
		</Box>
		<ConfigureSnowflake />
		<DBStatus/>
	</Box>
}


export const StartSync = (props) => {
	const classes = useStyles();
	const dispatch = useDispatch()

	const onEditTextCB = (key, value) => {
		dispatch(getChangeSyncOperationAction(1, key, value))
	}
	const snowflakeTableName = useSelector(state => getValueSafe(state.syncOperations, [1, 'snowflakeTableName'], ''))
	return <Box className={classes.root} margin='normal' mb={5}>
		<Box flexWrap='wrap'>
			<Box py={1} display='flex'>
				<TextField fullWidth onChange={(e) => onEditTextCB('snowflakeTableName', e.target.value)} label="New Table Name" value={snowflakeTableName} />
			</Box>
		</Box>
	</Box>
}

export const MonitorSync= (props) => {
	const dispatch = useDispatch()
	const currentSyncResult = useSelector(state => state.currentSyncResult)
	const syncResult = useSelector(state => getValueSafe(state, ['syncResults', currentSyncResult], null))
	const timer = useRef(null)
	useEffect(() => {
		if (!syncResult)
			return;
		if (syncResult.status == 'completed' || syncResult.status == 'failed'){
			clearInterval(timer.current)
			return;
		}
		const updateSyncResult = () => {
			console.log("updating sync result")
			dispatch(updateSyncResultThunk())
		}
		timer.current = setInterval(updateSyncResult, 500);
		
		return () => {
			clearInterval(timer.current);
		};
	}, [timer,syncResult,dispatch]);
	

	const classes = useStyles();
	if (!currentSyncResult) {
		return null;
	}
	const getStatus = () => {
		if (syncResult.status== 'failed') {
			return <Alert severity='error'>Sync failed:{syncResult.errorMessage}</Alert>
		}
		else if (syncResult.status == 'completed') {
			return <Alert>Sync completed. {syncResult.rowsTotal} rows.</Alert>
		}
		return null;
	}
	return <Box display='flex' className={classes.root} margin='normal' p={3} mb={5}>
		<Box p={1}>
			<LinearProgress variant="determinate" value={100} color={syncResult.status == 'failed' ? 'secondary' : 'primary'} />
		</Box>
		{getStatus()}
	</Box>
}

export const store = Redux.createStore(myAppReducers, loadState() , myMiddleware )
store.subscribe(throttle(() => {
	const currentState = Object.assign({},store.getState())
	delete currentState['namedRanges']
	saveState(
		currentState);
}, 1000));
export default function Home() {
	const classes = useStyles();
	const dispatch = useDispatch()
	const activeStep = useSelector(state => state.uiState.activeStep)
	const steps = getSteps();
	const setActiveStep = (step) => {
		dispatch(getChangeUIStateAction('activeStep', step))
	}

	const startSyncCB = () => {
		console.log("starting sync")
		dispatch(startSync())
	}

	const handleNext = () => {
		if (activeStep == steps.length - 1) {
			//start sync
			startSyncCB()
			setActiveStep(activeStep + 1);
		}
		setActiveStep(activeStep + 1);
	};

	const handleBack = () => {
		setActiveStep(activeStep - 1);
	};

	const handleReset = () => {
		setActiveStep(0);
	};
	const handleStepClick = (step) => {
		setActiveStep(step)
	}
	const getStepContent = (step) =>{
		switch (step) {
			case 0:
				return <><div className={classes.root} p={2}>Configure the data you wish to sync.</div><ConfigureData /></>
			case 1:
				return <><div className={classes.root} p={2}>Configure the database to sync to.</div><ConfigureDB /></>
			case 2:
				return <><div className={classes.root} p={2}>Configure sync</div><StartSync/></>
			default:
				return 'Unknown step';
		}
	}
  return (<>
		  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
		  <div style={{height:'100%'}} className={classes.root}>
			  {/*}
			  <FullWidthTabs />
  */}
			  <div style={{height:'100%'}} className={classes.root}>
				  <Stepper nonLinear alternativeLabel activeStep={activeStep}>
					  {steps.map((label, index) => {
						  const stepProps = { p: 0, onClick: step => setActiveStep(index) };
						  const labelProps = {};
						  return (
							  <Step key={label} {...stepProps}>
								  <StepLabel {...labelProps}>{label}</StepLabel>
							  </Step>
						  );
					  })}
				  </Stepper>
				  <Box className={classes.root} p={1} >
					  {activeStep === steps.length ? (
					  <div>
						  <MonitorSync />
						  <Button onClick={handleReset} className={classes.button}>
							  Reset
            </Button>
					  </div>
					  ) : (
							  <div className={classes.root}>
								  <Typography className={classes.instructions} />{getStepContent(activeStep)}
								  <div>
									  <Button disabled={activeStep === 0} onClick={handleBack} className={classes.button}>
										  Back
              </Button>
									  <Button
										  variant="contained"
										  color="primary"
										  onClick={handleNext}
										  className={classes.button}
									  >
										  {activeStep === steps.length - 1 ? 'Start Sync' : 'Next'}
									  </Button>
								  </div>
							  </div>
						  )}
				  </Box>
			  </div>
		  </div>
		  </>
  )
}
