import React, {useMemo, useCallback, useState, useEffect, useRef, useLayoutEffect} from 'react';
import {batch, shallowEqual,connect, useSelector, useDispatch} from 'react-redux'
import * as TaskHeader from './TaskHeader'
import * as Immer from 'immer'
import * as Drag from './Drag'
import * as Common from './common'
import * as StepMenu from './StepMenu'
import * as Materialization from './Materialization'
import * as Step from './Step'

import * as Shared from 'placeholder-shared'
import * as Antd from 'antd'
import * as Data from './Data'
import { MenuOutlined,FilterTwoTone,FilterOutlined,DownOutlined,CodeOutlined,PartitionOutlined,CloseOutlined,EyeOutlined, AimOutlined, FilterFilled} from '@ant-design/icons';

export const ModelDocs = (props: {
    stepCounter: number
}) => {
    const modelViewerStepCounter = props.stepCounter
    const stepData = useSelector ((state)=> {
        return Shared.Common.getValueSafe(state, ['steps', modelViewerStepCounter], null)
    })
    const runResult = useSelector ((state)=> {
        return Shared.Common.getValueSafe(state, ['runResults', modelViewerStepCounter], null)
    })
    const tests = ['No Nulls', 'Unique', 'Referential']
    const [checked, setChecked] = useState({})
    const onChange = (test, field, newValue) => {
        setChecked((checked) => {
            return Immer.produce(checked, (draftState) => {
                Shared.Common.setValue(draftState, [field, test], newValue)
            })
        })
    }
    const onDescriptionChange = (field, description)=>{
        setChecked((checked) => {
            return Immer.produce(checked, (draftState) => {
                Shared.Common.setValue(draftState, [field, 'description'], description)
            })
        })
    }
    if (!stepData ||! runResult) {
        return null;
    }
    const getFieldCards = () => {
        if (!runResult.previewResult) {
            return null;
        }
        else {
            return runResult.previewResult.fields.map((f)=> {
                return <Antd.Card key={f} className='' style={{ width: '100%' }} type="inner" title={
                    <Antd.Typography.Text code>{f.name}</Antd.Typography.Text>} extra={
                        <EyeOutlined/>
                    }>
                    <Common.DivWithClassName className='rowFlexBox'>
                        <Antd.Typography.Paragraph className='flexGrow' editable={{ onChange: (desc: string) => onDescriptionChange(f.name, desc) }}>{
                            Shared.Common.getValueSafe(checked, [f.name, 'description'], '')
                        }
                        </Antd.Typography.Paragraph>
                        <Common.DivWithClassName className='floatRight flexStay'>
                            {tests.map(tag => (
                                <Antd.Tag.CheckableTag
                                    key={tag}
                                    checked={Shared.Common.getValueSafe(checked, [f.name, tag], false)}
                                    onChange={(checked) => onChange(tag, f.name, checked)}
                                >
                                    {tag}
                                </Antd.Tag.CheckableTag>
                            ))}
                        </Common.DivWithClassName>
                    </Common.DivWithClassName>
                </Antd.Card>
            })
        }
    }
    return <Common.DivWithClassName className='flexGrow columnFlexBox'>
        <Antd.Card className='columnFlexBox flexGrow ModelViewerCard' style={{}}>
            <Common.DivWithClassName className='positionRelative flexGrow columnFlexBox'>
                <Common.DivWithClassName className='positionAbsolute width100Percent'>
                    <Antd.Space direction='vertical' style={{ width: '100%' }}>
                        {getFieldCards()}
                    </Antd.Space>
                </Common.DivWithClassName>
            </Common.DivWithClassName>
        </Antd.Card>
    </Common.DivWithClassName>
}
export const ModelMenu = (props: { filter:string[], stepCounter: number }
) => {

    const modelViewerStepCounter = props.stepCounter
    return <Common.DivWithClassName className='height100Percent flexStay minHeightZero positionRelative'>
            <Data.StateDataView filter={props.filter} width={'100%'} height={'100%'} className='positionAbsolute flexGrow' stepCounter={modelViewerStepCounter} />
    </Common.DivWithClassName>
} 

