import { graphql, Link, useStaticQuery } from 'gatsby'
import { ChangeEvent, useState } from 'react'
import { ExtendedTlObject, GraphqlAllResponse } from '../types'
import SearchIcon from '@material-ui/icons/Search'
import {
    Avatar,
    createStyles,
    Fade,
    fade,
    InputBase,
    List,
    ListItem,
    ListItemAvatar,
    ListItemText,
    makeStyles,
    Paper,
    Popper,
    Theme,
    Button,
    ClickAwayListener,
} from '@material-ui/core'
import React from 'react'
import { useLocalState } from '../hooks/use-local-state'
import Fuse from 'fuse.js'

import blue from '@material-ui/core/colors/blue'
import red from '@material-ui/core/colors/red'
import yellow from '@material-ui/core/colors/yellow'

import UnionIcon from '@material-ui/icons/AccountTree'
import ClassIcon from '@material-ui/icons/Class'
import FunctionsIcon from '@material-ui/icons/Functions'
import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline'
import { useFuse } from '../hooks/use-fuse'
import { FuseHighlight } from './fuse-highlight'
import { ActionBarSearchField } from './actionbar-search-field'

const useStyles = makeStyles((theme: Theme) =>
    createStyles({
        popup: {
            display: 'flex',
            height: 250,
            overflowY: 'auto',
            overflowX: 'hidden',
        },
        popupEmpty: {
            padding: theme.spacing(2),
            flex: '1 1 auto',
            alignItems: 'center',
            justifyContent: 'center',
            display: 'flex',
            flexDirection: 'column',
        },
        popupEmptyIcon: {
            fontSize: '48px',
            display: 'block',
            marginBottom: theme.spacing(2),
        },
        popupList: {
            width: '100%',
        },
        popupListItem: {
            padding: theme.spacing(0, 2),
        },
        searchItemMatch: {
            color: theme.palette.type === 'dark' ? blue[300] : blue[700],
        },
    })
)

export function GlobalSearchField({ isMobile }: { isMobile: boolean }): React.ReactElement {
    const classes = useStyles()
    const allObjects: {
        allTlObject: GraphqlAllResponse<ExtendedTlObject>
    } = useStaticQuery(graphql`
        query {
            allTlObject {
                edges {
                    node {
                        id
                        prefix
                        type
                        name
                    }
                }
            }
        }
    `)

    const [includeMtproto, setIncludeMtproto] = useLocalState('mtproto', false)
    const { hits, query, onSearch } = useFuse(
        allObjects.allTlObject.edges,
        {
            keys: ['node.name'],
            includeMatches: true,
            threshold: 0.3,
        },
        { limit: 25 },
        includeMtproto ? undefined : (it) => it.node.prefix !== 'mtproto/'
    )

    const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null)
    const [open, setOpen] = useState(false)

    const notFound = () => (
        <>
            <ErrorOutlineIcon className={classes.popupEmptyIcon} />
            Nothing found
            {!includeMtproto && (
                <Button
                    variant="text"
                    size="small"
                    style={{
                        margin: '4px auto',
                    }}
                    onClick={() => {
                        setIncludeMtproto(true)
                    }}
                >
                    Retry including MTProto objects
                </Button>
            )}
        </>
    )

    const emptyField = () => (
        <>
            <SearchIcon className={classes.popupEmptyIcon} />
            Start typing...
        </>
    )

    const renderSearchItem = (
        node: ExtendedTlObject,
        matches: ReadonlyArray<Fuse.FuseResultMatch>
    ) => (
        <ListItem
            button
            divider
            component={Link}
            to={`/${node.prefix}${node.type}/${node.name}`}
            className={classes.popupListItem}
            onClick={() => setOpen(false)}
            key={node.id}
        >
            <ListItemAvatar>
                <Avatar
                    style={{
                        backgroundColor:
                            node.type === 'class'
                                ? blue[600]
                                : node.type === 'method'
                                ? red[600]
                                : yellow[700],
                    }}
                >
                    {node.type === 'class' ? (
                        <ClassIcon />
                    ) : node.type === 'method' ? (
                        <FunctionsIcon />
                    ) : (
                        <UnionIcon />
                    )}
                </Avatar>
            </ListItemAvatar>
            <ListItemText
                primary={
                    <>
                        {node.prefix}
                        <FuseHighlight
                            matches={matches}
                            value={node.name}
                            className={classes.searchItemMatch}
                        />
                    </>
                }
                secondary={node.type}
            />
        </ListItem>
    )

    const popupContent = (
        <Paper className={classes.popup}>
            {query.length <= 1 || !hits.length ? (
                <div className={classes.popupEmpty}>
                    {query.length <= 1 ? emptyField() : notFound()}
                </div>
            ) : (
                <List disablePadding dense className={classes.popupList}>
                    {hits.map(({ item: { node }, matches }) =>
                        renderSearchItem(node, matches!)
                    )}
                    <div style={{ textAlign: 'center' }}>
                        <Button
                            variant="text"
                            size="small"
                            style={{
                                margin: '4px auto',
                            }}
                            onClick={() => {
                                setIncludeMtproto(!includeMtproto)
                            }}
                        >
                            {includeMtproto ? 'Hide' : 'Include'} MTProto
                            objects
                        </Button>
                    </div>
                </List>
            )}
        </Paper>
    )

    return (
        <ClickAwayListener onClickAway={() => setOpen(false)}>
            <>
                <ActionBarSearchField
                    inputRef={setAnchorEl}
                    autoComplete="off"
                    onFocus={() => setOpen(true)}
                    onBlur={() => setOpen(false)}
                    onChange={onSearch}
                />
                <Popper
                    open={open}
                    anchorEl={anchorEl}
                    placement="bottom"
                    transition
                    style={{
                        width: isMobile ? '100%' : anchorEl?.clientWidth,
                        zIndex: 9999,
                    }}
                >
                    {({ TransitionProps }) => (
                        <Fade {...TransitionProps} timeout={350}>
                            {popupContent}
                        </Fade>
                    )}
                </Popper>
            </>
        </ClickAwayListener>
    )
}