import { Divider } from "@mui/material";
import React from "react";
import { MatrixApiEvent } from "../../api/matrix/MatrixApiEvent";
import {
  MatrixApiProfile,
  type UsersMap,
} from "../../api/matrix/MatrixApiProfile";
import { MatrixApiRoom, type Room } from "../../api/matrix/MatrixApiRoom";
import { MatrixSyncApi } from "../../api/MatrixSyncApi";
import type { WsMessage } from "../../api/WsApi";
import { RoomEventsManager } from "../../utils/RoomEventsManager";
import { AsyncWidget } from "../AsyncWidget";
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
import { AppIconModifier } from "./AppIconModifier";
import { MatrixWS } from "./MatrixWS";
import { RoomSelector } from "./RoomSelector";
import { RoomWidget } from "./RoomWidget";
import { SpaceSelector } from "./SpaceSelector";

export function MainMessageWidget(): React.ReactElement {
  const [rooms, setRooms] = React.useState<Room[] | undefined>();
  const [users, setUsers] = React.useState<UsersMap | undefined>();

  const loadRoomsList = async () => {
    await MatrixSyncApi.Start();

    const rooms = await MatrixApiRoom.ListJoined();
    setRooms(rooms);

    // Get the list of users in rooms
    const users = rooms.reduce((prev, r) => {
      r.members.forEach((m) => prev.add(m));
      return prev;
    }, new Set<string>());

    setUsers(await MatrixApiProfile.GetMultiple([...users]));
  };

  return (
    <AsyncWidget
      loadKey={1}
      load={loadRoomsList}
      ready={!!rooms && !!users}
      errMsg="Failed to initialize messaging component!"
      build={() => (
        <_MainMessageWidget
          rooms={rooms!}
          users={users!}
          onRoomsListUpdate={(cb) => setRooms((r) => cb(r!))}
        />
      )}
    />
  );
}

function _MainMessageWidget(p: {
  rooms: Room[];
  users: UsersMap;
  onRoomsListUpdate: (cb: (a: Room[]) => Room[]) => void;
}): React.ReactElement {
  const user = useUserInfo();

  const [space, setSpace] = React.useState<string | undefined>();
  const [currentRoom, setCurrentRoom] = React.useState<Room | undefined>();

  const spaceRooms = React.useMemo(() => {
    return p.rooms
      .filter((r) => !r.is_space && (!space || r.parents.includes(space)))
      .sort(
        (a, b) => (b.latest_event?.time ?? 0) - (a.latest_event?.time ?? 0)
      );
  }, [space, p.rooms]);

  const unreadRooms = React.useMemo(
    () => p.rooms.filter((r) => r.number_unread_messages > 0).length,
    [p.rooms]
  );

  const [_refreshCount, setRefreshCount] = React.useState(0);
  const [roomMgr, setRoomMgr] = React.useState<undefined | RoomEventsManager>();

  const loadRoom = async () => {
    setRoomMgr(undefined);
    if (!currentRoom) {
      console.warn("Cannot load manager for no room!");
      return;
    }
    const messages = await MatrixApiEvent.GetRoomEvents(currentRoom);
    const mgr = new RoomEventsManager(currentRoom!, messages);
    setRoomMgr(mgr);
  };

  const [wsState, setWsState] = React.useState("");
  const handleWsEvent = (m: WsMessage) => {
    // Process messages for current room
    if (roomMgr?.processWsMessage(m)) {
      console.info("Current room updated!");
      setRefreshCount((c) => c + 1);
    }

    // Add a new unread message on left sidebar
    if (
      m.type === "RoomMessageEvent" &&
      !m.data["m.new_content"] &&
      m.sender !== user.info.matrix_user_id
    ) {
      p.onRoomsListUpdate((r) => {
        const n = [...r];
        const idx = r.findIndex((el) => el.id === m.room_id);
        if (idx)
          n[idx] = {
            ...n[idx],
            number_unread_messages: n[idx].number_unread_messages + 1,
          };
        return n;
      });
    }
  };

  return (
    <div style={{ display: "flex", height: "100%" }}>
      {/* Websocket */}
      <div style={{ position: "absolute", right: "0px", padding: "10px" }}>
        <MatrixWS onMessage={handleWsEvent} onStateChange={setWsState} />
      </div>

      {/** Application icon modifier */}
      <AppIconModifier numberUnread={unreadRooms} state={wsState} />

      {/* Space selector */}
      <SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />

      {/* Separator */}
      <Divider orientation="vertical" />

      {/* Room selector */}
      <RoomSelector
        {...p}
        rooms={spaceRooms}
        currRoom={currentRoom}
        onChange={setCurrentRoom}
      />

      {/* Separator */}
      <Divider orientation="vertical" />

      {/* If no room is selected */}
      {currentRoom === undefined && (
        <div
          style={{
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            flex: 1,
          }}
        >
          No room selected.
        </div>
      )}

      {/* In case of room */}
      {currentRoom && (
        <AsyncWidget
          loadKey={currentRoom.id}
          ready={!!roomMgr}
          load={loadRoom}
          errMsg="Failed to load room!"
          build={() => (
            <RoomWidget {...p} manager={roomMgr!} room={currentRoom} />
          )}
        />
      )}
    </div>
  );
}
