import { resources } from "@lib/resources.ts";
import fuzzysort from "npm:fuzzysort";
import { GenericResource } from "@lib/types.ts";
import { extractHashTags } from "@lib/string.ts";
import { getAllMovies, Movie } from "@lib/resource/movies.ts";
import { Article, getAllArticles } from "@lib/resource/articles.ts";
import { getAllRecipes, Recipe } from "@lib/resource/recipes.ts";
import { getAllSeries, Series } from "@lib/resource/series.ts";

type ResourceType = keyof typeof resources;

type SearchParams = {
  q: string;
  types?: string[];
  tags?: string[];
  rating?: number;
  authors?: string[];
};

export function parseResourceUrl(_url: string | URL): SearchParams | undefined {
  try {
    const url = typeof _url === "string" ? new URL(_url) : _url;
    let query = url.searchParams.get("q") || "*";
    if (!query) {
      return undefined;
    }
    query = decodeURIComponent(query);

    const hashTags = extractHashTags(query);

    for (const tag of hashTags) {
      query = query.replace("#" + tag, "");
    }

    return {
      q: query,
      types: url.searchParams.get("type")?.split(",") as ResourceType[] ||
        undefined,
      tags: hashTags,
      rating: url.searchParams.has("rating")
        ? parseInt(url.searchParams.get("rating")!)
        : undefined,
    };
  } catch (_err) {
    return undefined;
  }
}

const isResource = (
  item: Movie | Series | Article | Recipe | boolean,
): item is Movie | Series | Article | Recipe => {
  return !!item;
};

export async function searchResource(
  { q, tags = [], types, authors, rating }: SearchParams,
): Promise<GenericResource[]> {
  let resources = (await Promise.all([
    (!types || types.includes("movie")) && getAllMovies(),
    (!types || types.includes("series")) && getAllSeries(),
    (!types || types.includes("article")) && getAllArticles(),
    (!types || types.includes("recipe")) && getAllRecipes(),
  ])).flat().filter(isResource);

  if (tags?.length) {
    resources = resources.filter((r) => {
      return tags?.every((t) => r.tags.includes(t));
    });
  }

  if (authors?.length) {
    resources = resources.filter((r) => {
      return r?.meta?.author && authors.includes(r?.meta?.author);
    });
  }
  if (rating) {
    resources = resources.filter((r) => {
      return r?.meta?.rating && r.meta.rating >= rating;
    });
  }

  if (q.length && q !== "*") {
    const results = fuzzysort.go(q, resources, {
      keys: ["content", "name", "description"],
      threshold: 0.3,
    });
    resources = results.map((r) => r.obj);
  }

  return resources;
}