export const Title = (props: { stepCounter: number }) => {
    const { stepCounter } = props;
    const dispatch = useDispatch();
    const steps = useSelector(state => (state as any).steps)
    const onClick = (e) => {
        dispatch(Shared.SqlTabActions.addSqlResultThunkAndRunPreview(stepCounter))
    }
    return <Step.StepContainer stepCounter={stepCounter} className='flexGrow rowFlexBox width100Percent'>
        <TitleShared onClick={onClick} name={Shared.Runner.getName(steps[stepCounter])} clickText={'Preview'} />
    </Step.StepContainer>
}

export const TitleShared = (props: { onClick: Function, name: string, clickText: string }) => {
    const {name,onClick, clickText} = props;
    return <Common.DivWithClassName className="resultContainer rowFlexBox justifyContentSpaceBetween alignItemsCenter flexGrow">
        <Common.DivWithClassName onClick={(e) => {
            //dispatch(Shared.State.changeUIState('previewStepIndex', stepData.stepCounter))
        }}>{name}</Common.DivWithClassName>
        <Common.DivWithClassName className="viewThisResult rowFlexBox alignItemFlexStart floatRight flexStay">
            <span className="buttonBoxShadow floatRight flexStay" onClick={(e) => {
                onClick(e)
            }}><AimOutlined style={{ fontSize: '10px', color: '#08c' }} />{clickText}</span>
        </Common.DivWithClassName>
    </Common.DivWithClassName>
}

export const Columns = (props:{stepCounter:number}) => {
    const {stepCounter} = props;
    const runResults = useSelector(state => (state as any).runResults)
    const previewResults = Shared.Common.getValueSafe(runResults[stepCounter], ['previewResult'], null);
    const [result, columns]= Data.getData([], previewResults);
    return <Common.DivWithClassName className='columnFlexBox ColumnsContainer'>
        {(columns as any).map((c) => {

            return <Common.DivWithClassName key={c} className='columnName'><Antd.Typography.Text className='' code key={c}>{c}
            </Antd.Typography.Text>
            </Common.DivWithClassName>
        })}
    </Common.DivWithClassName>
}

