import util from "../../plugins/utils";

export default {
  data() {
    return {
      pollQuestion: "",
      pollAnswers: [],
      pollTotalVotes: 0,
      pollResponseRelations: null,
      pollEndRelations: null,
      pollEndTs: null,
      pollIsDisclosed: true,
      pollTentativeAnswer: null,
      pollViewResults: false,
    };
  },
  mounted() {
    this.$matrix.on("Room.timeline", this.pollMixinOnEvent);
    this.pollQuestion =
      (this.event &&
        this.event.getContent()["m.poll.start"] &&
        this.event.getContent()["m.poll.start"]["question"]["body"]) ||
      (this.event &&
        this.event.getContent()["org.matrix.msc3381.poll.start"] &&
        this.event.getContent()["org.matrix.msc3381.poll.start"]["question"]["body"]) ||
      "";
    this.updateAnswers();
  },
  destroyed() {
    this.$matrix.off("Room.timeline", this.pollMixinOnEvent);
  },
  beforeDestroy() {
    if (this.pollResponseRelations) {
      this.pollResponseRelations.off("Relations.add", this.onAddRelation);
      this.pollResponseRelations = null;
    }
    if (this.pollEndRelations) {
      this.pollEndRelations.off("Relations.add", this.onAddRelation);
      this.pollEndRelations = null;
    }
  },
  methods: {
    updateAnswers() {
      let answers =
        (this.event && this.event.getContent()["m.poll.start"] && this.event.getContent()["m.poll.start"]["answers"]) ||
        (this.event &&
          this.event.getContent()["org.matrix.msc3381.poll.start"] &&
          this.event.getContent()["org.matrix.msc3381.poll.start"]["answers"]) ||
        [];
      var answerMap = {};
      var answerArray = [];
      answers.forEach((a) => {
        let text = a["org.matrix.msc1767.text"];
        let answer = { id: a.id, text: text, numVotes: 0, percentage: 0 };
        answerMap[a.id] = answer;
        answerArray.push(answer);
      });

      // Kind of poll
      this.pollIsDisclosed = false;
      if (this.event) {
        if (this.event.getContent()["m.poll.start"]) {
          this.pollIssDisclosed = this.event.getContent()["m.poll.start"]["kind"] != "m.poll.undisclosed" || false;
        } else if (this.event.getContent()["org.matrix.msc3381.poll.start"]) {
          this.pollIssDisclosed =
            this.event.getContent()["org.matrix.msc3381.poll.start"]["kind"] != "org.matrix.msc3381.poll.undisclosed" ||
            false;
        }
      }

      // Look for poll end
      this.pollEndRelations =
        this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), "m.reference", "m.poll.end") ||
        this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), "m.reference", "org.matrix.msc3381.poll.end");
      if (this.pollEndRelations) {
        const endMessages = this.pollEndRelations.getRelations() || [];
        if (endMessages.length > 0) {
          this.pollEndTs = endMessages[endMessages.length - 1].getTs();
        }
      }

      // Process votes
      this.pollResponseRelations =
        this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), "m.reference", "m.poll.response") ||
        this.timelineSet.relations.getChildEventsForEvent(this.event.getId(), "m.reference", "org.matrix.msc3381.poll.response");
      var userVotes = {};
      if (this.pollResponseRelations) {
        const votes = this.pollResponseRelations.getRelations() || [];
        for (const vote of votes) {
          //const emoji = r.getRelation().key;
          if (this.pollEndTs && vote.getTs() > this.pollEndTs) {
            continue; // Invalid vote, after poll was closed.
          }
          const sender = vote.getSender();
          const answersFromThisUser =
            (vote.getContent()["m.poll.response"] && vote.getContent()["m.poll.response"]["answers"]) ||
            (vote.getContent()["org.matrix.msc3381.poll.response"] &&
              vote.getContent()["org.matrix.msc3381.poll.response"]["answers"]) ||
            [];
          if (answersFromThisUser.length == 0) {
            delete userVotes[sender];
          } else {
            userVotes[sender] = answersFromThisUser;
          }
        }
      }
      var totalVotes = 0;
      for (const [user, answersFromThisUser] of Object.entries(userVotes)) {
        for (const a of answersFromThisUser) {
          if (answerMap[a]) {
            answerMap[a].numVotes += 1;
            totalVotes += 1;
            if (user == this.$matrix.currentUserId) {
              answerMap[a].hasMyVote = true;
            }
          }
        }
      }

      // Update percentages. For algorithm, see here: https://revs.runtime-revolution.com/getting-100-with-rounded-percentages-273ffa70252b
      // (need to add up to 100%)
      if (totalVotes > 0) {
        let a = answerArray.map((a) => {
          let votes = (100 * a.numVotes) / totalVotes;
          return {
            answer: a,
            unrounded: votes,
            floor: Math.floor(votes),
          };
        });
        a.sort((item1, item2) => {
          Math.abs(item2.floor) - Math.abs(item1.floor);
        });
        var diff =
          100 -
          a.reduce((previousValue, currentValue) => {
            return previousValue + currentValue.floor;
          }, 0);
        const maxVotes = Math.max(...a.map((item) => item.unrounded));
        a.map((answer, index) => {
          answer.answer.percentage = index < diff ? answer.floor + 1 : answer.floor;
          answer.answer.winner = answer.unrounded == maxVotes;
        });
      }
      this.pollAnswers = answerArray;
      this.pollTotalVotes = totalVotes;
    },
    pollAnswer(id) {
      // Only if not voted and not currently viewing poll results.
      if (!this.userHasVoted && !this.pollViewResults) {
        if (id == this.pollTentativeAnswer) {
          this.pollTentativeAnswer = null;
        } else {
          this.pollTentativeAnswer = id;
        }
      }
    },
    pollAnswerSubmit() {
      if (!this.pollTentativeAnswer || this.pollIsClosed) {
        return;
      }
      util
        .sendPollAnswer(this.$matrix.matrixClient, this.room.roomId, [this.pollTentativeAnswer], this.event)
        .catch((err) => {
          console.log("Failed to send:", err);
        })
        .finally(() => {
          this.pollTentativeAnswer = null;
        });
    },
    pollClose() {
      util.closePoll(this.$matrix.matrixClient, this.room.roomId, this.event).then(() => {
        this.$emit("poll-closed", this);
      }).catch((err) => {
        console.log("Failed to send:", err);
      });
    },
    onAddRelation(ignoredevent) {
      this.updateAnswers();
    },
    pollMixinOnEvent(event) {
      if (event.getRoomId() !== this.room.roomId) {
        return; // Not for this room
      }
      this.$matrix.matrixClient.decryptEventIfNeeded(event).then(() => {
        if (event.getType().startsWith("m.poll.") || event.getType().startsWith("org.matrix.msc3381.poll.")) {
          this.updateAnswers();
        }
      });
    },
  },
  computed: {
    pollIsClosed() {
      return this.pollEndTs != null && this.pollEndTs !== undefined;
    },
    userCanClosePoll() {
      return (
        this.room &&
        this.room.currentState &&
        this.room.currentState.maySendRedactionForEvent(this.event, this.$matrix.currentUserId)
      );
    },
    userHasVoted() {
      return this.pollAnswers.some((a) => a.hasMyVote);
    },
    pollNumAnswers() {
      return this.pollTotalVotes;
    },
    pollIsAdmin() {
      // Admins can view results of not-yet-closed undisclosed polls.
      const me = this.room && this.room.getMember(this.$matrix.currentUserId);
      let isAdmin =
        me && this.room.currentState && this.room.currentState.hasSufficientPowerLevelFor("redact", me.powerLevel);
      return isAdmin;
    },
  },
  watch: {
    pollResponseRelations: {
      handler(newValue, oldValue) {
        if (oldValue) {
          oldValue.off("Relations.add", this.onAddRelation);
        }
        if (newValue) {
          newValue.on("Relations.add", this.onAddRelation);
        }
        this.updateAnswers();
      },
      immediate: true,
    },
    pollEndRelations: {
      handler(newValue, oldValue) {
        if (oldValue) {
          oldValue.off("Relations.add", this.onAddRelation);
        }
        if (newValue) {
          newValue.on("Relations.add", this.onAddRelation);
        }
        this.updateAnswers();
      },
      immediate: true,
    },
  },
};
