import { parseDocument } from "@lib/documents.ts";
import { parse, stringify } from "yaml";
import { createCrud } from "@lib/crud.ts";
import { extractHashTags, formatDate } from "@lib/string.ts";
import { fixRenderedMarkdown } from "@lib/helpers.ts";

export type Movie = {
  id: string;
  name: string;
  description: string;
  type: "movie";
  tags: string[];
  meta: {
    date: Date;
    tmdbId?: number;
    keywords?: string[];
    image: string;
    thumbnail?: string;
    average?: string;
    author: string;
    rating: number;
  };
};

export function renderMovie(movie: Movie) {
  const meta = movie.meta;
  if ("date" in meta && typeof meta.date !== "string") {
    meta.date = formatDate(meta.date) as unknown as Date;
  }

  delete meta.thumbnail;
  delete meta.average;

  const movieImage = `![](${movie.meta.image})`;

  return fixRenderedMarkdown(`${
    meta
      ? `---
${stringify(meta)}
---`
      : `---
---`
  }
# ${movie.name}
${
    // So we do not add a new image to the description everytime we render
    (movie.meta.image && !movie.description.includes(movieImage))
      ? movieImage
      : ""}
${movie.tags.map((t) => `#${t}`).join(" ")}
${movie.description}
`);
}

export function parseMovie(original: string, id: string): Movie {
  const doc = parseDocument(original);

  let meta = {} as Movie["meta"];
  let name = "";

  const range = [Infinity, -Infinity];

  for (const child of doc.children) {
    if (child.type === "yaml") {
      try {
        meta = (parse(child.value) || {}) as Movie["meta"];
      } catch (_) {
        // ignore here
      }

      if (meta["rating"] && typeof meta["rating"] === "string") {
        meta.rating = [...meta.rating?.matchAll("⭐")].length;
      }

      continue;
    }

    if (
      child.type === "heading" && child.depth === 1 && !name &&
      child.children.length === 1 && child.children[0].type === "text"
    ) {
      name = child.children[0].value;
      continue;
    }

    if (name) {
      const start = child.position?.start.offset || Infinity;
      const end = child.position?.end.offset || -Infinity;
      if (start < range[0]) range[0] = start;
      if (end > range[1]) range[1] = end;
    }
  }

  let description = original.slice(range[0], range[1]);
  const tags = extractHashTags(description);
  for (const tag of tags) {
    description = description.replace("#" + tag, "");
  }

  return {
    type: "movie",
    id,
    name,
    tags,
    description,
    meta,
  };
}

const crud = createCrud<Movie>({
  prefix: "Media/movies/",
  parse: parseMovie,
  render: renderMovie,
  hasThumbnails: true,
});

export const getMovie = async (id: string) => {
  const movie = await crud.read(id);
  return movie;
};

export const getAllMovies = crud.readAll;
export const createMovie = crud.create;
