import React, {Component} from "react";
import {Virtuoso, GroupedVirtuoso} from 'react-virtuoso'
import {connect} from "react-redux";
import {withRouter} from "react-router-dom";
import classnames from "classnames";
import {
  isIosAndSafari,
  mobileCheck,
  updateThreadWithCalls
} from "../utils/helpers";

//strings
import strings from "../constants/localization";
import {ROUTE_THREAD} from "../constants/routes";

//actions
import {
  threadCreateWithExistThread,
  threadCreateWithUser,
  threadGetList,
  threadGetListPartialCanceled,
  threadsListUpdate
} from "../actions/threadActions";
import {chatCallsJoinToCall, chatSearchResult} from "../actions/chatActions";
import {contactChatting} from "../actions/contactActions";

//components
import AsideThreadsContextMenu from "./AsideThreadsContextMenu";
import AsideThreadsThread from "./AsideThreadsThread";
import AsideThreadsContact from "./AsideThreadsContact";
import AsideThreadsSearchResultLoadMore from "./AsideThreadsSearchResultLoadMore";
import List from "../../../Talk-ui-kit/src/list";
import Container from "../../../Talk-ui-kit/src/container";
import LoadingBlinkDots from "../../../Talk-ui-kit/src/loading/LoadingBlinkDots";
import {LoadingSpinner} from "../../../Talk-ui-kit/src/loading";
import Loading from "../../../Talk-ui-kit/src/loading";
import {Text} from "../../../Talk-ui-kit/src/typography";
import Gap from "../../../Talk-ui-kit/src/gap";
import Message from "../../../Talk-ui-kit/src/message";

//styling
import style from "../../styles/app/AsideThreads.scss";
import {CHAT_CALL_BOX_COMPACTED} from "../constants/callModes";
import {THREADS_LIMIT_PER_REQUEST} from "../constants/historyFetchLimits";

//statics
const sanitizeRule = {
  allowedTags: ["img"],
  allowedAttributes: {
    img: ["src", "style", "class"]
  }
};
const statics = {
  count: 50
};

//functions
function PartialLoadingFragment() {
  return (
    <Container bottomCenter centerTextAlign style={{zIndex: 1}}>
      <Loading><LoadingBlinkDots size="sm" invert/></Loading>
    </Container>
  )
}

function NoResult() {
  return <Container relative centerTextAlign>
    <Gap y="8" x="5">
      <Text size="sm" color="gray">{strings.noResult}</Text>
    </Gap>
  </Container>
}

@connect(store => {
  return {
    threads: store.threads.threads,
    threadsFetching: store.threads.fetching,
    threadsHasNext: store.threads.hasNext,
    threadsNextOffset: store.threads.nextOffset,
    threadsPartialFetching: store.threadsPartial.fetching,
    threadId: store.thread.thread.id,
    chatInstance: store.chatInstance.chatSDK,
    chatRouterLess: store.chatRouterLess,
    chatSearchResult: store.chatSearchResult,
    user: store.user.user,
    chatCallBoxShowing: store.chatCallBoxShowing,
    smallVersion: store.chatSmallVersion
  };
})
class AsideThreads extends Component {

  constructor(props) {
    super(props);
    this.onThreadClick = this.onThreadClick.bind(this);
    this.onStartChat = this.onStartChat.bind(this);
    this.onScrollBottomThreshold = this.onScrollBottomThreshold.bind(this);
    this.onMenuShow = this.onMenuShow.bind(this);
    this.onMenuHide = this.onMenuHide.bind(this);
    this.justRequestNow = true;
    this.partialRequestTimeOutId = null;
    this.state = {activeThread: null};
  }

  componentDidUpdate(oldProps) {
    const {chatInstance, threadId, dispatch} = this.props;
    if (!oldProps.chatInstance && chatInstance) {
      dispatch(threadGetList(0, THREADS_LIMIT_PER_REQUEST, null, false)).then(({threads}) => {
        updateThreadWithCalls(threads, dispatch);
        setTimeout(() => {
          this.justRequestNow = false;
        }, 500);//TODO: Buy time for end reached in first place this is not solution just workaround
      });
    }
    if (oldProps.threadId !== threadId) {
      this.setState({
        activeThread: threadId
      });
    }
  }

  onScrollBottomThreshold() {
    clearTimeout(this.partialRequestTimeOutId);
    const {threadsNextOffset, dispatch} = this.props;
    dispatch(threadGetList(threadsNextOffset, THREADS_LIMIT_PER_REQUEST));
    this.partialRequestTimeOutId = setTimeout(() => {
      dispatch(threadGetListPartialCanceled());
    }, 10000)
  }

  onMenuShow(id) {
    this.setState({
      isMenuShow: id
    });
  }

  onMenuHide() {
    this.setState({
      isMenuShow: false
    });
  }

  onThreadClick(thread, e) {
    const {chatRouterLess, history, threadId, dispatch} = this.props;
    if (e.nativeEvent.which === 3) {
      e.preventDefault();
      return true;
    }
    if (thread.id === threadId) {
      return true;
    }
    if (!chatRouterLess) {
      history.push(ROUTE_THREAD);
    }
    dispatch(threadCreateWithExistThread(thread));
  }

