// Copyright (C) The Arvados Authors. All rights reserved.
//
// SPDX-License-Identifier: AGPL-3.0

import React from "react";
import { compose } from "redux";
import { CustomStyleRulesCallback } from 'common/custom-theme';
import { IconButton, Paper, Tooltip, InputAdornment, Input } from "@mui/material";
import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import SearchIcon from "@mui/icons-material/Search";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import { ArvadosTheme } from "common/custom-theme";
import { SearchView } from "store/search-bar/search-bar-reducer";
import { SearchBarBasicView, SearchBarBasicViewDataProps, SearchBarBasicViewActionProps } from "views-components/search-bar/search-bar-basic-view";
import {
    SearchBarAutocompleteView,
    SearchBarAutocompleteViewDataProps,
    SearchBarAutocompleteViewActionProps,
} from "views-components/search-bar/search-bar-autocomplete-view";
import {
    SearchBarAdvancedView,
    SearchBarAdvancedViewDataProps,
    SearchBarAdvancedViewActionProps,
} from "views-components/search-bar/search-bar-advanced-view";
import { KEY_CODE_DOWN, KEY_CODE_ESC, KEY_CODE_UP, KEY_ENTER } from "common/codes";
import { debounce } from "debounce";
import { Vocabulary } from "models/vocabulary";
import { connectVocabulary } from "../resource-properties-form/property-field-common";
import { Session } from "models/session";

type CssRules = "container" | "containerSearchViewOpened" | "input" | "view";

const styles: CustomStyleRulesCallback<CssRules> = (theme: ArvadosTheme) => {
    return {
        container: {
            position: "relative",
            width: "100%",
            borderRadius: theme.spacing(0.5),
            zIndex: theme.zIndex.modal,
        },
        containerSearchViewOpened: {
            position: "relative",
            width: "100%",
            borderRadius: `${theme.spacing(0.5)} ${theme.spacing(0.5)} 0 0`,
            zIndex: theme.zIndex.modal,
        },
        input: {
            border: "none",
            padding: `0`,
        },
        view: {
            position: "absolute",
            width: "100%",
            zIndex: 1,
        },
    };
};

export type SearchBarDataProps = SearchBarViewDataProps &
    SearchBarAutocompleteViewDataProps &
    SearchBarAdvancedViewDataProps &
    SearchBarBasicViewDataProps;

interface SearchBarViewDataProps {
    searchValue: string;
    currentView: string;
    isPopoverOpen: boolean;
    debounce?: number;
    vocabulary?: Vocabulary;
    sessions: Session[];
}

export type SearchBarActionProps = SearchBarViewActionProps &
    SearchBarAutocompleteViewActionProps &
    SearchBarAdvancedViewActionProps &
    SearchBarBasicViewActionProps;

interface SearchBarViewActionProps {
    onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
    onSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
    onSetView: (currentView: string) => void;
    closeView: () => void;
    openSearchView: () => void;
    loadRecentQueries: () => string[];
    moveUp: () => void;
    moveDown: () => void;
    setAdvancedDataFromSearchValue: (search: string, vocabulary?: Vocabulary) => void;
    searchSingleCluster: (session: Session, searchValue: string) => any;
}

type SearchBarViewProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;

const handleKeyDown = (e: React.KeyboardEvent, props: SearchBarViewProps) => {
    if (e.keyCode === KEY_CODE_DOWN) {
        e.preventDefault();
        if (!props.isPopoverOpen) {
            props.onSetView(SearchView.AUTOCOMPLETE);
            props.openSearchView();
        } else {
            props.moveDown();
        }
    } else if (e.keyCode === KEY_CODE_UP) {
        e.preventDefault();
        props.moveUp();
    } else if (e.keyCode === KEY_CODE_ESC) {
        e.preventDefault();
        props.closeView();
    } else if (e.keyCode === KEY_ENTER) {
        if (props.currentView === SearchView.BASIC) {
            e.preventDefault();
            props.onSearch(props.selectedItem.query);
        } else if (props.currentView === SearchView.AUTOCOMPLETE) {
            if (props.selectedItem.id !== props.searchValue) {
                e.preventDefault();
                props.navigateTo(props.selectedItem.id);
            }
        }
    }
};

const handleInputClick = (e: React.MouseEvent, props: SearchBarViewProps) => {
    if (props.searchValue) {
        props.onSetView(SearchView.AUTOCOMPLETE);
    } else {
        props.onSetView(SearchView.BASIC);
    }
    props.openSearchView();
};

