import {
  apiFavoriteProfile,
  apiUnfavoriteProfile,
  fetchFavouriteProfiles,
} from "#api/account/favorites";
import { PAGINATION_LIMIT } from "#lib/pagination";
import { ElementType } from "#lib/types";
import {
  ILocalStorageSchema,
  getLocalStorageItem,
  setLocalStorageItem,
} from "#storage/local";
import { IFavouriteArtist } from "../types";
import { isRegisteredAccount } from "./auth";

interface IProfileData extends Pick<IFavouriteArtist, "service" | "id"> {}

let favouriteProfiles:
  | Awaited<ReturnType<typeof fetchFavouriteProfiles>>
  | undefined = undefined;

export async function isFavouriteProfile(service: string, profileID: string) {
  const result = await findFavouriteProfiles([{ service, id: profileID }]);

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

export async function findFavouriteProfiles(
  profilesData: IProfileData[]
): Promise<IProfileData[]> {
  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 favProfiles = await getFavouriteProfiles();
  const matches: IProfileData[] = [];

  for (const profileData of profilesData) {
    const fav = favProfiles.find(
      ({ id, service }) =>
        id === profileData.id && service === profileData.service
    );

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

  return matches;
}

async function getFavouriteProfiles() {
  const storageFavs = getLocalStorageItem("favs");

  if (!storageFavs) {
    const favs = await fetchFavouriteProfiles();
    const localFavs = favs.map<ElementType<ILocalStorageSchema["favs"]>>(
      ({ service, id }) => {
        return { service, id };
      }
    );
    setLocalStorageItem("favs", JSON.stringify(localFavs));

    return favs;
  }

  const parsedFavs: ILocalStorageSchema["favs"] = JSON.parse(storageFavs);

  return parsedFavs;
}

/**
 * A duct-tape function to return actual fav profiles
 * instead of subset for matching.
 */
export async function getAllFavouriteProfiles(
  offset: number = 0,
  order: "asc" | "desc" = "desc",
  sortBy: "updated" | "faved_seq" | "last_imported" = "updated"
) {
  if (!favouriteProfiles) {
    favouriteProfiles = await fetchFavouriteProfiles();
  }

  {
    const localFavs = favouriteProfiles.map<
      ElementType<ILocalStorageSchema["favs"]>
    >(({ service, id }) => {
      return { service, id };
    });

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

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

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

        case "last_imported": {
          return compare(prev.last_imported, next.last_imported);
        }

        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,
    profiles: resultFavs,
  };
}

export async function addProfileToFavourites(
  service: string,
  profileID: string
) {
  await apiFavoriteProfile(service, profileID);

  const favs = await fetchFavouriteProfiles();
  const localFavs = favs.map<ElementType<ILocalStorageSchema["favs"]>>(
    ({ service, id }) => {
      return { service, id };
    }
  );

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

  return { service, profileID };
}

export async function removeProfileFromFavourites(
  service: string,
  profileID: string
) {
  await apiUnfavoriteProfile(service, profileID);

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

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

  return { service, profileID };
}
