import {
  apiFavoritePost,
  apiUnfavoritePost,
  fetchFavouritePosts,
} from "#api/account/favorites";
import { PAGINATION_LIMIT } from "#lib/pagination";
import { getLocalStorageItem, setLocalStorageItem } from "#storage/local";
import { isRegisteredAccount } from "./auth";

interface IPostData {
  service: string;
  user: string;
  id: string;
}

let favouritePosts:
  | Awaited<ReturnType<typeof fetchFavouritePosts>>
  | undefined = undefined;

export async function isFavouritePost(
  service: string,
  profileID: string,
  postID: string
): Promise<boolean> {
  const matches = await findFavouritePosts([
    { service, user: profileID, id: postID },
  ]);

  if (matches.length > 1) {
    throw new Error(
      `Expected one result for favorite post match but got several.`
    );
  }

  return matches.length === 0 ? false : true;
}

export async function findFavouritePosts(
  postsData: IPostData[]
): Promise<IPostData[]> {
  const isRegistered = await isRegisteredAccount();

  // return early for non-registered users
  // instead of rewriting all flows depending on it
  // since technically unregistered users won't get matches
  if (!isRegistered) {
    return [];
  }

  const favPosts = await getFavouritePosts();
  const matches: IPostData[] = [];

  for (const postData of postsData) {
    const fav = favPosts.find(
      ({ id, service, user }) =>
        id === postData.id &&
        user === postData.user &&
        service === postData.service
    );

    if (fav) {
      matches.push(fav);
    }
  }

  return matches;
}

async function getFavouritePosts() {
  if (favouritePosts) {
    return favouritePosts;
  }

  const storageFavs = getLocalStorageItem("post_favs");

  if (!storageFavs) {
    const favs = await fetchFavouritePosts();
    const localFavs = favs.map(({ service, user, id }) => {
      return { service, user, id };
    });

    setLocalStorageItem("post_favs", JSON.stringify(localFavs));

    favouritePosts = favs;
  } else {
    const parsedFavs: Awaited<ReturnType<typeof fetchFavouritePosts>> =
      JSON.parse(storageFavs);

    favouritePosts = parsedFavs;
  }

  return favouritePosts;
}

/**
 * A duct-tape function to return actual fav posts
 * instead of subset for matching.
 */
export async function getAllFavouritePosts(
  offset: number = 0,
  order: "asc" | "desc" = "desc",
  sortBy: "faved_seq" | "published" = "faved_seq"
) {
  if (!favouritePosts) {
    favouritePosts = await fetchFavouritePosts();
  }

  {
    const localFavs = favouritePosts.map(({ service, user, id }) => {
      return { service, user, id };
    });

    setLocalStorageItem("post_favs", JSON.stringify(localFavs));
  }

  const compare = new Intl.Collator().compare;
  const preppedFavs = favouritePosts
    .filter((post) => post)
    .sort((prev, next) => {
      switch (sortBy) {
        case "published": {
          return compare(prev.published, next.published);
        }

        case "faved_seq": {
          return prev.faved_seq === next.faved_seq
            ? 0
            : prev.faved_seq < next.faved_seq
            ? -1
            : 1;
        }

        default: {
          throw new Error(`Unknown sort by value "${sortBy satisfies never}".`);
        }
      }
    });

  if (order === "desc") {
    preppedFavs.reverse();
  }

  const resultFavs = preppedFavs.slice(offset, offset + PAGINATION_LIMIT);

  return {
    count: preppedFavs.length,
    posts: resultFavs,
  };
}

export async function addPostToFavourites(
  service: string,
  profileID: string,
  postID: string
) {
  await apiFavoritePost(service, profileID, postID);

  const favs = await fetchFavouritePosts();
  const localFavs = favs.map(({ service, user, id }) => {
    return { service, user, id };
  });

  setLocalStorageItem("post_favs", JSON.stringify(localFavs));
  favouritePosts = favs;

  return { service, profileID, postID };
}

export async function removePostFromFavourites(
  service: string,
  profileID: string,
  postID: string
) {
  await apiUnfavoritePost(service, profileID, postID);

  const favs = await fetchFavouritePosts();
  const localFavs = favs.map(({ service, user, id }) => {
    return { service, user, id };
  });

  setLocalStorageItem("post_favs", JSON.stringify(localFavs));
  favouritePosts = favs;

  return { service, profileID, postID };
}