export const Tree = (props: {showDependencies:boolean,stepCounter?:number, selectedKeys: any,expandedKeys:any, onSelect:any, onExpand:any}) =>{
    const {stepCounter, selectedKeys, expandedKeys,onExpand, onSelect,showDependencies} = props;
    const dispatch = useDispatch();

    const gFinal = useSelector(state => (state as any).edges.gFinal)
    const steps = useSelector(state => (state as any).steps)
    const treeData = useSelector(state => (state as any).treeData[1])
    const materializations = useSelector(state => (state as any).materializations)

    const lineageGroups = useSelector(state => (state as any).lineageGroups)

    const dependencies = useMemo(()=> {
        return gFinal && stepCounter ? gFinal.predecessors(stepCounter).map((s) => {
            if(!gFinal){
                console.error("gFinal wasn't calculated -> go look in task.tsx ")
            } else {
                return {
                    title: <Common.DivWithClassName className='columnFlexBox'>
                        <Title stepCounter={parseInt(s)} />
                        <Columns stepCounter={parseInt(s)}/>
                    </Common.DivWithClassName>
                    ,
                    key: `dependency-${parseInt(s)}`
                }
            }
            
        }) : []
    },[gFinal, stepCounter])
    const [models, sources] = useMemo(() => {
        let models = []
        let sources = []
        const stepCB = (stepCounter, stepData) => {
            switch (stepData.operation.type) {
                case Shared.Runner.StepTypes.sourceInput:
                    sources.push({
                        title: <Title stepCounter={parseInt(stepCounter)} />,
                        key: `step-${parseInt(stepCounter)}-sources`
                    })
                    break;
            }
        }
        Shared.TreeData.forAllStepsInTask(treeData, steps, stepCB)
        return [models,sources]
    }, [treeData, steps])

    const materializationItems = useMemo(() => {
        const materializationItems =Object.keys(materializations).map((mCounter) => {
            const onClickMaterialization = () => {
                dispatch(Materialization.ShowMaterializationThunk(mCounter))
            }
            return {
                title: <TitleShared name={materializations[mCounter].name} onClick={() => {
                    onClickMaterialization()
                }}
                    clickText={'Edit'} />,
                key: mCounter
            }
        })
        return materializationItems
    },[materializations,dispatch])
    const treeDataNodes = useMemo(() => {
        let treeDataNodes =[]
        if (showDependencies) {
            treeDataNodes.push({
                title:'Dependencies', 
                key:'Dependencies',
                children:dependencies
            })
        }
        else{
            if (lineageGroups) {
                for (let l in lineageGroups) {
                    let lineageGroupSteps = lineageGroups[l].steps
                    treeDataNodes.push({
                        title: <span className='fontBold'>{l}</span>,
                        key: `lineage-${l}`,
                        children:
                            [...
                                !lineageGroupSteps ? [] : Object.keys(lineageGroupSteps).map((s) => {
                                    const stepData = steps[parseInt(s)]
                                    const name = Shared.Runner.getName(stepData);
                                    return {
                                        title: <Title stepCounter={parseInt(s)} />, key: `step-${s}-${l}`, isLeaf: true
                                    }
                                })
                            ]
                    })
                }
            }
            const leftOver = []
            Shared.TreeData.forAllStepsInTask(treeData, steps, (stepCounter, stepData) => {
                if (!stepData.operation.lineageGroups || !Object.keys(stepData.operation.lineageGroups).length) {
                    leftOver.push({
                        title: <Title stepCounter={stepCounter} />,
                        key: `step-${stepCounter}`, isLeaf: true
                    })
                }
            })
            treeDataNodes.push({
                title: <span className='fontBold'>Models</span>,
                key: 'Unassigned Models',
                children: leftOver
            })

            treeDataNodes.push({
                title: <span className='fontBold'>Sources</span>,
                key: 'Sources',
                children: sources
            })
            treeDataNodes.push({
                title: <span className='fontBold'>Materialization Templates</span>,
                key: 'Materialization Templates',
                children: materializationItems
            })
        }

        return treeDataNodes
    }, [lineageGroups, treeData, showDependencies, dependencies, steps, sources, materializationItems])

    return     <Antd.Tree.DirectoryTree
    expandAction={false}
    /*onExpand={onExpand}
    expandedKeys={expandedKeys}*/
    blockNode={true}
    icon={null}
    showIcon={false}
    className='height100Percent '
    multiple={true}
    switcherIcon={null}
    selectedKeys={
        selectedKeys
    }
    defaultExpandAll={showDependencies?true:false}
    onSelect={onSelect}
    treeData={treeDataNodes} />
}
export const ModelViewerTree = (props: {
    stepCounter: number
}) => {
    const {stepCounter}= props;
    const dispatch = useDispatch()
    const steps = useSelector(state => (state as any).steps)
    const treeData = useSelector(state => (state as any).treeData[1])
    const modelViewerSteps = useSelector(state => {
        return Shared.Common.getValueSafe(state, ['modelViewerState', stepCounter, 'selectedKeys'], [])
    })
    //refactored out so onSelect can be added as a prop
    const onSelect = useCallback((selectedKeys: any[], info: any) => {
        const parents = ['Dependencies', 'Models', 'Sources']
        if (parents.includes(info.node.key)) {
            return;
        }
        if (modelViewerSteps.includes(info.node.key)){
            selectedKeys = selectedKeys.filter(k => k != info.node.key)
        }
        dispatch(Shared.State.changeModelViewerState(stepCounter, 'selectedKeys', selectedKeys.filter(k => !parents.includes(k))))
    }, [dispatch,stepCounter,modelViewerSteps])

    const currentStep = useSelector(state => (state as any).currentStep)
  

    const onExpand = useCallback((expandedKeys) => {
        const expKeys = expandedKeys.filter(s => ['Dependencies', 'Models', 'Sources'].includes(s)) 
        console.log("on expand" ,stepCounter,expandedKeys, expKeys)
        dispatch(Shared.State.changeModelViewerState(stepCounter,'expandedKeys', expKeys))
    },[dispatch,stepCounter])
    const expandedKeys = useSelector(state => Shared.Common.getValueSafe((state as any), ['modelViewerState', stepCounter,'expandedKeys'], []))
    return(
        <Common.DivWithClassName className={Common.classNames({
            flexGrow: true,
            ModelViewerTree:true,
            height100Percent:true
        })}>
            <Tree 
            showDependencies={true}
            stepCounter={currentStep}
            selectedKeys={modelViewerSteps}
            expandedKeys={expandedKeys}
            onSelect={onSelect}
            onExpand={onExpand}
               />
        </Common.DivWithClassName>)
    }