const handleDropdownClick = (e: React.MouseEvent, props: SearchBarViewProps) => {
    e.stopPropagation();
    if (props.isPopoverOpen && props.currentView === SearchView.ADVANCED) {
        props.closeView();
    } else {
        props.setAdvancedDataFromSearchValue(props.searchValue, props.vocabulary);
        props.onSetView(SearchView.ADVANCED);
    }
};

export const SearchBarView = compose(
    connectVocabulary,
    withStyles(styles)
)(
    class extends React.Component<SearchBarViewProps> {
        state={
            loggedInSessions: [],
        }

        debouncedSearch = debounce(() => {
            this.props.onSearch(this.props.searchValue);
        }, 1000);

        handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
            this.debouncedSearch();
            this.props.onChange(event);
        };

        handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
            this.debouncedSearch.clear();
            this.props.onSubmit(event);
        };

        componentDidMount(): void {
            this.setState({ loggedInSessions: this.props.sessions.filter((ss) => ss.loggedIn && ss.userIsActive)});
        }

        componentDidUpdate( prevProps: Readonly<SearchBarViewProps>, prevState: Readonly<{loggedInSessions: Session[]}>, snapshot?: any ): void {
            if (prevProps.sessions !== this.props.sessions) {
                this.setState({ loggedInSessions: this.props.sessions.filter((ss) => ss.loggedIn)});
            }
            //if a new session is logged in after a search is started, search the new cluster and append those to the results
            if(prevState.loggedInSessions.length !== this.state.loggedInSessions.length){
                const newLogin = this.state.loggedInSessions.filter((ss) => !prevState.loggedInSessions.includes(ss));
                this.props.searchSingleCluster(newLogin[0], this.props.searchValue);
            }
        }

        componentWillUnmount() {
            this.debouncedSearch.clear();
        }

        render() {
            const { children, ...props } = this.props;
            const { classes, isPopoverOpen } = this.props;
            return <>
                {isPopoverOpen && <Backdrop onClick={props.closeView} />}

                <Paper className={isPopoverOpen ? classes.containerSearchViewOpened : classes.container}>
                    <form
                        data-cy="searchbar-parent-form"
                        onSubmit={this.handleSubmit}>
                        <Input
                            data-cy="searchbar-input-field"
                            className={classes.input}
                            onChange={this.handleChange}
                            placeholder="Search"
                            value={props.searchValue}
                            fullWidth={true}
                            disableUnderline={true}
                            onClick={e => handleInputClick(e, props)}
                            onKeyDown={e => handleKeyDown(e, props)}
                            startAdornment={
                                <InputAdornment position="start">
                                    <Tooltip title="Search">
                                        <IconButton type="submit" size="large">
                                            <SearchIcon />
                                        </IconButton>
                                    </Tooltip>
                                </InputAdornment>
                            }
                            endAdornment={
                                <InputAdornment position="end">
                                    <Tooltip title="Advanced search">
                                        <IconButton onClick={e => handleDropdownClick(e, props)} size="large">
                                            <ArrowDropDownIcon />
                                        </IconButton>
                                    </Tooltip>
                                </InputAdornment>
                            }
                        />
                    </form>
                    <div className={classes.view}>{isPopoverOpen && getView({ ...props })}</div>
                </Paper>
            </>;
        }
    }
);

const getView = (props: SearchBarViewProps) => {
    switch (props.currentView) {
        case SearchView.AUTOCOMPLETE:
            return (
                <SearchBarAutocompleteView
                    navigateTo={props.navigateTo}
                    searchResults={props.searchResults}
                    searchValue={props.searchValue}
                    selectedItem={props.selectedItem}
                />
            );
        case SearchView.ADVANCED:
            return (
                <SearchBarAdvancedView
                    closeAdvanceView={props.closeAdvanceView}
                    tags={props.tags}
                    saveQuery={props.saveQuery}
                />
            );
        default:
            return (
                <SearchBarBasicView
                    onSetView={props.onSetView}
                    onSearch={props.onSearch}
                    loadRecentQueries={props.loadRecentQueries}
                    savedQueries={props.savedQueries}
                    deleteSavedQuery={props.deleteSavedQuery}
                    editSavedQuery={props.editSavedQuery}
                    selectedItem={props.selectedItem}
                />
            );
    }
};

const backdropStyles: CustomStyleRulesCallback<"backdrop"> = theme => ({
    backdrop: {
        position: "fixed",
        top: 0,
        right: 0,
        bottom: 0,
        left: 0,
        zIndex: theme.zIndex.modal,
    },
});

const Backdrop = compose(withStyles(backdropStyles))(({ classes, ...props }: WithStyles<"backdrop"> & React.HTMLProps<HTMLDivElement>) => (
    <div
        className={classes.backdrop}
        {...props}
    />
));
