import React, { useEffect, useRef } from 'react';
import {GlobalState, globalStore} from '../stores/global';
import { RoomState, roomStore, windowMonitor} from '../stores/room';
import { WhiteboardState, whiteboard } from '../stores/whiteboard';
import { useHistory, useLocation } from 'react-router-dom';
import { resolveMessage, resolvePeerMessage, resolveChannelAttrs, jsonParse } from '../utils/helper';
import GlobalStorage from '../utils/custom-storage';
import {endPoint, goHome} from "../services/agora-end-points";
// import {Map as immuMap} from "immutable";

export type IRootProvider = {
  globalState: GlobalState
  roomState: RoomState
  whiteboardState: WhiteboardState
}

export interface IObserver<T> {
  subscribe: (setState: (state: T) => void) => void
  unsubscribe: () => void
  defaultState: T
}

function useObserver<T>(store: IObserver<T>) {
  const [state, setState] = React.useState<T>(store.defaultState);
  React.useEffect(() => {
    store.subscribe((state: any) => {
      setState(state);
    });
    return () => {
      store.unsubscribe();
    }
  }, [store]);

  return state;
}


export const RootContext = React.createContext({} as IRootProvider);

export const useStore = () => {
  const context = React.useContext(RootContext)
  if (context === undefined) {
    throw new Error('useStore must be used within a RootProvider');
  }
  return context;
}

export const useGlobalState = () => {
  return useStore().globalState;
}

export const useRoomState = () => {
  return useStore().roomState;
}

export const useWhiteboardState = () => {
  return useStore().whiteboardState;
}



export const RootProvider: React.FC<any> = ({children}) => {
  const globalState = useObserver<GlobalState>(globalStore);
  const roomState = useObserver<RoomState>(roomStore);
  const whiteboardState = useObserver<WhiteboardState>(whiteboard);
  const history = useHistory();

  const ref = useRef<boolean>(false);

  useEffect(() => {
    return () => {
      ref.current = true;
    }
  }, []);

  const value = {
    globalState,
    roomState,
    whiteboardState,
  }

  useEffect(() => {
    if (!roomStore.state.rtm.joined) return;
    const rtmClient = roomStore.rtmClient;
    rtmClient.on('ConnectionStateChanged', ({ newState, reason }: { newState: string, reason: string }) => {
      console.log(`[ConnectionStateChanged] newState: ${newState} reason: ${reason}`);
      if (reason === 'LOGIN_FAILURE') {
        globalStore.showToast({
          type: 'rtmClient',
          message: 'login failure'
        });
        // history.push('/');
        goHome(history)
        return;
      }
      if (reason === 'REMOTE_LOGIN' || newState === 'ABORTED') {
        globalStore.showToast({
          type: 'rtmClient',
          message: 'kick'
        });
        // history.push('/');
        goHome(history)
        return;
      }
    });
    rtmClient.on("MessageFromPeer", ({ message: { text }, peerId, props }: { message: { text: string }, peerId: string, props: any }) => {
      console.log('MessageFromPeer', text);
      const body = resolvePeerMessage(text);
      body.cmd = +body.cmd
      resolveMessage(peerId, body);

      roomStore.handlePeerMessage(body.cmd, peerId)
      .then(() => {
      }).catch(console.warn);
    });
    rtmClient.on("AttributesUpdated", (attributes: object) => {
      if (Object.keys(attributes).length === 0) {
        globalStore.showToast({
          type: 'rtmClient',
          message: 'teacher close classroom'
        });
        // history.push('/');
        goHome(history)
        return;
      }
      console.log('[rtm-client] updated origin attributes', JSON.parse(JSON.stringify(attributes)));
      const channelAttrs = resolveChannelAttrs(attributes);
      // console.log('[rtm-client] updated resolved attrs', channelAttrs);
      roomStore.updateRoomAttrs(channelAttrs)
    });
    rtmClient.on("MemberJoined", async (memberId: string) => {
      console.log(memberId, '[rtm] MemberJoined MemberJoined MemberJoined MemberJoined');
      if (roomStore.state.me.role === 'teacher') {
        await roomStore.rtmJoinUser(memberId)
      }
    });
    rtmClient.on("MemberLeft", async (memberId: string) => {
      console.log(memberId, '[rtm] MemberLeft MemberLeft MemberLeft MemberLeft');
      if (roomStore.state.me.role === 'teacher') {
        await roomStore.rtmLeaveUser(memberId)
      }
      if (roomStore.state.applyUid === +memberId) {
        roomStore.updateCourseLinkUid(0)
        .then(() => {
          globalStore.removeNotice();
        }).catch(console.warn);
      }
    });
    rtmClient.on("MemberCountUpdated", (count: number) => {
      console.log(count, '[rtm] MemberCountUpdated MemberCountUpdated MemberCountUpdated');
      !ref.current && roomStore.updateMemberCount(count);
    });
    rtmClient.on("ChannelMessage", ({ memberId, message }: { message: { text: string }, memberId: string }) => {
      const msg = jsonParse(message.text);
      const chatMessage = {
        account: msg.account,
        text: msg.content,
        link: msg.link,
        ts: +Date.now(),
        id: memberId,
      }
      console.log("[rtmClient] ChannelMessage", msg);
      roomStore.updateChannelMessage(chatMessage);
    });
    return () => {
      rtmClient.removeAllListeners();
    }
    // eslint-disable-next-line
  }, [roomStore.state.rtm.joined]);

  const location = useLocation();



  useEffect(() => {
    // console.log(11223344, location.pathname)
    if (location.pathname === '/') {
      // alert('clear1')
      // GlobalStorage.clear('agora_room');
      windowMonitor.removeUnloadEvent();
      return;
    }
    windowMonitor.addUnloadEvent();
    const room = value.roomState;
    GlobalStorage.save('agora_room', {
      me: room.me,
      course: room.course,
      mediaDevice: room.mediaDevice,
      cwLink: endPoint.userToken,
      studentsOrder: room.studentsOrder,
      userUsage: room.userUsage,
      handsUpStudents: room.handsUpStudents
    });
    // WARN: DEBUG ONLY MUST REMOVED IN PRODUCTION
    //@ts-ignore
    window.room = roomState;
    //@ts-ignore
    window.state = globalState;
    //@ts-ignore
    window.whiteboard = whiteboardState;
    //@ts-ignore
    window.store = globalStore;
    // eslint-disable-next-line
  }, [value, location]);

  return (
    <RootContext.Provider value={value}>
      {children}
    </RootContext.Provider>
  )
}