export const ModelViewerParent = (props:{stepCounter:number} ) => {
    const stepCounter= props.stepCounter

    const viewMode = 'allModels';
    const modelViewerCurrentSteps = useSelector(state => Shared.Common.getValueSafe(state,['modelViewerState',stepCounter,'selectedKeys'], [])) 
    const modelViewerCurrentStepsToDisplay = useSelector((state: any)=>{
        /*
        if (viewMode == 'current' || viewMode == 'dependencies'
        ) {
            if (!state.edges.gFinal || !state.currentStep) {
                return []
            }
            if (viewMode == 'current') {
                return [state.currentStep];
            }
            else {
                return state.edges.gFinal.predecessors(state.currentStep)
            }
        }
        else if (viewMode == 'allModels') {
            */
            return modelViewerCurrentSteps
            /*
        }
        */
    })

    const getMenu = () => {
        return null;
        return <Antd.Space direction='horizontal'>
            {/*}
            <Antd.Typography.Text strong>Viewing:</Antd.Typography.Text>
            <Antd.Dropdown overlay={
                <Antd.Menu onClick={onSelect}>
                    <Antd.Menu.Item key='dependencies'>
                            Dependencies
                    </Antd.Menu.Item>
                    <Antd.Menu.Item key='current'>
                        Current Model
                    </Antd.Menu.Item>
                </Antd.Menu>
            }>
                <a className='ant-dropdown-link rowFlexBox alignItemsCenter'>
                    {viewMode == 'dependencies' ? 'Dependencies' : 'Current Model'}
                     <DownOutlined style={{ fontSize: '10px' }} />
                </a>
            </Antd.Dropdown>
            <Antd.Typography.Text strong>Viewing:</Antd.Typography.Text>
            <Antd.Radio.Group size='small' value={viewMode} onChange={(e) => {
                dispatch(Shared.State.changeUIState('viewMode', e.target.value))
            }}>
                <Antd.Radio.Button value='dependencies'><Antd.Space><PartitionOutlined rotate={180} />Dependencies</Antd.Space></Antd.Radio.Button>
                {/*}
                <Antd.Radio.Button value='current'><Antd.Space><CodeOutlined />Current Model</Antd.Space></Antd.Radio.Button>
                <Antd.Radio.Button value='allModels'><Antd.Space><CodeOutlined />All Models</Antd.Space></Antd.Radio.Button>
            </Antd.Radio.Group>
        */}
        </Antd.Space>
    }
    const getWarning = () => {
        let text = 'No model selected' 
        return <Common.DivWithClassName className='columnFlexBox justifyContentCenter flexGrow height100Percent width100Percent'> <Antd.Empty description={text}
        />
        </Common.DivWithClassName>
    }

    const eachHeight = modelViewerCurrentStepsToDisplay.length ?  100 / modelViewerCurrentStepsToDisplay.length : 0;
    return <Common.DivWithClassName className='backgroundLightGrey padding5px columnFlexBox flexGrow height100Percent '>
            {/*}
        <Common.DivWithClassName className='PaletteContainer width100Percent rowFlexBox'>
            <Common.DivWithClassName className=''>
                {getMenu()}
            </Common.DivWithClassName>
        </Common.DivWithClassName>
*/}
        <Common.DivWithClassName className='rowFlexBox flexGrow'>
            <Antd.Layout>
                <Antd.Layout.Sider defaultCollapsed={true} width={225} collapsible={true} collapsedWidth={0} theme='light' trigger={<MenuOutlined />}
                >
                    <Common.DivWithClassName className='height100Percent flexStay width225px overflowAuto backgroundWhite'>
                        <Common.DivWithClassName className='positionRelative height100Percent'>
                            <Common.DivWithClassName className='positionAbsolute height100Percent width100Percent'>
                                <ModelViewerTree stepCounter={stepCounter} />
                            </Common.DivWithClassName>
                        </Common.DivWithClassName>
                    </Common.DivWithClassName>
                </Antd.Layout.Sider>
                <Antd.Layout.Content className='rowFlexBox flexGrow'>
                <Common.DivWithClassName className='flexGrow columnFlexBox'>
                    {modelViewerCurrentStepsToDisplay.length == 0 && getWarning()}
                    {modelViewerCurrentStepsToDisplay.map((stepCounter) => {
                        return <Common.DivWithClassName
                            className='flexGrow columnFlexBox minHeightZero'
                            key={stepCounter} style={{
                                height: `${eachHeight}%`
                            }}><ModelView key={stepCounter} stepCounter={stepCounter} />
                        </Common.DivWithClassName>
                    })}
                </Common.DivWithClassName>
                </Antd.Layout.Content>
            </Antd.Layout>
        </Common.DivWithClassName>
    </Common.DivWithClassName>

}