  onStartChat(contact) {
    const {history, chatRouterLess, dispatch} = this.props;
    dispatch(contactChatting(contact));
    dispatch(threadCreateWithUser(contact.id));
    if (!chatRouterLess) {
      history.push(ROUTE_THREAD);
    }
    dispatch(chatSearchResult());
  }

  render() {
    const {
      threads,
      threadsFetching,
      threadsHasNext,
      threadShowing,
      chatInstance,
      chatSearchResult,
      user,
      threadsPartialFetching,
      chatCallBoxShowing,
      smallVersion
    } = this.props;
    const {activeThread, isMenuShow} = this.state;
    const callBoxCompactedCondition = chatCallBoxShowing.showing === CHAT_CALL_BOX_COMPACTED;
    const isMobile = mobileCheck();
    const classNames = classnames({
      [style.AsideThreads]: true,
      [style["AsideThreads--smallVersion"]]: smallVersion,
      [style["AsideThreads--callBoxCompacted"]]: callBoxCompactedCondition,
      [style["AsideThreads--autoZIndex"]]: isIosAndSafari(),
      [style["AsideThreads--hiddenOverflow"]]: isMobile ? false : isMenuShow && true,
      [style["AsideThreads--isThreadShow"]]: threadShowing
    });
    let scrollerClassNames = classnames({
      [style.AsideThreads__Scroller]: true,
      [style["AsideThreads__Scroller--mobileVersion"]]: isMobile,
    });
    let filteredThreads = threads;
    let filteredContacts;
    let isSearchResult;
    if (chatSearchResult?.isShowing) {
      isSearchResult = this.justRequestNow = true;
      filteredThreads = chatSearchResult.threads;
      filteredContacts = chatSearchResult.contacts;
      if (filteredContacts) {
        filteredContacts = filteredContacts.filter(e => e.linkedUser);
      }
    }
    let pinedThread = threads.filter(e => e.pin);
    if (!user.id || !chatInstance || threadsFetching) {
      return (
        <Container className={classNames} centerTextAlign>
          <Gap y={10}>
            <LoadingSpinner size={30} primaryColor={style.colorAccent} width={3}/>
          </Gap>
        </Container>
      )
    } else {
      if (!chatSearchResult && !filteredThreads.length) {
        return (
          <section className={classNames}>
            <Container center centerTextAlign>
              <Message invert size="lg">{strings.thereIsNoChat}</Message>
            </Container>
          </section>
        )
      }

      return <Container className={classNames}>
        <AsideThreadsContextMenu onThreadClick={this.onThreadClick}
                                 user={user}
                                 onMenuShow={this.onMenuShow}
                                 onMenuHide={this.onMenuHide}
                                 pinedThread={pinedThread}/>
        {isMenuShow && <Container className={style.AsideThreads__Overlay} onContextMenu={e => {
          e.stopPropagation();
          e.preventDefault();
        }}/>}
        <List style={{height: "100%"}}>
          {isSearchResult &&
          <GroupedVirtuoso groupCounts={[filteredThreads.length || 1, filteredContacts.length || 1]}
                           className={scrollerClassNames}
                           topItemCount={0}
                           fixedItemHeight={79}
                           groupContent={index => {
                             return <Container className={style.AsideThreads__GroupsHead}>
                               <Gap y={8} x={5}>
                                 <Container className={style.AsideThreads__GroupsHeadTextContainer}>
                                   <Text color="accent">{index === 0 ? strings.conversations : strings.contacts}</Text>
                                 </Container>
                               </Gap>
                             </Container>
                           }
                           }
                           itemContent={(index, groupIndex) => {
                             if (groupIndex === 0) {
                               const thread = filteredThreads[index];
                               if (!thread) {
                                 return <NoResult/>
                               }
                               return <>
                                 <AsideThreadsThread $this={this}
                                                     onThreadClick={this.onThreadClick}
                                                     isMenuShow={isMenuShow}
                                                     activeThread={activeThread}
                                                     user={user}
                                                     thread={thread}/>
                                 {filteredThreads.length - 1 === index &&
                                 <AsideThreadsSearchResultLoadMore PartialLoadingFragment={PartialLoadingFragment}/>}
                               </>
                             } else {
                               const contact = filteredContacts[index - filteredThreads.length];
                               if (!contact) {
                                 return <NoResult/>
                               }
                               return <AsideThreadsContact onStartChat={this.onStartChat} contact={contact}/>
                             }
                           }}/>
          }
          {!isSearchResult &&
          <Virtuoso data={filteredThreads}
                    scrollerRef={ref => this.scrollerRef = ref}
                    atBottomStateChange={reached => {
                      if (reached) {
                        if (this.scrollerRef.scrollTop > 0) {
                          if (!threadsFetching && threadsHasNext && !threadsPartialFetching && !isSearchResult) {
                            this.onScrollBottomThreshold();
                          }
                        }
                      }
                    }}
                    className={scrollerClassNames}
                    fixedItemHeight={82.5}
                    itemContent={(index, el) =>
                      <AsideThreadsThread $this={this}
                                          onThreadClick={this.onThreadClick}
                                          isMenuShow={isMenuShow}
                                          activeThread={activeThread}
                                          user={user}
                                          thread={el}/>}/>
          }

        </List>
        {threadsPartialFetching && <PartialLoadingFragment/>}
      </Container>;
    }
  }
}

const exportDefault = withRouter(AsideThreads);
export {sanitizeRule, exportDefault as default};