import { BaseEventOrig, Button, Image, ScrollView, ScrollViewProps, Textarea, TextareaProps } from '@tarojs/components';
import Taro, { NodesRef, useDidHide, useDidShow, useLoad, useUnload } from '@tarojs/taro';

import classNames from 'classnames';
import { useCallback, useEffect, useRef, useState } from 'react';

import {
  ContactMessage,
  JobMessage,
  LocationMessage,
  MaterialMessage,
  TextMessage,
  TimeMessage,
} from '@/components/message-chat';
import PageLoading from '@/components/page-loading';
import SafeBottomPadding from '@/components/safe-bottom-padding';
import { EventName } from '@/constants/app';
import { CollectEventName } from '@/constants/event';
import { ChatWatchType, MessageType, PULL_NEW_MESSAGES_TIME } from '@/constants/message';
import useListHeight, { IUseListHeightProps } from '@/hooks/use-list-height';
import { RESPONSE_ERROR_CODE } from '@/http/constant';
import { HttpError } from '@/http/error';
import {
  IChatUser,
  IChatInfo,
  IChatMessage,
  IJobMessage,
  ILocationMessage,
  IMaterialMessage,
  IMessageStatus,
  PostMessageRequest,
} from '@/types/message';
import { isAnchorMode } from '@/utils/app';
import { getScrollItemId, last, logWithPrefix } from '@/utils/common';
import { collectEvent } from '@/utils/event';
import {
  isExchangeMessage,
  isJobMessage,
  isLocationMessage,
  isMaterialMessage,
  isTextMessage,
  isTimeMessage,
  openLocationSelect,
  postAddMessageTimes,
  postChatRejectWatch,
  postSendMessage,
  requestActionDetail,
  requestChatDetail,
  requestChatWatch,
  requestMessageStatusList,
  requestNewChatMessages,
} from '@/utils/message';
import { getPageQuery, parseQuery } from '@/utils/route';
import Toast from '@/utils/toast';
import { getUserId } from '@/utils/user';

import './index.less';

const PREFIX = 'page-message-chat';
const LIST_CONTAINER_CLASS = `${PREFIX}__chat-list`;
const CALC_LIST_PROPS: IUseListHeightProps = {
  selectors: [`.${LIST_CONTAINER_CLASS}`],
  calc: (rects: [NodesRef.BoundingClientRectCallbackResult]) => {
    const [rect] = rects;
    return rect.height;
  },
};
const log = logWithPrefix(PREFIX);
const chooseLocation = Taro.requirePlugin('chooseLocation');

interface ILoadProps {
  chatId: string;
  jobId?: string;
  job?: string;
  material?: string;
}

const getHeaderLeftButtonText = (job?: IJobMessage, material?: IMaterialMessage) => {
  if (job) {
    return '不感兴趣';
  }
  if (material) {
    return '标记为不合适';
  }
  return isAnchorMode() ? '不感兴趣' : '标记为不合适';
};