const ModelView = (props:{stepCounter:any}) => {
    const {stepCounter} = props;

    const [filter, setFilter] = useState(null)
    const stepCounterToUse = stepCounter.startsWith && stepCounter.startsWith('dependency') ? stepCounter.split('-')[1] : stepCounter
    const [activeTabKey , setActiveTabKey] = useState('Data')
    const dispatch = useDispatch()
    const stepData = useSelector(state => (state as any).steps[stepCounterToUse])
    
    const stepName = Shared.Runner.getName(stepData)
    const modelViewerCurrentSteps = useSelector(state=> (state as any).modelViewerCurrentSteps)
    const getContent = (key:string) => {
        const content =  {
            'Data':() => {
                return <ModelMenu filter={filter} stepCounter={stepCounterToUse} />
            },
            'Docs': () => {
            return <ModelDocs stepCounter={stepCounterToUse}/>
            },
            'Discussion':() =>{
                return null;
            }
        }
        return <Common.DivWithClassName className='flexGrow columnFlexBox minHeightZero'>
            {content[key]()}
        </Common.DivWithClassName>
    }
    const tabList = [{
        key: 'Data', tab: 'Data'
    },
    {
        key: 'Docs', tab: 'Docs'
    },
    {
        key: 'Discussion', tab: 'Discussion'
    }]
    const onClose = useCallback(() => {
        const newModelViewerCurrentSteps = [...modelViewerCurrentSteps].filter(s => s != stepCounter)
        dispatch(Shared.State.setModelViewerCurrentStep(newModelViewerCurrentSteps))
    },[dispatch, modelViewerCurrentSteps,stepCounter])
    const onClickFilter = useCallback((columnName) => {
        setFilter((filter) => {
            return Immer.produce(filter, (draftState) => {
                if (columnName in filter){
                    delete draftState[columnName]
                }
                else{
                    draftState[columnName] = true;
                }
            })
        })
    },[setFilter])
    return <Antd.Card 
        tabBarExtraContent={<Antd.Button 
            onClick={(e) => {
                if (!filter) {
                    setFilter({})
                }
                else{
                    setFilter(null)
                }
            }}
        size='small'>{filter && <FilterTwoTone/>}
    {filter == null && <FilterOutlined />}</Antd.Button>}
    extra={<Antd.Button onClick={onClose} size='small'><CloseOutlined />
    </Antd.Button>
    } tabProps={{ size: 'small'}} tabList={tabList} activeTabKey={activeTabKey} onTabChange={setActiveTabKey} title={stepName} className='columnFlexBox flexGrow ModelViewerTabs minHeightZero'>
        {filter != null && <Data.ColumnSelector 
        onClickFilter={onClickFilter}
        stepCounter={stepCounterToUse} filter={filter!}/>}
        {getContent(activeTabKey)}
    </Antd.Card>
}

export const ModelViewer = ModelViewerParent