import { parse } from "date-fns";
import filter from "lodash/filter";
import pick from "lodash/pick";
import property from "lodash/property";
import values from "lodash/values";
import WaitQueue from "wait-queue";

import { markdownConverter } from "markdown";

export const seenPostsLSKey = "cf2021_seen_posts";
export const seenAnnouncementsLSKey = "cf2021_seen_announcements";

/**
 * Filter & sort collection of posts.
 * @param {CF2021.PostStoreFilters} filters
 * @param {CF2021.PostStoreItems} allItems
 * @returns {CF2021.Post[]}
 */
export const filterPosts = (filters, allItems) => {
  const predicate = {};

  if (filters.flags === "active") {
    predicate.archived = false;
  }
  if (filters.flags === "archived") {
    predicate.archived = true;
  }

  if (filters.type === "proposalsOnly") {
    predicate.type = "procedure-proposal";
  }
  if (filters.type === "discussionOnly") {
    predicate.type = "post";
  }

  let filteredItems = filter(allItems, predicate);

  if (!filters.showPendingProposals) {
    filteredItems = filteredItems.filter(
      (item) => item.type === "post" || item.state !== "pending"
    );
  }

  if (filters.sort === "byDate") {
    return filteredItems.sort((a, b) => b.datetime - a.datetime);
  }

  return filteredItems.sort((a, b) => b.ranking.score - a.ranking.score);
};

/**
 * Update current posts window according to used filters.
 * @param {CF2021.PostStorePayload} state
 */
export const updateWindowPosts = (state) => {
  state.window.items = filterPosts(state.filters, values(state.items)).map(
    property("id")
  );
};

/**
 * Update itemIds from items.
 * @param {CF2021.AnnouncementStorePayload} state
 */
export const syncAnnoucementItemIds = (state) => {
  state.itemIds = values(state.items)
    .sort((a, b) => b.datetime - a.datetime)
    .map((announcement) => announcement.id);
};

export const postsMyVoteMapping = {
  0: "none",
  1: "like",
  [-1]: "dislike",
};

export const postsTypeMapping = {
  0: "procedure-proposal",
  1: "post",
};

export const postsTypeMappingRev = {
  post: 1,
  "procedure-proposal": 0,
};

export const postsStateMapping = {
  0: "pending",
  1: "announced",
  2: "accepted",
  3: "rejected",
  4: "rejected-by-chairman",
};

export const postsStateMappingRev = {
  pending: 0,
  announced: 1,
  accepted: 2,
  rejected: 3,
  "rejected-by-chairman": 4,
};

export const announcementTypeMapping = {
  0: "rejected-procedure-proposal",
  1: "accepted-procedure-proposal",
  2: "suggested-procedure-proposal",
  3: "voting",
  4: "announcement",
  5: "user-ban",
};

export const announcementTypeMappingRev = {
  "rejected-procedure-proposal": 0,
  "accepted-procedure-proposal": 1,
  "suggested-procedure-proposal": 2,
  voting: 3,
  announcement: 4,
  "user-ban": 5,
};

/**
 * Parse single post from the API.
 *
 * @param {any} rawPost
 * @returns {CF2021.Post}
 */
export const parseRawPost = (rawPost) => {
  const post = {
    ...pick(rawPost, ["id", "content", "author"]),
    contentHtml: markdownConverter.makeHtml(rawPost.content),
    datetime: parse(rawPost.datetime, "yyyy-MM-dd HH:mm:ss", new Date()),
    historyLog: rawPost.history_log,
    ranking: {
      dislikes: rawPost.ranking.dislikes,
      likes: rawPost.ranking.likes,
      score: rawPost.ranking.score,
      myVote: postsMyVoteMapping[rawPost.ranking.my_vote],
    },
    type: postsTypeMapping[rawPost.type],
    modified: Boolean(rawPost.is_changed),
    archived: Boolean(rawPost.is_archived),
    hidden: false,
    seen: isSeen(seenPostsLSKey, rawPost.id),
  };

  if (post.type === "procedure-proposal") {
    post.state = postsStateMapping[rawPost.state];
  }

  return post;
};

/**
 * Parse single announcement from the API.
 *
 * @param {any} rawAnnouncement
 * @returns {CF2021.Announcement}
 */
export const parseRawAnnouncement = (rawAnnouncement) => {
  const announcement = {
    ...pick(rawAnnouncement, ["id", "content", "link"]),
    datetime: parse(
      rawAnnouncement.datetime,
      "yyyy-MM-dd HH:mm:ss",
      new Date()
    ),
    type: announcementTypeMapping[rawAnnouncement.type],
    seen: isSeen(seenAnnouncementsLSKey, rawAnnouncement.id),
  };

  return announcement;
};

export const createSeenWriter = (localStorageKey) => {
  const queue = new WaitQueue();

  const seenWriterWorker = async () => {
    const id = await queue.shift();
    const seen = new Set(
      (localStorage.getItem(localStorageKey) || "").split(",")
    );

    seen.add(id);

    localStorage.setItem(localStorageKey, Array.from(seen).join(","));

    setTimeout(seenWriterWorker);
  };

  seenWriterWorker();

  return {
    markSeen: (id) => queue.push(id),
  };
};

export const isSeen = (localStorageKey, id) => {
  const val = localStorage.getItem(localStorageKey) || "";
  return val.indexOf(id) !== -1;
};