export default function MessageChat() {
  const listHeight = useListHeight(CALC_LIST_PROPS);
  const [input, setInput] = useState('');
  const [showMore, setShowMore] = useState(false);
  const [chat, setChat] = useState<IChatInfo | null>(null);
  const [reject, setReject] = useState<boolean>(false);
  const [receiver, setReceiver] = useState<IChatUser | null>(null);
  const [messages, setMessages] = useState<IChatMessage[]>([]);
  const [messageStatusList, setMessageStatusList] = useState<IMessageStatus[]>([]);
  const [jobId, setJobId] = useState<string>();
  const [job, setJob] = useState<IJobMessage>();
  const [material, setMaterial] = useState<IMaterialMessage>();
  const [scrollItemId, setScrollItemId] = useState<string>();
  const scrollToLowerRef = useRef(false);
  const autoSendRef = useRef({ sendJob: false, sendMaterial: false });
  const loadMoreRef = useRef(async (chatId: string, currentMessages: IChatMessage[], forceScroll?: boolean) => {
    try {
      const lastMsgId = last(currentMessages)?.msgId;
      const newMessages = await requestNewChatMessages({ chatId: chatId, lastMsgId });
      log('requestNewChatMessages', newMessages, forceScroll);
      if (newMessages.length) {
        setMessages([...currentMessages, ...newMessages]);
        (forceScroll || scrollToLowerRef.current) && setScrollItemId(getScrollItemId(last(newMessages)?.msgId));
      }
    } catch (e) {
      console.error(e);
    }
  });

  const handleInput = useCallback((e: BaseEventOrig<TextareaProps.onInputEventDetail>) => {
    const value = e.detail.value || '';
    setInput(value);
  }, []);

  const handleClickExpand = useCallback(() => setShowMore(true), []);

  const handleScroll = useCallback(
    (e: BaseEventOrig<ScrollViewProps.onScrollDetail>) => {
      // log('handleScroll', e);
      const { scrollTop, scrollHeight } = e.detail;
      scrollToLowerRef.current = listHeight + scrollTop >= scrollHeight - 40;
    },
    [listHeight]
  );

  const handleClickSendLocation = useCallback((e: React.MouseEvent) => {
    e.stopPropagation();
    openLocationSelect();
  }, []);

  const handleClickMoreOuter = () => showMore && setShowMore(false);

  const handleClickContactButton = useCallback(async () => {
    if (!chat) {
      return;
    }
    await loadMoreRef.current(chat.chatId, messages, true);
  }, [chat, messages]);

  const handleSendMessage = useCallback(
    async (newMessage: Omit<PostMessageRequest, 'chatId' | 'bizId'>) => {
      if (!chat) {
        return;
      }
      try {
        Taro.showLoading();
        await postSendMessage({ chatId: chat.chatId, bizId: jobId || chat.lastJobId, ...newMessage });
        await loadMoreRef.current(chat.chatId, messages, true);
        Taro.hideLoading();
      } catch (error) {
        const e = error as HttpError;
        const errorCode = e.errorCode;
        collectEvent(CollectEventName.MESSAGE_DEV_LOG, { action: 'send-message', e, message: newMessage });
        let tips = '发送失败请重试';
        let duration = 1500;
        if (
          errorCode === RESPONSE_ERROR_CODE.INSUFFICIENT_BALANCE &&
          newMessage.type === MessageType.RequestCompanyContact
        ) {
          tips = '今日申请交换联系方式次数已用完，当前每日限制为5次';
          duration = 3000;
        }
        tips.length > 7 ? Toast.info(tips, duration) : Toast.error(tips, duration);
      }
    },
    [chat, jobId, messages]
  );

  const handleClickReject = useCallback(async () => {
    if (!chat || !receiver || reject) {
      return;
    }
    const watchType = isAnchorMode() ? ChatWatchType.AnchorReject : ChatWatchType.CompanyReject;
    await postChatRejectWatch({
      type: watchType,
      toUserId: receiver.userId,
      jobId: jobId || chat.lastJobId,
      status: false,
    });
    setReject(true);
  }, [jobId, chat, receiver, reject]);

  const handleSendExchangeContact = useCallback(async () => {
    postAddMessageTimes('click_request_exchange_contact');
    const type = isAnchorMode() ? MessageType.RequestCompanyContact : MessageType.RequestAnchorContact;
    handleSendMessage({ type, actionObject: '' });
  }, [handleSendMessage]);

  const handleSendJobMessage = useCallback(async () => {
    if (!job || !receiver || autoSendRef.current.sendJob) {
      return;
    }
    const detail = await requestActionDetail({ type: MessageType.Job, bizId: job.id, toUserId: receiver.userId });
    if (!detail) {
      handleSendMessage({ type: MessageType.Job, actionObject: JSON.stringify(job) });
    }
    autoSendRef.current.sendJob = true;
  }, [job, receiver, handleSendMessage]);

  const handleSendMaterialMessage = useCallback(async () => {
    if (!material || !receiver || autoSendRef.current.sendMaterial) {
      return;
    }
    const detail = await requestActionDetail({
      type: MessageType.Material,
      bizId: material.id,
      toUserId: receiver.userId,
    });
    if (!detail) {
      handleSendMessage({ type: MessageType.Material, actionObject: JSON.stringify(material) });
    }
    autoSendRef.current.sendMaterial = true;
  }, [material, receiver, handleSendMessage]);

  const handleSendLocationMessage = useCallback(
    (location: Omit<ILocationMessage, 'id'>) => {
      setShowMore(false);
      handleSendMessage({ type: MessageType.Location, actionObject: JSON.stringify(location) });
    },
    [handleSendMessage]
  );

  const handleSendTextMessage = useCallback(async () => {
    if (!input) {
      return;
    }
    postAddMessageTimes('send_message_button');
    await handleSendMessage({ type: MessageType.Text, content: input });
    setInput('');
  }, [input, handleSendMessage]);

  // useEffect(() => {
  //   loadMoreRef.current = async (chatId: string, currentMessages: IChatMessage[], forceScroll: boolean) => {
  //     try {
  //       const lastMsgId = last(currentMessages)?.msgId;
  //       const newMessages = await requestNewChatMessages({ chatId: chatId, lastMsgId });
  //       log('requestNewChatMessages', newMessages);
  //       if (newMessages.length) {
  //         setMessages([...currentMessages, ...newMessages]);
  //         (forceScroll || scrollToLowerRef.current) && setScrollItemId(getScrollItemId(last(newMessages)?.msgId));
  //       }
  //     } catch (e) {
  //       console.error(e);
  //     }
  //   };
  // }, []);

  useEffect(() => {
    if (!chat) {
      return;
    }
    const intervalId = setInterval(async () => {
      loadMoreRef.current(chat.chatId, messages);
      const statusList = await requestMessageStatusList(chat.chatId);
      setMessageStatusList(statusList);
    }, PULL_NEW_MESSAGES_TIME);

    return () => {
      clearInterval(intervalId);
    };
  }, [chat, messages]);

  useEffect(() => {
    if (!chat) {
      return;
    }
    job && handleSendJobMessage();
    material && handleSendMaterialMessage();
  }, [chat, job, material, handleSendJobMessage, handleSendMaterialMessage]);

  useLoad(async () => {
    const query = getPageQuery<ILoadProps>();
    const chatId = query.chatId;
    if (!chatId) {
      return;
    }
    try {
      const currentUserId = getUserId();
      const watchType = isAnchorMode() ? ChatWatchType.AnchorReject : ChatWatchType.CompanyReject;
      const chatDetail = await requestChatDetail(chatId);
      const toUserInfo = chatDetail.participants.find(u => u.userId !== currentUserId);
      if (!toUserInfo) {
        throw new Error('not receiver');
      }
      const watchStatus = await requestChatWatch({
        type: watchType,
        toUserId: toUserInfo.userId,
        jobId: query.jobId || chatDetail.lastJobId,
      });
      const parseJob = query.job ? parseQuery<IJobMessage>(query.job) : null;
      const parseMaterial = query.material ? parseQuery<IMaterialMessage>(query.material) : null;
      // log('requestChatDetail', chatDetail, parseJob, parseMaterial);
      setChat(chatDetail);
      setJobId(query.jobId);
      setMessages(chatDetail.messages);
      setScrollItemId(getScrollItemId(last(chatDetail.messages)?.msgId));
      parseJob && setJob(parseJob);
      parseMaterial && setMaterial(parseMaterial);
      Taro.setNavigationBarTitle({ title: toUserInfo.nickName });
      setReceiver(toUserInfo);
      setReject(!watchStatus);
    } catch (e) {
      console.error(e);
      collectEvent(CollectEventName.MESSAGE_DEV_LOG, { action: 'init-chat-message', e });
      Toast.error('加载失败请重试');
    }
  });

  useDidShow(() => {
    const location = chooseLocation?.getLocation() as Omit<ILocationMessage, 'id'>;
    log('useDidShow', location);
    if (!location) {
      return;
    }
    // 发送定位消息
    handleSendLocationMessage(location);
    chooseLocation?.setLocation(null);
  });

  useDidHide(() => chooseLocation?.setLocation(null));

  useUnload(() => {
    chooseLocation?.setLocation(null);
    Taro.eventCenter.trigger(EventName.EXIT_CHAT_PAGE);
  });

  log('render', scrollItemId, scrollToLowerRef.current);

  return (
    <div className={PREFIX}>
      {!chat && <PageLoading className={`${PREFIX}__loading`} />}
      <div className={`${PREFIX}__header`} onTouchStart={handleClickMoreOuter}>
        <Button className={classNames(`${PREFIX}__header__reject`, { highlight: reject })} onClick={handleClickReject}>
          {getHeaderLeftButtonText(job, material)}
        </Button>
        <Button className={`${PREFIX}__header__exchange`} onClick={handleSendExchangeContact}>
          交换联系方式
        </Button>
      </div>
      <div className={`${PREFIX}__chat`} onTouchStart={handleClickMoreOuter}>
        <ScrollView className={LIST_CONTAINER_CLASS} scrollIntoView={scrollItemId} onScroll={handleScroll} scrollY>
          {messages.map((message: IChatMessage) => {
            if (isTextMessage(message)) {
              return (
                <TextMessage
                  id={message.msgId}
                  key={message.msgId}
                  message={message}
                  isRead={messageStatusList.some(m => m.msgId === message.msgId && !!m.isRead)}
                />
              );
            } else if (isTimeMessage(message)) {
              return <TimeMessage key={message.msgId} id={message.msgId} message={message} />;
            } else if (isJobMessage(message)) {
              return <JobMessage key={message.msgId} id={message.msgId} message={message} />;
            } else if (isMaterialMessage(message)) {
              return <MaterialMessage key={message.msgId} id={message.msgId} message={message} />;
            } else if (isExchangeMessage(message)) {
              return (
                <ContactMessage
                  key={message.msgId}
                  id={message.msgId}
                  message={message}
                  onClick={handleClickContactButton}
                />
              );
            } else if (isLocationMessage(message)) {
              return (
                <LocationMessage
                  id={message.msgId}
                  key={message.msgId}
                  message={message}
                  isRead={messageStatusList.some(m => m.msgId === message.msgId && !!m.isRead)}
                />
              );
            }
          })}
        </ScrollView>
      </div>
      <div className={`${PREFIX}__footer`}>
        <div className={`${PREFIX}__input-container`} onTouchStart={handleClickMoreOuter}>
          <Image
            mode="aspectFit"
            className={`${PREFIX}__expand-icon`}
            src={require('@/statics/svg/chat_expand.svg')}
            onTouchStart={e => e.stopPropagation()}
            onClick={handleClickExpand}
          />
          <Textarea
            fixed
            autoHeight
            value={input}
            maxlength={100}
            cursorSpacing={20}
            confirmType="return"
            onInput={handleInput}
            showConfirmBar={false}
            className={`${PREFIX}__input`}
            placeholderClass={`${PREFIX}__input-placeholder`}
          />
          <Button className={`${PREFIX}__send-button`} onClick={handleSendTextMessage}>
            发送
          </Button>
        </div>
        {showMore && (
          <div className={`${PREFIX}__more`}>
            <div className={`${PREFIX}__more__item`} onClick={handleClickSendLocation}>
              <div className={`${PREFIX}__more__item__icon-wrapper`}>
                <Image
                  mode="aspectFit"
                  className={`${PREFIX}__more__item__icon`}
                  src={require('@/statics/svg/location_black.svg')}
                />
              </div>
              <div className={`${PREFIX}__more__item__text`}>定位</div>
            </div>
          </div>
        )}
        <SafeBottomPadding />
      </div>
    </div>
  );
}
