import React, {Component, createRef, Fragment} from "react";
import {connect} from "react-redux";
import {Dispatch} from "redux";
import debounce from "lodash/debounce";

import {UserInfo} from "@/typings/user";
import {ChatboxTextMessage} from "@/typings/message.d";
import {sendTextMessageToServer} from "@/lib/messaging";
import {getCurrentUTCTimestamp} from "@/lib/utils";
import {getUserChatHistory} from "@/lib/api";
import {AppState, NetworkingStatusType} from "@/store/typing";
import {actions} from "@/store/actions";
import {NOTIFICATIONS} from "@/constant/text";
import {getAdminInfo} from "@/lib/user";
import storage, {userChatHistoryStorageKey} from "@/lib/storage";

import {MessageItem} from "./MessageItem";
import InputMessage from "./InputMessage";
import TypingNotification from "./TypingNotification";

import './Chatbox.css';


type ChatboxProps = {
    user_info: UserInfo;
    network_connection: NetworkingStatusType;
    chat_messages: ChatboxTextMessage[];
    dispatch: Dispatch;
}

type ChatboxState = {
    loadingHistory: boolean,
}


/*function persistMessage(user_id: string, messages: any[]) {
    const MAX_MESSAGE_KEPT_PER_USER: number = 200;

    // limit messages per user
    const total_message     = messages.length;
    const keep_from_index   = total_message - MAX_MESSAGE_KEPT_PER_USER;

    storage.save(userChatHistoryStorageKey(user_id), (keep_from_index > 0) ? messages.slice(keep_from_index) : messages);
}*/


class Chatbox extends Component<ChatboxProps, ChatboxState> {

    static MAX_MESSAGE_KEPT_PER_USER: number = 200;

    private trackLastScrollTop: number;
    private disableScrollBottom: boolean;
    private loadingHistoryStatus: 'idle' | 'loading' | 'done';
    private noMoreHistoryMessage: boolean;

    private scrollBottomDiv: React.RefObject<HTMLDivElement>;
    private scrollToHistoryDiv: React.RefObject<HTMLDivElement>;

    constructor(props: ChatboxProps) {
        super(props);
        this.state = {
            loadingHistory: false
        }
        this.scrollBottomDiv = createRef<HTMLDivElement>();
        this.scrollToHistoryDiv = createRef<HTMLDivElement>();

        this.trackLastScrollTop = 0;
        this.disableScrollBottom = false;
        this.loadingHistoryStatus = 'idle';
        this.noMoreHistoryMessage = false;
    }

    componentDidMount() {
        this.getChatHistory().then();
    }

    getSnapshotBeforeUpdate(prevProps: ChatboxProps, prevState: ChatboxState) {
        // Are we adding new items to the list?
        // Capture the scroll position so we can adjust scroll later.
        if (prevProps.chat_messages.length < this.props.chat_messages.length) {
            const div = this.scrollToHistoryDiv.current;
            return (div) ? div.scrollHeight - div.scrollTop : null;
        }
        return null;
    }

    componentDidUpdate(prevProps: ChatboxProps, prevState: ChatboxState, snapshot: number | null) {
        console.log('componentDidUpdate at ' + new Date().getTime());
        // snapshot is the result of getSnapshotBeforeUpdate()
        if (snapshot !== null) {
            this.scrollToHistoryDiv.current!.scrollTop = this.scrollToHistoryDiv.current!.scrollHeight - snapshot;
            // this.scrollToHistoryDiv.current!.scrollTo({top: expected_top, behavior:'smooth'});
            this.loadingHistoryStatus = 'idle';
        }

        if(prevProps.chat_messages.length !== this.props.chat_messages.length) {
            this.scrollToBottom();
        }
    }


    getChatHistory = async (from_scroll: boolean = false) => {

        console.log('Chatbox getChatHistory');

        const {user_info, chat_messages, network_connection, dispatch} = this.props;

        // todo: 15-April-2021
        // because 1 user can chat with many admin staff, we need to remove all old messages in storage (if exist) when user first chat with this staff
        if(network_connection === 'offline') {
            // get for offline view only
            const stored_messages: ChatboxTextMessage[] = await storage.get(userChatHistoryStorageKey(user_info.id)) || [];
            console.log('stored_messages');
            console.log(stored_messages);
            dispatch(actions.addHistoryMessage({[user_info.id]: stored_messages}));
            return;
        }

        if ( chat_messages.length === 0 || from_scroll ) {

            let last_fetch = (chat_messages.length > 0) ? chat_messages[0].time : 0;

            this.setState({loadingHistory: true});

            const old_messages = await getUserChatHistory({thread_id: user_info.id, last_fetch: last_fetch});

            this.setState({loadingHistory: false});

            if (old_messages.length === 0) {
                console.log('old_messages: this.noMoreHistoryMessage = true')
                this.noMoreHistoryMessage = true;
                return;
            }

            console.log('dispatching historied messages');
            dispatch(actions.addHistoryMessage({[user_info.id]: old_messages}));
        }
    }

