import AddReactionIcon from "@mui/icons-material/AddReaction";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import {
  Box,
  Button,
  ButtonGroup,
  Chip,
  Dialog,
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
  TextField,
  Typography,
  useTheme,
} from "@mui/material";
import EmojiPicker, { EmojiStyle, Theme } from "emoji-picker-react";
import React from "react";
import { MatrixApiEvent } from "../../api/matrix/MatrixApiEvent";
import type { UsersMap } from "../../api/matrix/MatrixApiProfile";
import type { Room } from "../../api/matrix/MatrixApiRoom";
import { useAlert } from "../../hooks/contexts_provider/AlertDialogProvider";
import { useConfirm } from "../../hooks/contexts_provider/ConfirmDialogProvider";
import { useLoadingMessage } from "../../hooks/contexts_provider/LoadingMessageProvider";
import { useSnackbar } from "../../hooks/contexts_provider/SnackbarProvider";
import type {
  Message,
  MessageReaction,
  RoomEventsManager,
} from "../../utils/RoomEventsManager";
import { useUserInfo } from "../dashboard/BaseAuthenticatedPage";
import { EmojiIcon } from "../EmojiIcon";
import { AccountIcon } from "./AccountIcon";

export function RoomMessagesList(p: {
  room: Room;
  users: UsersMap;
  mgr: RoomEventsManager;
}): React.ReactElement {
  const messagesEndRef = React.createRef<HTMLDivElement>();

  // Automatically scroll to bottom when number of messages change
  React.useEffect(() => {
    if (messagesEndRef)
      messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [p.mgr.messages.length]);

  return (
    <div
      style={{
        flex: 1,
        width: "100%",
        paddingRight: "50px",
        overflow: "scroll",
        paddingLeft: "20px",
      }}
    >
      {p.mgr.messages.map((m, idx) => (
        <RoomMessage
          key={m.event_id}
          {...p}
          message={m}
          previousFromSamePerson={
            idx > 0 &&
            p.mgr.messages[idx - 1].account === m.account &&
            m.time_sent - p.mgr.messages[idx - 1].time_sent < 60 * 3 * 1000
          }
          firstMessageOfDay={
            idx === 0 ||
            m.time_sent_dayjs.startOf("day").unix() !=
              p.mgr.messages[idx - 1].time_sent_dayjs.startOf("day").unix()
          }
        />
      ))}

      <div ref={messagesEndRef} style={{ height: "10px" }} />
    </div>
  );
}

function RoomMessage(p: {
  room: Room;
  users: UsersMap;
  message: Message;
  previousFromSamePerson: boolean;
  firstMessageOfDay: boolean;
}): React.ReactElement {
  const theme = useTheme();
  const user = useUserInfo();
  const alert = useAlert();
  const confirm = useConfirm();
  const snackbar = useSnackbar();
  const loadingMessage = useLoadingMessage();

  const [showImageFullScreen, setShowImageFullScreen] = React.useState(false);

  const [editMessage, setEditMessage] = React.useState<string | undefined>();
  const [pickReaction, setPickReaction] = React.useState(false);

  const closeImageFullScreen = () => setShowImageFullScreen(false);

  const sender = p.users.get(p.message.account);

  const handleDeleteMessage = async () => {
    if (!(await confirm(`Do you really want to delete this message?`))) return;
    try {
      await MatrixApiEvent.DeleteEvent(p.room, p.message.event_id);
    } catch (e) {
      console.error(`Failed to delete message!`, e),
        alert(`Failed to delete message!${e}`);
    }
  };

  const handleEditMessage = () => setEditMessage(p.message.content);
  const handleCancelEditMessage = () => setEditMessage(undefined);
  const handleSubmitEditMessage = async (event: React.FormEvent) => {
    event.preventDefault();

    try {
      loadingMessage.show(`Updating message content...`);
      await MatrixApiEvent.SetTextMessageContent(
        p.room,
        p.message.event_id,
        editMessage!
      );
      setEditMessage(undefined);
    } catch (e) {
      console.error(`Failed to edit message!`, e);
      alert(`Failed to edit message content! ${e}`);
    } finally {
      loadingMessage.hide();
    }
  };

  const handleAddReaction = () => setPickReaction(true);
  const handleCancelAddReaction = () => setPickReaction(false);
  const handleSelectEmoji = async (key: string) => {
    loadingMessage.show("Setting reaction...");
    try {
      await MatrixApiEvent.ReactToEvent(p.room, p.message.event_id, key);
      setPickReaction(false);
    } catch (e) {
      console.error("Failed to select emoji!", e);
      alert(`Failed to select emoji! ${e}`);
    } finally {
      loadingMessage.hide();
    }
  };

  const handleToggleReaction = async (
    key: string,
    reaction: MessageReaction
  ) => {
    try {
      if (!reaction)
        await MatrixApiEvent.ReactToEvent(p.room, p.message.event_id, key);
      else await MatrixApiEvent.DeleteEvent(p.room, reaction.event_id);
    } catch (e) {
      console.error(`Failed to toggle reaction!`, e);
      snackbar(`Failed to toggle reaction! ${e}`);
    }
  };

  return (
    <>
      {/* Print date if required */}
      {p.firstMessageOfDay && (
        <Typography
          variant="caption"
          component={"div"}
          style={{ textAlign: "center", marginTop: "50px" }}
        >
          {p.message.time_sent_dayjs.format("DD/MM/YYYY")}
        </Typography>
      )}

      {/* Give person name if required */}
      {(!p.previousFromSamePerson || p.firstMessageOfDay) && sender && (
        <div
          style={{
            display: "flex",
            flexDirection: "row",
            alignItems: "center",
            marginTop: "20px",
          }}
        >
          <AccountIcon user={sender} />
          &nbsp;&nbsp;&nbsp;
          {sender.display_name}
        </div>
      )}

      {/* Message content */}
      <Box
        style={{
          wordBreak: "break-all",
          wordWrap: "break-word",
          maxWidth: "100%",
          transition: "all 0.01s ease-in",
          position: "relative",
        }}
        component="div"
        sx={{
          [theme.getColorSchemeSelector("dark") + "&:hover"]: {
            backgroundColor: "#ffffff2b",
          },
          [theme.getColorSchemeSelector("light") + "&:hover"]: {
            backgroundColor: "#00000039",
          },
          "&:hover *": { visibility: "visible" },
        }}
      >
        <Typography variant="caption">
          &nbsp; {p.message.time_sent_dayjs.format("HH:mm")}
        </Typography>{" "}
        &nbsp;&nbsp;&nbsp;
        {p.message.image ? (
          <img
            onClick={() => setShowImageFullScreen(true)}
            src={MatrixApiEvent.GetEventFileURL(
              p.room,
              p.message.event_id,
              true
            )}
            style={{
              maxWidth: "200px",
            }}
          />
        ) : (
          p.message.content
        )}
        <ButtonGroup
          className="buttons"
          size="small"
          style={{
            position: "absolute",
            visibility: "hidden",
            display: "block",
            top: "-34px",
            right: "0px",
          }}
        >
          {/* Common reactions */}
          <ReactionButton {...p} emojiKey="👍" /> {/* 👍 */}
          <ReactionButton {...p} emojiKey="♥️" /> {/* ♥️ */}
          <ReactionButton {...p} emojiKey="😂" /> {/* 😂 */}
          {/* Add reaction */}
          <Button onClick={handleAddReaction}>
            <AddReactionIcon />
          </Button>
          {/* Edit text message */}
          {p.message.account === user.info.matrix_user_id &&
            !p.message.image && (
              <Button onClick={handleEditMessage}>
                <EditIcon />
              </Button>
            )}
          {/* Delete message */}
          {p.message.account === user.info.matrix_user_id && (
            <Button onClick={handleDeleteMessage}>
              <DeleteIcon color="error" />
            </Button>
          )}
        </ButtonGroup>
      </Box>

      {/* Reaction */}
      <Box sx={{ marginLeft: "50px" }}>
        {[...p.message.reactions.keys()].map((r) => {
          const userReaction = p.message.reactions
            .get(r)!
            .find((r) => r.account === user.info.matrix_user_id);
          return (
            <Chip
              size="small"
              style={{
                height: "2em",
                marginRight: "5px",
                maxHeight: "unset",
                cursor: "pointer",
              }}
              slotProps={{
                root: { onClick: () => handleToggleReaction(r, userReaction) },
                label: { style: { height: "2em" } },
              }}
              color={userReaction !== undefined ? "success" : undefined}
              variant="filled"
              label={
                <span
                  style={{
                    display: "inline-flex",
                    justifyContent: "center",
                    alignItems: "center",
                    flexDirection: "row",
                  }}
                >
                  <div style={{ margin: "3px 3px" }}>
                    <EmojiIcon emojiKey={r} />
                  </div>
                  <div style={{ height: "2em", marginLeft: "2px" }}>
                    {p.message.reactions.get(r)?.length}
                  </div>
                </span>
              }
            />
          );
        })}
      </Box>

      {/* Full screen image dialog */}
      <Dialog open={showImageFullScreen} onClose={closeImageFullScreen}>
        <img
          src={MatrixApiEvent.GetEventFileURL(
            p.room,
            p.message.event_id,
            false
          )}
        />
      </Dialog>

      {/* Pick reaction dialog */}
      <Dialog open={pickReaction} onClose={handleCancelAddReaction}>
        <EmojiPicker
          emojiStyle={EmojiStyle.GOOGLE}
          theme={Theme.AUTO}
          onEmojiClick={(emoji) => handleSelectEmoji(emoji.emoji)}
        />
      </Dialog>

      {/* Edit message dialog */}
      <Dialog open={!!editMessage} onClose={handleCancelEditMessage} fullWidth>
        <DialogTitle>Edit message content</DialogTitle>
        <DialogContent>
          <DialogContentText>Enter new message content:</DialogContentText>
          <form
            onSubmit={handleSubmitEditMessage}
            id={`edit-message-${p.message.event_id}`}
          >
            <TextField
              autoFocus
              required
              margin="dense"
              label="New content"
              type="text"
              fullWidth
              variant="standard"
              value={editMessage}
              onChange={(e) => setEditMessage(e.target.value)}
            />
          </form>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleCancelEditMessage}>Cancel</Button>
          <Button type="submit" form={`edit-message-${p.message.event_id}`}>
            Edit
          </Button>
        </DialogActions>
      </Dialog>
    </>
  );
}

function ReactionButton(p: {
  room: Room;
  message: Message;
  emojiKey: string;
}): React.ReactElement {
  const alert = useAlert();
  const user = useUserInfo();

  const sendEmoji = async () => {
    try {
      await MatrixApiEvent.ReactToEvent(p.room, p.message.event_id, p.emojiKey);
    } catch (e) {
      console.error("Failed to send reaction!", e);
      alert(`Failed to send reaction! ${e}`);
    }
  };

  // Do not offer to react to existing reactions
  if (
    p.message.reactions
      .get(p.emojiKey)
      ?.find(
        (r) => r.key === p.emojiKey && r.account === user.info.matrix_user_id
      ) !== undefined
  )
    return <></>;

  return (
    <Button onClick={sendEmoji}>
      <EmojiIcon {...p} />
    </Button>
  );
}
