"use client";

import { useQueryState, parseAsStringLiteral, parseAsString } from "nuqs";
import { EventItem } from "./EventItem";
import { EventFilter, EventFilterExplained } from "./EventFilter";
import {
  EventFragment,
  EventCategory,
  SingularEvent,
  getSingularEvents,
  organizeEventsInCalendar,
  EventOrganizer,
} from "@/lib/event";
import { isTodayOrFuture } from "@/lib/date";
import styles from "./eventContainer.module.scss";
import { formatDate, formatYearMonth } from "@/lib/date";
import { parse } from "date-fns";
import { unique } from "@/lib/common";
import Icon from "../general/Icon";
import { useState } from "react";
import { VenueFragment } from "@/gql/graphql";

/*
  TODO: canonical / alternate URLs https://github.com/47ng/nuqs?tab=readme-ov-file#seo
*/

export const EventContainer = ({
  events,
  eventCategories,
  eventOrganizers,
  venues,
}: {
  events: EventFragment[];
  eventCategories: EventCategory[];
  eventOrganizers: EventOrganizer[];
  venues: VenueFragment[];
}) => {
  const [mode, setMode] = useQueryState(
    "mode",
    parseAsStringLiteral(["list", "calendar"]).withDefault("list")
  );
  const [category, setCategory] = useQueryState("category", parseAsString);
  const [organizer, setOrganizer] = useQueryState("organizer", parseAsString);
  const [venue, setVenue] = useQueryState("venue", parseAsString);

  const resetFilters = () => {
    setCategory(null);
    setOrganizer(null);
    setVenue(null);
  };

  /* Allow filtering on all categories that are configured to be shown */
  const filterableCategories = eventCategories.filter((x) => x.showInFilters);

  /*
    Allow filtering on all organizers that have upcoming events
    Filtering on an organizer with no upcoming events will work, but be hidden from dropdown
  */
  const uniqueOrganizers: string[] = unique(
    events
      .map((x) => x.organizers)
      .flat()
      .filter((x) => x.__typename === "EventOrganizer")
      .map((x) => x.slug)
      .filter((x) => typeof x === "string")
  );
  const filterableOrganizers = uniqueOrganizers
    .map((slug) => eventOrganizers.find((haystack) => haystack.slug === slug))
    .filter((x) => x !== undefined) as EventOrganizer[];

  /*
    Allow filtering on all venues that have upcoming events
    Filtering on a venue with no upcoming events will work,
    and in that case it's included in the dropdown
   */
  const venueSlugsWithUpcomingEvents = events
    .map((x) => x.occurrences)
    .flat()
    .filter((x) => x.venue?.__typename === "VenuePage")
    .map((x) => x.venue?.slug)
    .filter((x) => typeof x === "string");
  const filterableVenues = venues
    .map((x) =>
      venues.find(
        (haystack) => haystack.slug === x.slug || haystack.slug === venue
      )
    )
    .filter((x) => x !== undefined) as VenueFragment[];

  const filteredEvents = events
    .filter(
      (x) =>
        !organizer ||
        x.organizers.map((organizer) => organizer.slug).includes(organizer)
    )
    .filter(
      (x) =>
        !category ||
        x.categories
          .map((eventCategory) => eventCategory.slug)
          .includes(category)
    )
    .filter(
      (x) =>
        !venue ||
        x.occurrences
          .map((occurrence) => occurrence.venue?.slug)
          .filter((x) => typeof x === "string")
          .includes(venue)
    );

  const [showFilter, setShowFilter] = useState(false);
  function toggleFilter() {
    setShowFilter(!showFilter);
  }

  return (
    <div className={styles.events}>
      <div className={styles.eventWrapper}>
        <div className={styles.displayOptions}>
          <button onClick={() => setMode(null)} className="button secondary">
            <span>Vis liste</span>
            <Icon />
          </button>
          <button
            onClick={() => setMode("calendar")}
            className="button secondary"
          >
            <span>Vis kalender</span>
            <Icon />
          </button>
          <button onClick={toggleFilter} className="button tertiary">
            <span>Filter</span>
            <Icon />
          </button>
        </div>
        <EventFilter
          eventCategories={filterableCategories}
          setCategory={setCategory}
          activeCategory={category}
          eventOrganizers={filterableOrganizers}
          setOrganizer={setOrganizer}
          activeOrganizer={organizer}
          venues={filterableVenues}
          setVenue={setVenue}
          activeVenue={venue}
          resetFilters={resetFilters}
          toggleVisibility={toggleFilter}
          isVisible={showFilter}
        />
        <EventFilterExplained
          eventCategories={filterableCategories}
          activeCategory={category}
          eventOrganizers={filterableOrganizers}
          activeOrganizer={organizer}
          venues={filterableVenues}
          activeVenue={venue}
        />
        {mode === "list" && <EventList events={filteredEvents} />}
        {mode === "calendar" && <EventCalendar events={filteredEvents} />}
      </div>
    </div>
  );
};

const EventList = ({ events }: { events: EventFragment[] }) => {
  return (
    <ul className={styles.eventList}>
      {events.map((event) => (
        <EventItem key={event.id} event={event} mode="list" />
      ))}
    </ul>
  );
};

const CalendarDay = ({
  day,
  events,
}: {
  day: string;
  events: SingularEvent[];
}) => (
  <div
    className={`${styles.calendarDay} ${events.length === 0 && styles.empty}`}
  >
    <h3>{formatDate(parse(day, "yyyy-MM-dd", new Date()), "eeee dd.MM.")}</h3>
    <ul>
      {events.map((event) => (
        <EventItem key={event.id} event={event} mode="calendar" size="small" />
      ))}
    </ul>
  </div>
);

const EventCalendar = ({ events }: { events: EventFragment[] }) => {
  const [showAll, setShowAll] = useState(false);

  const futureSingularEvents = getSingularEvents(events).filter(
    (x) => x.occurrence?.start && isTodayOrFuture(x.occurrence.start)
  );
  const eventsByDate = organizeEventsInCalendar(futureSingularEvents);
  const yearMonths = Object.keys(eventsByDate);
  const visibleYearMonths = showAll ? yearMonths : yearMonths.slice(0, 2);

  return (
    <>
      <div className={styles.eventCalendar}>
        {Object.keys(eventsByDate)
          .filter((yearMonth) => visibleYearMonths.includes(yearMonth))
          .map((yearMonth) => (
            <div key={yearMonth} className={styles.calendarYearMonth}>
              <h2>{formatYearMonth(yearMonth)}</h2>
              {Object.keys(eventsByDate[yearMonth]).map((week) => (
                <div key={week} className={styles.calendarWeek}>
                  {Object.keys(eventsByDate[yearMonth][week]).map((day) => (
                    <CalendarDay
                      key={day}
                      day={day}
                      events={eventsByDate[yearMonth][week][day]}
                    />
                  ))}
                </div>
              ))}
            </div>
          ))}
      </div>
      {!showAll && yearMonths.length > 2 && (
        <button onClick={() => setShowAll(true)} className="button">
          <span>Vis alt</span>
          <Icon />
        </button>
      )}
    </>
  );
};