    scrollToBottom = () => {
        // no scroll if user view history
        if ( this.disableScrollBottom ) {
            return;
        }

        this.scrollBottomDiv.current!.scrollIntoView({ behavior: "smooth" });
    }

    showNotification = () => {
        const { user_info, network_connection } = this.props;
        const NotiMessage = (txt: string, key: string|number) => (<div key={key}>{txt}</div>);

        let list_messages: string[] = [];

        // in-chat notification: user typing
        if (network_connection === 'offline') {
            list_messages.push(NOTIFICATIONS['network_offline']);
        }

        // notify user offline
        if (!user_info.online) {
            list_messages.push(NOTIFICATIONS['user_offline']);
        }

        if(list_messages.length > 0) {
            return (
                <div style={{fontStyle:'italic', fontSize:11, textAlign: 'center'}} >
                    {
                        list_messages.map((txt, index) => NotiMessage(txt, index))
                    }
                </div>
            )
        }

        // default nothing
        return null;
    }

    handleChatboxScroll = debounce((event: any) => {

        console.log('start handleChatboxScroll at ' + new Date().getSeconds());
        //console.log(event);

        if(!event.target) {
            console.log('handleChatboxScroll: event.currentTarget');
            return;
        }

        if(this.noMoreHistoryMessage) {
            console.log('handleChatboxScroll: noMoreHistoryMessage');
            return;
        }

        // user is scrolling up, so disable bottom scrolling for user to read old messages without interruption
        if(this.trackLastScrollTop > event.target.scrollTop) {
            this.disableScrollBottom = true;
        }

        // track again
        this.trackLastScrollTop = event.target.scrollTop;

        // load history on top
        if (event.target.scrollTop === 0 && this.loadingHistoryStatus === 'idle') {
            this.loadingHistoryStatus = 'loading';
            this.disableScrollBottom = true;
            console.log('handleChatboxScroll should get the history now ... ');
             this.getChatHistory(true).then();
        }
    }, 300)


    sendMessage = (typed_message: string) => {
        //alert(typed_message);
        const { user_info, dispatch, network_connection } = this.props; // , admin_info, user_info

        //const {typed_message} = this.state;
        if(typed_message === '') {
            return;
        }

        // reset when user send new message
        if(this.disableScrollBottom) {
            this.disableScrollBottom = false;
        }

        // cannot send message when network_connection is offline
        // and restore message to the input
        if(network_connection === 'offline') {
            console.log("network_connection =offline");
            return;
        }

        // let composed_message = composeNewSendingMessage(txt);
        // add locally
        let composed_message = {
            id: '',
            from: 'me',
            content: typed_message,
            time: getCurrentUTCTimestamp(true),
            // sequence: local_sequence,
            deliveryStatus: 0,
        } as ChatboxTextMessage;

        dispatch(actions.addCurrentMessage({[user_info.id] : [composed_message]}));

        // pass to networking layer to send to server
        let send_to = (user_info.online) ? [user_info.id, user_info.node].join('-').trim() : '';
        sendTextMessageToServer(send_to, typed_message);
    }

    showLoadingHistory = () => {
        const {loadingHistory} = this.state;

        if(loadingHistory) {
            return (
                <div style={{fontStyle:'italic', fontSize:11, textAlign: 'center'}} >Loading ...</div>
            )
        }

        // default
        return null;
    }


    render() {

        const { network_connection, user_info, chat_messages} = this.props;
        const admin_info = getAdminInfo();

        return (
            <Fragment >
                <div
                    style={{height: 600, overflow: 'auto'}}
                    onScroll={this.handleChatboxScroll}
                    // onClick={listenForInChatAction}
                    ref={this.scrollToHistoryDiv}
                    className={'message-list'}
                >

                    <div className="message-list-container">

                        { this.showLoadingHistory() }

                        {
                            chat_messages.map((message, index) => <MessageItem key={message.time + '-' + index} is_me={admin_info.id === message.from} {...message} />)
                        }

                        { user_info.typing && <TypingNotification /> }
                    </div>

                    { this.showNotification() }

                    <div ref={this.scrollBottomDiv}  />

                </div>

                <InputMessage
                    sendMessage={this.sendMessage}
                    network_connection={network_connection}
                    user_info={user_info}
                />
            </Fragment>
        )
    }
}

const mapStateToProps = (state: AppState, ownProps: {user_info: UserInfo}) => ({
    network_connection: state.network_connection,
    chat_messages: state.current_messages[ownProps.user_info.id] || [],
});
export default connect(mapStateToProps)(Chatbox);
